サンドボックスのユーザドキュメントディレクトリ(~/Documents)を取得する

先日はNSCodingの仕組みを使って行う、iOSにおけるオブジェクトの直列化の方法にについて言及した。
方法が分ったのは良いが、実際にはどのように、どこにデータを書き出すのだろう。

iOSも他のモバイルOSと同様にアプリケーションがアクセスするファイルシステムはアプリケーション(プロセス)毎に厳重に守られており、アクセスできる場所はサンドボックス(砂場)に限られている。
iOS Standard Directories: Where Files Reside - File System Programming Guide - Apple Developer

アプリケーションが使用できるサンドボックス内のディレクトリはiOS5.0のiPhoneシミュレータであれば、"/Users/ユーザ名/Library/Application Support/iPhone Simulator/5.0/Applications/"の直下にアプリケーション毎のUUIDを付加したサンドボックスとして構成される。

ディレクトリ 説明
/AppName.app アプリケーションのホーム
/Documents/ ユーザ又はアプリケーション固有のデータを保存する
/Documents/Inbox (メールの様な)読込みと削除のみ行うデータを外部から登録する
/Library/ ユーザとは直接関係の無いデータを保存する
/tmp/ アプリケーションの起動期間中のみ使用する一時的なデータを保存する

オブジェクトのアーカイブはアプリケーションで使用したユーザのデータを永続化する訳なので、保存対象は/Documents/が適当だろう。
既に書いたようにこれらのディレクトリはユーザ環境やアプリケーションによって変わるため、アプリケーションで固定のディレクトリパスを設定してはならない。代わりに環境によって変わるディレクトリを取得するためのイディオムが存在する。

ユーザのドキュメントディレクトリ(/Documents)を取得する
   NSArray *documentDirectories =NSSearchPathForDirectoriesInDomains(NSDocumentationDirectory, NSUserDomainMask, YES);
   NSString *documentDirectory = [documentDirectories lastObject];
   NSLog(@"%@", documentDirectory);

NSSearchPathForDirectoriesInDomains関数の第一パラメタでサンドボックスの種類を指定するのだが ~Documents/を取得する場合にはNSDocumentDirectoryを使用する。また、実際のディレクトリ名にはアプリケーション毎に採番されたUUIDが付加される。

さて、ここからが本題だが、この結果としてログには以下のようなディレクトリパスがログを出力されるはずだ。

期待される結果
/Users/kazz/Library/Application Support/iPhoneSimulator/5.0/Applications/[UUID]/Documents

ところが私の環境(Xcode 4.2 + iOS 5.0シミュレータ)では何度実行しても以下のディレクトリ名が戻ってしまう。

実際の結果 (期待とは違う)
/Users/kazz/Library/Application Support/iPhoneSimulator/5.0/Applications/[UUID]/Library/Documentation/

NSDocumentationDirectory定数をセットして同関数を実行しているはずなのに実際に戻るのは~Library/Documentation/下なのである。

一応このディレクトリでも、存在チェックをして無い場合ディレクトリを作り(当然無い)ファイルを保存することはできるのだが、Appleのドキュメントではここの場所は「非ユーザデータ」とされている訳で、腑に落ちない訳なのだが、どうしてこうなるのかが今の所全く分らない。 

追記:
と、今日は1日この問題の原因を調べて潰したのだが、先ほどようやく原因が分った。iOS開発の経験がある方は一分もしないで私のミスが分っただろう。

私が書いたコード
NSArray *documentDirectories = NSSearchPathForDirectoriesInDomains(NSDocumentationDirectory, NSUserDomainMask, YES);
~/Documentsを取得するための正しいコード
NSArray *documentDirectories = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);


はぁぁぁぁぁぁぁぁぁぁぁぁぁ。こんなんで1日潰すとは.....



Objective-Cはその言語の経緯と成り立ちから、全ての名前を平易で長いものにする傾向がある。
それ自体は別に嫌いではないしメソッドのオーパロード機能が無いにも関わらず、それをエレガントに解決するための一助になっているのだが、今回のようなちょっとした間違いをやらかしやすい言語だと思う。

特にXcodeのようにエディタのコード補完が強力なIDEを使ってコーディングする場合には注意すべき点だと思う。※


※一般的なメソッドの入力は接頭の何文字かと、パラメタの回数分のTABキーと実パラメタの入力だけで完了してしまう。それだけに自分が入力した覚えの無いパラメタや定数がセットされてしまうことも頻繁に起こる。