その後のその後

iOSエンジニア 堤 修一のブログ github.com/shu223

NSUserDefaults に保存する際に自動的に iCloud にも保存してくれるライブラリ "MKiCloudSync"

MKiCloudSync を使用すると、たった1行コードを追加しておくだけで、あとは自動的に NSUserDefaults の内容を iCloud に同期してくれます。


もともとシンプルな iCloud 同期ですが、もっとシンプルになるのでとりあえず iCloud 対応したい、ちょっと試してみたい、等の場合にオススメです。

使い方

1. ソースをダウンロード
https://github.com/MugunthKumar/MKiCloudSync


2. ヘッダをインポート

#import "MKiCloudSync.h"


3. start メソッドをコール
AppDelegate の application:didFinishLaunchingWithOptions: 内で、以下のように start メソッドをコール

[MKiCloudSync start];


たったこれだけ。


「おいおい、iCloudに書き込みたいときはどうすんだ?」と思った方もいらっしゃったかもしれませんが、そこを自動化してくれてるのが MKiCloudSync なので、上記だけでいいのです。


MKiCloudSync のしくみ

簡単にいうと(実際に中身も簡単なのですが)、
NSUserDefaults の変更を監視 → 変更があれば iCloud に反映
というしくみになっています。


ソースをのぞいてみると、start メソッドをコールすると2つの通知の監視を開始していることがわかります。

if([NSUbiquitousKeyValueStore defaultStore]) {  // is iCloud enabled
    
    [[NSNotificationCenter defaultCenter] addObserver:self 
                                             selector:@selector(updateFromiCloud:) 
                                                 name:NSUbiquitousKeyValueStoreDidChangeExternallyNotification 
                                               object:nil];
    
    [[NSNotificationCenter defaultCenter] addObserver:self 
                                             selector:@selector(updateToiCloud:) 
                                                 name:NSUserDefaultsDidChangeNotification
                                               object:nil];


NSUserDefaultsDidChangeNotification は、NSUserDefaults に変更があったときに発行される通知で、この通知が発行されたときに実行される updateToiCloud: メソッドで NSUserDefaults の内容を iCloud に同期するようになっています。


また NSUbiquitousKeyValueStoreDidChangeExternallyNotification は、iCloud のキーに外部からの変更があった場合に発行される通知で、この通知を受け取ると updateFromiCloud: メソッドで iCloud の内容を NSUserDefaults に同期するようになっています。(もちろんこの間のNSUserDefaultsDidChangeNotification の監視は止めてあります)


注意点

MKiCloudSync では NSUserDefaults のキーの削除には対応していません。NSUserDefaultsDidChangeNotification の通知を受け取ったときに実行されるハンドラメソッドである updateToiCloud では、

NSDictionary *dict = [[NSUserDefaults standardUserDefaults] dictionaryRepresentation];

[dict enumerateKeysAndObjectsUsingBlock:^(id key, id obj, BOOL *stop) {
    
    [[NSUbiquitousKeyValueStore defaultStore] setObject:obj forKey:key];
}];

このように NSUserDefaults に存在するキーを列挙して iCloud に書き込んでいるので、NSUserDefaults に存在しなくなったキーを iCloud に書き込んではくれないのです。


これで何が困るかというと、開発中のアプリを削除して最初から動作確認しようとしても、再インストール時に NSUbiquitousKeyValueStoreDidChangeExternallyNotification 通知が発行されて iCloud からの自動同期が行われてしまい、諸々の設定値が復活してしまうという点です。


この辺りは、キーの削除への対応であれば fork して使うなり、アプリ削除への対応であればデバッグモード用に iCloud の内容をリセットする機能を追加するなりする必要があります。


たとえば僕の場合、デバッグビルド版にはこんな感じで iCloud の内容をリセットする機能をつけています。

NSUbiquitousKeyValueStore *iCloudStore = [NSUbiquitousKeyValueStore defaultStore];
NSDictionary *dict = [iCloudStore dictionaryRepresentation];                    
[dict enumerateKeysAndObjectsUsingBlock:^(id key, id obj, BOOL *stop) {
    
    [iCloudStore removeObjectForKey:key];
}];