読者です 読者をやめる 読者になる 読者になる

mixi engineer blog

ミクシィ・グループで、実際に開発に携わっているエンジニア達が執筆している公式ブログです。様々なサービスの開発や運用を行っていく際に得た技術情報から採用情報まで、有益な情報を幅広く取り扱っています。

OS X は判ってくれない

Objective-C

年末のお忙しいところ失礼いたします。トーストを床に落とすと必ずマーマレードを塗った方が下になる、iPhoneアプリ開発担当の七尾です。いつも仕事でObjective-Cばかり書いておりますが、そういえばMac用のアプリもObjective-Cで書けるんだっけ?じゃ、おれでも書ける?という、いつも通り頭の中がお花畑な発想で、ただでさえこの忙しい時期に初Cocoaアプリを作ってみたので、お知らせしたく記事を書いております。

punchdrunker/Opener – GitHub

何を作ろうかな?

幸いにも(?) 以前からずっと困っていた事があったので、その問題を解決するツールを作ることにしました。
その問題がどのくらいの人に関係するのかはハッキリ申し上げて、あんまいないだろうなと正直思うのですが(笑)、それはファイルサーバ上のファイルパスの共有です。
自分はMacをメインの作業端末にしているけれど、周りにはwindowsな人も結構いたりして、という場合、メールやIRCで

\\file-server\dir1\dir2

みたいなパスで送られてきた時に、いちいちFinderで辿るのが面倒で困っていました。
ショートカット使えよ、という声が聞こえる気がしますが、ショートカットを使っても、いつもと違うところや、開いた先で階層を1つでも辿ったりするのが面倒でした。
というわけで、バックスラッシュ区切りのパスをFinderですぐに開けるアプリを作ろうと思いました。

どうやって作ろうかな?

幸いにも社内では

\\ホスト名\dir1\dir2

のようなパスが常用されていたので、dfコマンドを利用すれば自分がマウントしているパスを探せるかなー、と当たりを付けました。
ターミナルでdfコマンドを実行すると以下のような出力が得られます。

Filesystem                      1K-blocks       Used Available Capacity  Mounted on
/dev/disk0s2s1                       xxx        xxxx         xxx          x%  /
devfs                                        xxx        xxxx         xxx          x%  /dev
(...中略...)
//ユーザ名@ホスト名                xxx        xxxx         xxx          x%   /fizz/buzz

これなら各行をスペース区切りで配列にして、0番目の要素がホスト名にマッチするかを見れば、最後の要素からマウントしているパスを取れるので、この方針で実装しようと決めました。
本当はCocoaのAPIかsambaのコマンドでそれっぽいものが見つかれば良かったんですが、ヤフーでググるスキルが足りなかったので、諦めました。ゴメンナサイ。

もっと言うと、windows端末側でネットワークドライブに割り当てられた

Z:\\dir1\dir2

みたいなパスには対応していません。
そもそもドライブ名は任意ですし、下手すると、同じ社内でも同じホストに対して違うドライブ名当てちゃってる場合はお相手にファイルパスを送っても、参照できないのでスルーしました。
ドライブ名とホスト名の紐付けを自分で行う設定項目があれば可能ですが、今回は見送りということで。

Hello Cocoa!

実装方法が固まったのであとはXcodeでコードを書くだけです。
「New Project」で、いつもは目もくれない「Mac OS X」セクションの「Application」を選び、「Cocoa Application」を選択。あとは流れで。
プロジェクトを開くと#import とか、SApplicationDelegateという目新しい名前が!などwktkしました。
とりあえず必要なパーツをxibにササっと配置します。

xib

ファイルパスを入力するテキストフィールドと、「Finderで開く」ボタン、あとはエラー表示用のテキストがあれば十分。
それぞれのクラス名を見てみると、「NSTextField」とか「NSButton」など。ここでやっと、iPhone/iPadと違う!と気付きました。(今さら)
普段我々がiOSアプリを開発する時はCocoa Touchフレームワーク(UITextFieldとかUIButton)を使っていて、Mac用のアプリにはCocoaというフレームワークを使っているんですね。いや、知ってましたけど、これがCocoaかぁと思った瞬間でした。

NSButton

NSButtonさん初めまして

とりあえず作った

そんなわけでとりあえず作りました。かかった時間はおよそ1日くらいです。
その中でちょっと大変だったところが以下2点です

1.階層がやっかい
時間がかかったのは、マウント先の指定を、ホスト名/ディレクトリにしている時に、与えられたパスの階層を正しく組み直す部分がやっかいでした。
たとえばdfした時に

//ユーザ名@ホスト名/dir1                xxx        xxxx         xxx          x%   /Volumes/dir1

となっていた場合

\\ホスト名\dir1\dir2

というパスを貰った時に、ホスト名でマウントしてるパスを拾えたからと言って、素直に繋げてしまうと

/Volumes/dir1/dir1/dir2

と、重複してしまうので、マウント先の指定がディレクトリを含める場合は重複を避けるような工夫が必要でした。

2.拡張正規表現

私の手元の環境のObjective-Cには拡張正規表現が無いので、ライブラリの選定でも時間を取られました。
Mac OS 10.7からはNSRegularExpressionが使えるのですが、私の自宅、職場ともに10.6なので諦めました。
とりあえず入れてみたもののビルドが通らないなどでハマりつつ、最終的にRegexKitLiteに落ち着きました。
静的解析に掛けると、16個くらい警告が出ますが、今では愛着すら感じます :-)

あとはCocoaフレームワークとは言えObjective-Cなので、とにかくAppleのリファレンスに当たれば大体の事は解決します。

完成(のつもり)

ひととおり出来たところで、一度社内のMacユーザに使ってみてもらいました。ここで2つ問題が起きました。

1.改行

URLのパスを入力する部分はNSTextFieldを使っていますが、コピペする時に改行が含まれていた場合、素直に改行も入ってしまうので、

\\hostname\dir1\dir2\dir3\n(改行コード)

をコピペすると見事に改行されて入力欄が空白になってしまいました。(空ではないが、改行後の文字が無いので、空に見える)
NSControlTextDidChangeNotificationという通知でテキストフィールドのテキストが変更されたイベントを受けとれるので、
そこで、改行があれば削除するようにしました。

2.円マーク

Windowsの「¥」(円マーク)とMacの「\」(バックスラッシュ)は同じ文字(0x5C)のはずなのに、Macなのに「¥」(円マーク)がデフォで入力できちゃう人がいたりして、愕然としたんですが、これって多分UTFの円マーク(0xCA25)ですよね?まあ確かに何かの拍子で入らないとも限らないので、円マークは全て「\」(バックスラッシュ)に置換するようにしました。ちなみにMacではoption+yでUTFな円マーク入力できるってことを知れて良かったです。

おわりに

以上のような工程を終えて、ひとまずOpener完成となりました。
いままでiPhoneアプリばかり書いていましたが、そのスキルの延長でCocoaアプリが書けるので、思っていたより簡単に作ることが出来て良かったです。これからも何か必要なものがあったら作ってみたいと思いました。
また今回作ったアプリを必要とする人はあんまりいないとは思いますが、周りに困っている人がいたらぜひ教えてあげてください:)