iOS 6で簡単になったUUIDの取得

iOS 6からUUIDが簡単に取得できるようになりました。

UUIDの取得方法

NSString *uuid= [[[NSUUID alloc] init] UUIDString]

※ iOS 6から利用可能です。旧バージョンのOSをサポートする場合は、次の記事に記載しているコードをお使いください。(ブログ記事:UDIDからUUIDへ

UUIDの使い途

UUIDは、アプリで取得コードを実行する度に異なるユニークなIDが得られます。

そこで、Appleは、iCloudをサポートするアプリでCoreData使う場合にデータのユニーク識別子としてUUIDを使うことを推奨しています。
(cf. [227]Using iCloud with Core Data (View in iTunes)

推奨というか、実際に実装してみると、CoreDataを使うアプリをiCloudサポートにする場合には必須のような気がします。

なぜかというと、コード内で連番を採番してユニークなIDを作成したつもりでも、異なるデバイスでオフライン時にIDを採番すると同じIDがそれぞれ採番されてしまう可能性があるからです。

UUIDだけではうまくいかない

では、UUIDを採番してユニークIDにすれば全て解決かというと、そうでもありません。

というのも、ユニークなデータにしてよいかどうかは、データの内容によるからです。

例えば、CoreDataで「フォルダ」というテーブルを作成してデータをグルーピングする場合で考えてみます。この「フォルダ」テーブルのデータを作成する場合、ユーザからするとユニークなキーはフォルダ名を想像します。「メモ」という名前のフォルダを作った後、オフライン状態の別のデバイスで「メモ」というフォルダを作ったとしても、どちらも同じフォルダという認識です。しかし、アプリでこの「フォルダ」データ作成時にUUIDをユニークキーにしてしまうと、「メモ」という名前のフォルダデータが2つ存在し、アプリではそれぞれ別のフォルダとして扱うことになってしまいます。

UUIDonlyKey
オフラインでそれぞれ「メモ」フォルダを作成して同期

この場合の解決策として考えられるのは、データをマージする時に、フォルダ名をキーにして同じフォルダ名なら同一のフォルダとして扱うように処理することです。

名前をキーにしてもうまくいかない

では、そもそもこの場合は、UUIDではなくフォルダ名をユニークキーにすれば良いのではないかと思ったりもしますが、これも正しく処理ができません。

フォルダ名をユニークキーにしてしまうと、ユーザがフォルダ名を変更した場合に対応できなくなってしまうからです。一方のデバイスでフォルダ名をリネームした後、リネームする前のデータを保持してるもう一方のデバイスとデータを同期すると、アプリでの認識はそれぞれ別のフォルダなので、フォルダでグループ化したフォルダ内のデータがどちらのフォルダの配下に入れば良いか判断できなくなってしまいます。

タイムスタンプも考慮

そこで、この場合は、ユニークなキーはUUIDとして保持し、さらにフォルダテーブルにタイムスタンプを追加して最終更新日時を保管するようにします。そして、データをマージする時に、まずUUIDが同じかどうかを確認し、もし同じであれば次にタイムスタンプを比較して新しい方のタイムスタンプを持っているデータが最新のデータ(フォルダ名)だと判断してマージ処理をします。

UUIDwithTimestamp
UUIDとタイムスタンプを使ってデータをマージ

めんどくさいですね!

結論

CoreDataでiCloudをサポートする場合は、UUIDをユニークキーにしてタイムスタンプを保持することがほぼ必須。

そして、マージ処理は、データの内容(意味合い)に依存するので、テーブル単位に個別のマージ処理を実装する。


関連記事