iCloud
一旦你的app通过UIDocument进行操作,那么iCloud服务会让你更满意。你只需要两步接入iCloud服务:
注册使用iCloud的资格
在苹果开发者网站,注册你的App ID ,勾选启用iCloud,并生成对应的provisioning profile文件,下载下来双击:
然后在项目工程中,勾选使用iCloud服务:
获取一个兼容iCloud的目录
在你app启动时,在后台调用 NSFileManager 的 URLForUbiquityContainerIdentifier:
(传入nil)方法来获取一个云分享目录的URL。这个目录会在file://localhost/private/var/mobile/Library/Mobile Documents/ 路径下。尽管严格意义上,这个路径并不在你app沙箱的范围内,但是你仍然可以像访问沙箱一样范围这个路径。你app放置在这个路径下的子目录都会自动分享到云端。
现在我们可以把我们上一篇文章中的People Groups app 变成兼容iCloud服务的。我们在app delegate 中,当我的app启动时,我启动一个后台线程,获取一个云分享目录的URL,然后在主线程中,以属性形式保存起来这个URL:
dispatch_async(dispatch_get_global_queue(0, 0), ^{
NSFileManager* fm = [NSFileManager new];
NSURL* ubiq = [fm URLForUbiquityContainerIdentifier:nil];
dispatch_async(dispatch_get_main_queue(), ^{
self.ubiq = ubiq;
});
});
在iOS 6 中,你可以直接在主线程中通过调用 NSFileManager 的 ubiquityIdentityToken
,因为这个方法是立刻返回的。如果返回值为nil,那么iCloud服务不可用,或者这个手机用户没有注册iCloud。如果返回不为nil,那么这个可以标识一个iCloud账户,你可以通过这个标识来判断用户是否登录了不同的账户。
然后,在任意地方,我可以指定这个URL当作用户的Documents 目录来查找和保存数据:
NSURL* docsurl = [fm URLForDirectory:NSDocumentDirectory
inDomain:NSUserDomainMask
appropriateForURL:nil create:NO error:nil];
NSURL* ubiq =
[(AppDelegate*)[[UIApplication sharedApplication] delegate] ubiq];
if (ubiq)
docsurl = ubiq;
现在你可以运行你的app,创建一条数据,然后在你的另一个设备上,同样运行这个app,你会发现这个新创建的数据同步到了这台设备上。非常有趣。
我们还需要去留意是否有新的文档出现在云端了,为了达到这个目的,我会运行一个 NSMetadataQuery 。通常的策略是:实例化一个NSMetadataQuery,配置它的搜索,注册通知,如NSMetadataQueryDidFinishGatheringNotification
、 NSMetadataQueryDidUpdateNotification
,然后开启搜索,我们还要持有这个NSMetadataQuery实例的强引用,以便让它在整个app的生命周期内都在运行。
另一个需要注意的地方是,我们还要留意本地磁盘上正在打开的文件是否有新的版本从云端下载了。为了达到这个目的,我们注册一个UIDocumentStateChangedNotification
通知。我们可以通过它的documentState 属性来获取当前文档的状态。一个比较麻烦的问题是,当这个documentState
是UIDocumentStateInConflict
时如何解决冲突。 你可以使用 NSFileVersion 类 ,若要了解更多关于这个类的使用,可以查看 苹果开发者文档:Document-based App Programming Guide里面 的 “Resolving Document Version Conflicts” 章节。
还有一个需要注意的地方是,一开始,用户关闭了iCloud服务或者对你的app没有启用iCloud服务,但是后来又启用了,这时,你的数据存放在两个不同的地方(云端和Documents 目录)。 你可以调用 NSFileManager 的 setUbiquitous:itemAtURL:destinationURL:error:
方法,来把某个目录转换到云端的无处不在的容器目录。
其实,相对于存放正式的文件在云端,我们更多时候或者更好的做法是只存放一些键值对数据到云端,类似于一个在线版的NSUserDefaults。 为了达到这个目的,你可以使用 NSUbiquitousKeyValueStore 类,获取一个defaultStore 共享的实例对象,使用的方式跟NSUserDefaults 类似。 这个 NSUbiquitousKeyValueStoreDidChangeExternallyNotification
可以在你的云端数据改变时通知你。通过这个NSUbiquitousKeyValueStore保存的到云端的数据不会记入用户的iCloud使用容量,不过还是应该尽量保持数据简短。