您的位置:首页 > 移动开发 > IOS开发

【iOS开发】iCloud开发&nbsp…

2017-02-13 17:21 253 查看
先mark一下吧,以后肯定能用上。原文地址:【iOS开发】iCloud开发 共享的两种方式作者:一棵草Telen
---------------------------------------------
另一篇相关日志:
【iOS开发】iCloud开发 分析 :http://blog.sina.com.cn/s/blog_693de6100101jur4.html
---------------------------------------------


iCloud Document Storage 和 Key-Value Data Storage 教程与简单示例

--------------------------------------------------
UIDocument
--------------------------------------------------

iCloud Document
Storage开发


测试
iCloud目前只能在真实设备中测试,不能在Simulator中测试

Display
Set

在iTunesConnect中新建"iCloud Manage Display
Sets",iCloud的文档和数据存储在display sets中。多个应用可以引用和存储数据到同一个display
set。(iTunesConnect手册上有这一步,但是很多教程,包括iOS App
Programming Guide也没有提到,可能不需要了)
APP ID 和
开发证书


iOS Provisioning Portal中,创建启用iCloud的App ID,现有的App
ID也可以编辑并启用iCloud,App ID必须不包含通配符"*"

针对上面的App
ID,创建新的Development Provisioning Profile,下载后双击使用Xcode打开完成安装,并设置Code
Signing。

Xcode
entitlements[/b]
在Xcode中配置应用的iCloud
Entitlements,Target->Summary->Entitlements,勾选后Xcode会自动生成
iCloud Key-Value Store: com.company.App
iCloud Containers: com.company.App
Keychain Access Groups: com.company.App

这里Xcode自动生成的Entitlements使用了通配符,我们需要手工修改PROJECT.Entitlements文件:
iCloud Key-Value
Store: TeamIdentifier.com.company.App
iCloud Containers: TeamIdentifier.com.company.App
Keychain Access Groups: ApplicationIdentifierPrefix.com.company.App

其中TeamIdentifier是申请
IDP 时拿到的标识,可以在MemberCenter -> Your Account
-> Account Summary中找到(http://developer.apple.com/membercenter/index.action
ApplicationIdentifierPrefix
则是注册App ID时使用的前缀。

其实简单的办法是用编辑器打开上面下载的Development
Provisioning Profile,直接在里面就可以找到这两个字符串。

源代码定义Ubiquity
Container URL

定义一个常量字符串,我们代码里面使用它来构造iCloud
storage的URL,实际上和Entitlement中定义的字符串是一样的。
#define
UBIQUITY_CONTAINER_URL
@"ABCDEF12345.com.yourdomain.icloudapp

UIDocument
iOS
5为iCloud引入的file
presenter,创建和管理文档及其内容,极大地方便了开发。对本地文件使用UIDocument也能带来一些好处。如后台队列的异步读写、处理版本冲突、自动文档保存等。

继承UIDocument
contentsForType:error:[/b]
UIDocument对象将数据写入文件或文档时调用,这个方法负责收集要写入的数据,并返回NSData或NSFileWrapper对象。

loadFromContents:ofType:error:[/b]
UIDocument对象从文件或文档中读取数据时调用,并传递从文件或文档中已经读取到的数据给这个方法。方法负责把这些数据装载到应用的内部数据model

文档状态和冲突解决
文档状态:
UIDocumentStateNormal
– 文档已打开并允许用户编辑
UIDocumentStateClosed
– 文档当前已关闭,读取文档时出错也可能是这个状态
UIDocumentStateInConflict
– 检测到文档的多个版本冲突
UIDocumentStateSavingError
– 试图保存文档时出错
UIDocumentStateEditingDisabled
– 文档繁忙,当前编辑不安全

文档状态变化时会发送 UIDocumentStateChangedNotification
通知,注册这个通知就能监测文档状态,及时处理出错、冲突等。

解决冲突:
如果可行,合并冲突版本

如果不丢失数据,丢弃冲突版本

提示用户,选择要保留的版本

iCloud
理想情况下,应该允许用户指定哪些文件存储在iCloud,哪些文件存储在本地
之前已经添加到iCloud的文档,不能使用绝对路径来访问!应用应该通过名字在iCloud
storage中查找文档。
存储到iCloud的文档,应该放在应用的Documents目录

使用 URLForUbiquityContainerIdentifier:
方法检测iCloud是否可用,传递已经定义好的Container
URL,也可以传递nil,表示Entitlements中定义的第一个URL。方法返回nil表示iCloud不可用,返回URL表示可用。

拿到这个URL后,再对它调用 URLByAppendingPathComponent:@"Documents"
来组建一个路径。

默认情况下,当前应用的iCloud
storage中并没有这个Documents目录,因此我们需要首先创建它。

然后再使用 NSMetadataQuery
查找iCloud中是否存在我们要的文档,查找过程会在另一个线程中进行,完成后会通过 NSMetadataQueryDidFinishGatheringNotification
通知告诉你注册的observer

本地文件移动到iCloud
调用 setUbiquitous:
itemAtURL: 方法,传递YES表示本地文件移动到iCloud,传递NO则表示反向移动。

设备配置

Settings 应用中启用设备的iCloud Document and Data Storage,运行程序测试,此时可以在
Settings 应用中看到已经上传到iCloud中的文档

完整例子
MyDocument.h[/b]
@interface
MyDocument : UIDocument
{
 
  NSString *userText;
}
@property
(strong, nonatomic) NSString *userText;

MyDocument.m
-(id)contentsForType:(NSString
*)typeName error:(NSError *__autoreleasing
*)outError
{
 
  return [NSData dataWithBytes:[self.userText
UTF8String] length:[self.userText length]];
}

-(BOOL)
loadFromContents:(id)contents ofType:(NSString *)typeName
error:(NSError *__autoreleasing
*)outError 
{
 
  if ( [contents length] > 0)
{
 
     
self.userText = [[NSString alloc] initWithBytes:[contents bytes]
length:[contents length]
encoding:NSUTF8StringEncoding];
 
  } else {
 
     
self.userText = @"";
 
  }
 
  return YES;
}

iCloudStoreViewController.h[/b]
@interface
iCloudStoreViewController : UIViewController
{
 
  MyDocument *document;
 
  NSURL *documentURL;
 
  NSURL *ubiquityURL;
 
  UITextView *textView;
 
  NSMetadataQuery *metadataQuery;
}
@property
(strong, nonatomic) IBOutlet UITextView *textView;
@property
(strong, nonatomic) NSURL *documentURL;
@property
(strong, nonatomic) MyDocument *document;
@property
(strong, nonatomic) NSURL *ubiquityURL;
@property
(strong, nonatomic) NSMetadataQuery
*metadataQuery;
-(IBAction)saveDocument;

iCloudStoreViewController.m
-
(void)saveDocument
{
 
  self.document.userText =
textView.text;
 
  [self.document saveToURL:ubiquityURL
forSaveOperation:UIDocumentSaveForOverwriting
 
     
   completionHandler:^(BOOL
success) {
 
     
     
 if (success){
 
     
     
   
 NSLog(@"Saved to cloud for
overwriting");
 
     
     
 } else {
 
     
     
     NSLog(@"Not
saved to cloud for overwriting");
 
     
     
 } 
 
     
   }];
}

-
(void)viewDidLoad
{
 
  [super viewDidLoad];
 
  NSArray *dirPaths =
NSSearchPathForDirectoriesInDomains(NSDocumentDirectory,
NSUserDomainMask, YES);
 
  NSString *docsDir = [dirPaths
objectAtIndex:0];
 
  NSString *dataFile = [docsDir
stringByAppendingPathComponent: @"document.doc"];
 
  self.documentURL = [NSURL
fileURLWithPath:dataFile];
 
  NSFileManager *filemgr = [NSFileManager
defaultManager];
 
  ubiquityURL = [[filemgr
URLForUbiquityContainerIdentifier:UBIQUITY_CONTAINER_URL]
URLByAppendingPathComponent:@"Documents"];
 
  NSLog(@"iCloud path = %@", [ubiquityURL
path]);

 
  if ([filemgr fileExistsAtPath:[ubiquityURL
path]] == NO)
 
  {
 
     
NSLog(@"iCloud Documents directory does not
exist");
 
      [filemgr
createDirectoryAtURL:ubiquityURL withIntermediateDirectories:YES
attributes:nil error:nil];
 
  } else {
 
     
NSLog(@"iCloud Documents directory exists");
 
  }

 
  ubiquityURL = [ubiquityURL
URLByAppendingPathComponent:@"document.doc"];
 
  NSLog(@"Full ubiquity path = %@", [ubiquityURL
path]);

 
  // Search for document in iCloud
storage
 
  metadataQuery = [[NSMetadataQuery alloc]
init];
 
  [metadataQuery setPredicate: [NSPredicate
predicateWithFormat: @"%K like 'document.doc'",
 
     
     
  NSMetadataItemFSNameKey]];
 
  [metadataQuery setSearchScopes:[NSArray
arrayWithObjects:NSMetadataQueryUbiquitousDocumentsScope,nil]];
 
  [[NSNotificationCenter defaultCenter]
addObserver:self
selector:@selector(metadataQueryDidFinishGathering:) 
 
     
     
 
name:NSMetadataQueryDidFinishGatheringNotification 
 
     
     
  object:metadataQuery];
 
  NSLog(@"starting query");
 
  [metadataQuery startQuery];
}

-
(void)metadataQueryDidFinishGathering:(NSNotification
*)notification
{
 
  NSMetadataQuery *query = [notification
object];
 
  [query disableUpdates];
 
  [[NSNotificationCenter defaultCenter]
removeObserver:self
name:NSMetadataQueryDidFinishGatheringNotification
object:query];
 
  [query stopQuery];
 
  NSArray *results = [[NSArray alloc]
initWithArray:[query results]];

 
  if ([results count] == 1)
 
  {    
   
 
     
NSLog(@"File exists in cloud");
 
     
ubiquityURL = [[results objectAtIndex:0]
valueForAttribute:NSMetadataItemURLKey];
 
     
self.document = [[MyDocument alloc]
initWithFileURL:ubiquityURL];
 
     
//self.document.userText = @"";
 
      [document
openWithCompletionHandler:
 
     
 ^(BOOL success)

 
     
     if
(success){
 
     
     
   NSLog(@"Opened cloud
doc");
 
     
     
   textView.text =
document.userText;
 
     
     } else
{
 
     
     
   NSLog(@"Not opened cloud
doc");
 
     
   
 } 
 
     
 }];
 
  } else {
 
     
NSLog(@"File does not exist in cloud");
 
     
self.document = [[MyDocument alloc]
initWithFileURL:ubiquityURL];

 
      [document
saveToURL:ubiquityURL 
 
     
   forSaveOperation:
UIDocumentSaveForCreating
 
     
  completionHandler:^(BOOL success)

 
     
      if
(success){
 
     
     
    NSLog(@"Saved to cloud");
  
 
     
      }
 else {
 
     
     
    NSLog(@"Failed to save to
cloud");
 
     
     
}
 
     
  }];
 
  }
}

发布与提交[/b]
开发测试完成后,在 iOS Provisioning Portal 中为 App
ID 创建新的Distribution Provisioning
Profile,打包并Submit应用

--------------------------------------------------
NSUbiquitousKeyValueStore 
--------------------------------------------------
iCloud
Key-Value Data Storage开发


iCloud Key-Value Data
Storage主要用于应用的小量非关键数据在多个设备间共享,支持的数据包括:NSString, NSDate, NSArray,
NSData, Boolean, NSDictionary, NSNumber等对象
NSUbiquitousKeyValueStore类
NSUbiquitousKeyValueStore
*keyStore = 
[[NSUbiquitousKeyValueStore
alloc] init];
[keyStore setString:@”Saved
String” forKey:@"MyString"];

然后调用:
[keyStore
synchronize];

保存在本地,以便稍后同步到iCloud,但是调用synchronize方法并不会立即将本地保存的数据同步到iCloud。iOS自己会在适当的时候将这些本地数据同步到iCloud
Key-value Data Storage

获取key-value值:
NSString *storedString =
[keyStore stringForKey:@"MyString"];

iCloud
Key-Value不会产生冲突,最后上传的总是最新的值。

observer可以通过 NSUbiquitousKeyValueStoreDidChangeExternallyNotification
通知来监听key-value storage的变化。
key-value存储超出限制时也会递送这个通知,并传递给你 NSUbiquitousKeyValueStoreQuotaViolationChange
常量。

完整例子

iCloudKeysViewController.h
@interface
iCloudKeysViewController : UIViewController {
UITextField
*textField;
NSUbiquitousKeyValueStore
*keyStore;
}
@property (strong, nonatomic)
IBOutlet UITextField *textField;
-(IBAction)saveKey;
@end

iCloudKeysViewController.m

-(void)saveKey

{

    [keyStore
setString:textField.text forKey:@"MyString"];

    [keyStore synchronize];

    NSLog(@"Save key");

}

- (void)viewDidLoad

{

    [super viewDidLoad];

// Do any additional setup after loading the view, typically from a
nib.

    keyStore =
[NSUbiquitousKeyValueStore defaultStore];

    NSString *storedString =
[keyStore stringForKey:@"MyString"];

    if (storedString !=
nil)

    {

     
  textField.text = storedString;

    }

    [[NSNotificationCenter
defaultCenter] addObserver:self

     
     
selector:@selector(ubiquitousKeyValueStoreDidChange:) 

     
     
name:NSUbiquitousKeyValueStoreDidChangeExternallyNotification

     
     
object:keyStore];

}

-(void) ubiquitousKeyValueStoreDidChange: (NSNotification
*)notification

{

    NSLog(@"External Change
detected");

    UIAlertView *alert =
[[UIAlertView alloc]

     
     
     
     
  initWithTitle:@"Change detected"

     
     
     
     
  message:@"Change detected"

     
     
     
     
  delegate:nil 

     
     
     
     
  cancelButtonTitle:@"Ok"

     
     
     
     
  otherButtonTitles:nil, nil];

    [alert show];

    textField.text = [keyStore
stringForKey:@"MyString"];

}

--------------------------------------------------
--------------------------------------------------
在《萌塔塔》项目采用 NSUbiquitousKeyValueStore,借鉴userdefault的用法,有效解决。
难点在c++与oc之间准确的进行数据转换。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: