ICloud基础

  1. 在开发者后台创建AppID,启用iCloud服务。
  2. 在Xcode的Capabilities面板开启iCloud选项,勾选iCloud Documents,之后Xcode会自动在工程目录下生成entitlements文件,里面包含对应的iCloud container id。这个id不能包含通配符’*’。
  3. 每个新生成的iCloud container都默认包含名为Documents的子目录。这个目录及其子目录都是对玩家可见的。你可以在手机里通过iCloud>Storage & Backup > Manage Storage直接查看和操作这个目录里的文件和文件夹。在Documents目录之外的目录对玩家都是不可见的。
  4. 在游戏使用iCloud功能前,要先询问系统当前游戏的iCloud是否可用。通过获取当前默认container的云端基址来实现,如果是空,说明iCloud暂时不可使用。
id containerUrl = [[NSFileManager defaultManager] URLForUbiquityContainerIdentifier: nil];

这是个阻塞方法,所以最好在后台线程调用它。示例代码如下:

dispatch_async (dispatch_get_global_queue (DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^(void) {
    NSURL* containerUrl = [[NSFileManager defaultManager]
            URLForUbiquityContainerIdentifier: nil]; // 实际项目里最好把containerUrl保存下来
    if (containerUrl != nil) {
        NSLog(@"iCloud已开启");
	    dispatch_async (dispatch_get_main_queue (), ^(void) {
	        // 回到主线程,执行下一步操作
	    });
    }
    else
    {
    	NSLog(@"iCloud未开启");
    }
});

得到containerUrl后,通过方法URLByAppendingPathComponent:即可一级级的拼接出你想要的目录地址。

// 获取documents目录
NSURL* documentsUrl = [containerUrl URLByAppendingPathComponent:@"Documents"];

ICloud文件存储

  1. 首先要继承UIDocument类。这个类自带文档和云端基于NSURL的存取功能,我们只需要再实现以下两个方法,从而定义文档内容和云端数据是如何相互转换的就可以了。
/**
 *  保存文档时调用,将文档内容转换成NSXX类型云端数据
 *  @param typeName 文档文件类型
 *  @param outError 错误信息输出
 *  @return 文档数据 NSData
 */
-(id)contentsForType:(NSString *)typeName error:(NSError * _Nullable __autoreleasing *)outError;

/**
 *  读取数据时调用,将云端数据转换成文档内容
 *  @param contents 云端文档数据,NSXX类型
 *  @param typeName 文档文件类型
 *  @param outError 错误信息输出
 *  @return 读取是否成功
 */
-(BOOL)loadFromContents:(id)contents ofType:(NSString *)typeName error:(NSError * _Nullable __autoreleasing *)outError;

贴一个我自己实现的LDDocuments:

LDDocuments.h

@interface LDDocument : UIDocument
@property (nonatomic, copy) NSString *text;
-(nullable id)contentsForType:(NSString *)typeName error:(NSError **)outError;
-(BOOL)loadFromContents:(id)contents ofType:(nullable NSString *)typeName error:(NSError **)outError ;
@end

LDDocuments.mm

#import <Foundation/Foundation.h>
#import <UIKit/UIKit.h>
#import "LZDocument.h"
@implementation LZDocument

-(id)contentsForType:(NSString *)typeName error:(NSError * _Nullable __autoreleasing *)outError
{
    if (self.text.length <= 0) {     
        self.text = @"";
    }
    
    NSData *data = [self.text dataUsingEncoding:NSUTF8StringEncoding];   
    return data;
}

-(BOOL)loadFromContents:(id)contents ofType:(NSString *)typeName error:(NSError * _Nullable __autoreleasing *)outError
{ 
    self.text = [[NSString alloc]initWithData:contents encoding:NSUTF8StringEncoding];  
    return YES;
}
@end
  1. 下面介绍针对UIDocument的存取操作
  • 创建或者保存UIDocument
LZDocument *doc = [[LZDocument alloc] initWithFileURL:url]; // url是文档地址
doc.text = text; //文档内容
[doc saveToURL:url forSaveOperation:UIDocumentSaveForCreating completionHandler:^(BOOL success) {
    if(success){
        NSLog(@"保存成功");
    }else{
        NSLog(@"保存失败");
    }
}];
  • 读取UIDocument
LZDocument * doc = [[LZDocument alloc] initWithFileURL:url];
[doc openWithCompletionHandler:^(BOOL success) {
    if(success){
    	 NSLog(@"读取成功 text = %@", [doc.text UTF8String]);
    }else{
        NSLog(@"读取失败");
    }
}];
  • 删除UIDocument
NSError error = nil;
[[NSFileManager defaultManager] removeItemAtURL: url error:&error];
if (error) {
    NSLog(@"删除文档过程中发生错误,错误信息:%@",error.localizedDescription);
}
  1. 通常为了保证玩家的存档的绝对安全性(甩锅),iCloud云同步的实际上是本地游戏存档的一个打包备份。由玩家依据备份的创建时间,自己决定是否上传本地存档或者下载云端备份。这需要我们通过NSMetaQuery元数据查询获取iCloud文档列表,进而得到文档最后一次修改的时间。
- (void)getICloudTime
{
    if (!_metaQuery) {
        _metaQuery = [[NSMetadataQuery alloc] init];
        _metaQuery.searchScopes = @[NSMetadataQueryUbiquitousDocumentsScope];
        //查询状态是通过通知的形式告诉监听对象的
        NSNotificationCenter *center = [NSNotificationCenter defaultCenter];
        [center addObserver:self selector:@selector(queryFinish:)
        	name:NSMetadataQueryDidFinishGatheringNotification object:_metaQuery];//数据获取完成通知
    }
    else
    {
    	[_metaQuery stopQuery];
    }
    //开始查询
    [_metaQuery startQuery];
}

- (void)queryFinish:(NSNotification *)notification
{
    NSLog(@"数据获取成功!");
    NSArray *items = _metaQuery.results;

    [items enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
        NSMetadataItem *item = obj;
        //获取文件名
        NSString *fileName = [item valueForAttribute:NSMetadataItemFSNameKey];
        if([fileName hasPrefix:@"packsave"])
        {
			//获取文件最后一次修改的日期
	        NSDate *date = [item valueForAttribute:NSMetadataItemFSContentChangeDateKey];
	        NSTimeInterval interval = [date timeIntervalSince1970];	
	        long totalMilliseconds = interval * 1000;
	        stop = true;
	        // 弹出UI
        }
    }];
}