iOS网络编程-iCloud文档存储编程实例

本文介绍了iCloud文档存储编程的基础知识与实践案例,包括配置Xcode工程、自定义文档类、获取iCloud文档目录、查找及保存文档等内容。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

iCloud文档存储编程相对键值数据存储而言比较复杂,涉及到自定义文档类、获得iCloud文档目录、查找Ubiquity容器中的文档、保存文档和解决文档冲突等内容。

 

实例:iCloud文档存储编程设计

实例介绍一下iCloud文档存储编程过程,画面中有一个文本框和一个按钮控件,在设备1输入内容,点击“保存数据”按钮,将数据保存iCloud服务器。右图是设备2画面,过几秒钟后设备2上会读取iCloud服务器端数据,并显示在文本框中。

14

配置Xcode工程

编写iCloud文档存储编应用程序也需要在Xcode工程中进行一些配置,选择TAGETS→MyNotes→Summary→Entitlements

15

在图中Ubiquity Contrainers添加com.51work6.MyNotes,这是Ubiquity容器标识,可以有多个Ubiquity容器标识,这个容器标识代表着不同的目录。com.51work6.MyNotes代表目录如下所示:

/var/mobile/Library/Mobile Documents/98Z3R5XU29~com~51work6~MyNotes/

其中的98Z3R5XU29是在iOS开发者配置门户网站创建App ID时候生成的,它被称为Team ID

如果应用中没有使用iCloud键值数据存储key-Value Store可以不用配置。

自定义文档类

我 们需要自己封装一个文档类,它继承抽象类UIDocument,而UIDocument实现NSFilePresenter协议。实现 NSFilePresenter协议的类,它所代表的文件和目录可以被查看和编辑,这些NSFilePresenter实现类与文件协调者类 NSFileCoordinator结合使用,可以协调管理文件或目录。

这个类我们命名为MyCloudDocument, MyCloudDocument的代码如下:

Java代码   收藏代码
  1. #import <UIKit/UIKit.h>  
  2.   
  3.    
  4.   
  5. @interface MyCloudDocument : UIDocument ①  
  6.   
  7. @property (strong, nonatomic) NSString *contents;  
  8.   
  9. @end  
  10.   
  11.    
  12.   
  13. #import ”MyCloudDocument.h”  
  14.   
  15. @implementation MyCloudDocument  
  16.   
  17.    
  18.   
  19. //加载数据  
  20.   
  21. - (BOOL)loadFromContents:(id)contents ofType:(NSString *)typeName error:(NSError **)outError ②  
  22.   
  23. {  
  24.   
  25. if ([contents length] > 0)  
  26.   
  27. {  
  28.   
  29. self.contents = [[NSString alloc] initWithData:contents encoding:NSUTF8StringEncoding]; ③  
  30.   
  31. }  
  32.   
  33. return YES;  
  34.   
  35. }  
  36.   
  37. //保存数据  
  38.   
  39. - (id)contentsForType:(NSString *)typeName error:(NSError **)outError ④  
  40.   
  41. {  
  42.   
  43. return [self.contents dataUsingEncoding:NSUTF8StringEncoding];  ⑤  
  44.   
  45. }  
  46.   
  47. @end  

 

获得iCloud文档目录

iCloud文档目录是指在Ubiquity容器下的Document目录,因此获得了Ubiquity容器的根目录,就可以获得的iCloud文档目录了。

ViewController.m中的 ubiquitousDocumentsURL方法可以iCloud文档目录:

Java代码   收藏代码
  1. //请求本地Ubiquity容器,从容器中获得Document目录URL  
  2.   
  3. - (NSURL *)ubiquitousDocumentsURL {  
  4.   
  5. NSFileManager* fileManager = [NSFileManager defaultManager]; ①  
  6.   
  7. NSURL* containerURL = [fileManager  
  8.   
  9. URLForUbiquityContainerIdentifier:@"98Z3R5XU29.com.51work6.MyNotes"]; ②  
  10.   
  11. containerURL = [containerURL URLByAppendingPathComponent:@"Documents"]; ③  
  12.   
  13. return containerURL;  
  14.   
  15. }  

 

查找Ubiquity容器中的文档

获得iCloud文档目录后,我们需要找到容器中的文件。查询容器中的文件需要注册两个广播通知:

NSMetadataQueryDidFinishGatheringNotification  查询结束发出通知;

NSMetadataQueryDidUpdateNotification  查询结束,进入开始更新阶段发出的通知;

ViewController.m中注册和解除通知代码如下:

Java代码   收藏代码
  1. - (void)viewDidLoad  
  2.   
  3. {  
  4.   
  5. [super viewDidLoad];  
  6.   
  7. //为查询iCloud文件的变化,注册通知  
  8.   
  9. [[NSNotificationCenter defaultCenter] addObserver:self  
  10.   
  11. selector:@selector(updateUbiquitousDocuments:)  
  12.   
  13. name:NSMetadataQueryDidFinishGatheringNotification object:nil];  
  14.   
  15. [[NSNotificationCenter defaultCenter] addObserver:self  
  16.   
  17. selector:@selector(updateUbiquitousDocuments:)  
  18.   
  19. name:NSMetadataQueryDidUpdateNotification object:nil];  
  20.   
  21.    
  22.   
  23. //查询iCloud文件的变化  
  24.   
  25. [self searchFilesOniCloud];  
  26.   
  27.    
  28.   
  29. }  
  30.   
  31. - (void)didReceiveMemoryWarning  
  32.   
  33. {  
  34.   
  35. [super didReceiveMemoryWarning];  
  36.   
  37. [[NSNotificationCenter defaultCenter] removeObserver:self];  
  38.   
  39. }  
  40.   
  41.    

 

 

保存文档 

保存文档很简单,它是在ViewController.m中的 saveClick:方法处理的,saveClick:方法是点击按钮时候触发:

Java代码   收藏代码
  1. - (IBAction)saveClick:(id)sender {  
  2.   
  3. _myCloudDocument.contents = _txtContent.text;  
  4.   
  5. [_myCloudDocument updateChangeCount:UIDocumentChangeDone];  
  6.   
  7. [_txtContent resignFirstResponder];  
  8.   
  9. }  

 

 

保 存成功之后我们可以在其它设备上看看是否iCloud中已经有abc.txt文档了。如果使用Mac OS X系统电脑,我们可以在“系统偏好设置”中打 开iCloud对话框,点击“管理”按钮可以打开iCloud空间管理对话框,其中的MyNotes是我应用名,右边的abc.txt创建的文件。

16

如果在iPhone、iPod touch和iPad等iOS设备中查看,可以启动设置应用程序,也进入到存储空间管理中,我的iPod touch中查看的情况。

17



这节课主要讲iCloud以及demo。

iCloud

什么是iCloud呢?基本上,对用户来说iCloud只是个网络共享目录的URL,它的意图主要是让用户把他们的文档、数据、备份、app文件放到网上去,然后在他们任意的其他设备上,都可以访问该数据。这是它最主要的用途。

为了app能访问云,它需要获得正确的权限,在xcode中只要点击一个按钮就能获得权限。

只要在project target里单击黄色箭头指向的按钮,它会自动填写授权信息。

授权有两部分内容:一个是创建iCloud需要提供的档案资料;另一个是,它可以被授权写入数据到你多个app共享的云,也就是你用的云不一定是绑定到一个app的。

这个URL是什么?怎么得到这个URL?从NSFileManager获得:

[[NSFileManager defaultManager] URLForUbiquityContainerIdentifier:nil];

如果有多个app写入到同一个地方,那么必须指定container identifier,而且必须和之前的Entitlements匹配。此调用将会返回一个URL,就是可以在云端创建文件的URL。通常情况下你创建的是一个文档,那么应该放到这个URL的Documents目录下,所以URL要追加Documents路径。

大多数的NSFileManager的事情都要另开线程去做。用NSFileManager时还要协调好,因为两个设备可能会访问同一个文件,甚至是同时访问。

NSFilePresenter是一个protocol,和UIDocument一样是抽象的,作用就是为用户显示文档。当文件有变化或有人要写入或读取时,NSFilePresenter需要被通知。

UIDocument是NSFilePresenter的子类,它会自动协调文件的变更。

UIManagedDocument有个内建机制,使得一开始只上传一种基本的SQL数据库,之后所有上传的都是其变化,就是所做的更改记录,然后任何时间任何其他设备都从这个基本数据库获得变化记录后再作用到本地数据库。如何做到这一点呢?UIManagedDocument有个很重要的字典property叫做persistentStoreOptions,需要设置它的NSPersistentStoreUbiquitousContentNameKey,该key的值只是个string,它是文件的名称。NSPersistentStoreUbiquitousContentURLKey是可选的,这是所有变化日志在云端的存放URL,要放到document同级的位置。

从iCloud打开一个UIManagedDocument,它被包装成文件了,文件包装基本上是个目录,而不是一个文件。变化日志就保存在文件包装内部的DocumentMetadata.plist,这是个字典,所以可以从该plist中获得NSPersistentStoreUbiquitousContentNameKey。要从document URL里获取DocumentMetadata.plist。这样做了之后就可以用UIManagedDocument的saveToURL和openWithCompletionHandler了。

需要云中的内容时,要通过特定的查询语句来查询,然后如果该文件改变了,你会得到通知。可以用NSMetadataQuery来创建查询,这里要指定两个东西:一是在iCloud中的搜索范围,有两种范围:一是documentsScope,就是只查documents目录;另一个是datascope,除了documents之外的目录。然后需要指定predicate,用的仍然是NSPredicate,但和Core Data有点不同,要查询目标中某个特定的属性,这种类似于在一个文件系统中搜索。

NSMetadataQuery *query = [[NSMetadataQuery alloc] init];
query.searchScopes = [NSArray arrayWithObjects:scope1, scope2, nil]; 
NSMetadataQueryUbiquitousDocumentsScope is all files in the Documents directory. NSMetadataQueryUbiquitousDataScope is all files NOT in the Documents directory.
query.predicate = [NSPredicate predicateWithFormat:@“%K like ‘*’”, NSMetadataItemFSNameKey]; // all

下面是个注册接收消息的例子:

NSNotificationCenter *center = [NSNotificationCenter defaultCenter];
[center addObserver:self selector:@selector(processQueryResults:) name:NSMetadataQueryDidFinishGatheringNotification // first results object:query];
[center addObserver:self selector:@selector(processQueryResults:) name:NSMetadataQueryDidUpdateNotification // subsequent updates object:query];

NSMetadataQueryDidFinishGatheringNotification在第一次从云端接收数据时会发过来,NSMetadataQueryDidUpdateNotification是云中的内容改变了之后发的。任何时候添加自己为observer,都要在dealloc里remove掉。

- (void)dealloc { 
[[NSNotificationCenter defaultCenter] removeObserver:self]; 
}

这是processQueryResults方法:

- (void)processQueryResults:(NSNotification *)notification{
   [query disableUpdates];
   int resultCount = [query resultCount];
   for (int i = 0; i < resultCount; i++) {
        NSMetadataItem *item = [query resultAtIndex:i];
        NSURL *url = [item valueForAttribute:NSMetadataItemURLKey];
        // do something with the urls here 
        // but remember that these are URLs of the files inside the file wrapper! 
   }
   [query enableUpdates];
}

调用disableUpdates是不希望在处理过程中间得到另一个更新,NSMetadataItem包含查询的各种信息,NSMetadataItemURLKey是查询文件在iCloud的地址。有一件事要小心,这些是文件的URL,没有得到目录的URL,所以你要做的是用一些代码去剥离出文件名再添加到文档目录的末尾。这就是如何列举云端文件,总是要小心会不会有新状况。

如果用的是UIManagedDocument,它会自动协调。如果用NSFileManager删除一个iCloud的文件,就需要协调了。任何需要协调的访问都必须在主线程之外完成。这是文件协调的样子:

NSFileCoordinator *coordinator = [[NSFileCoordinator alloc] initWithFilePresenter:nil];
NSError *coordinationError; 
[coordinator coordinateReadingItemAtURL:(NSURL *)url
                                options:(NSFileCoordinatorReadingOptions)options
                                  error:(NSError **)error
                             byAccessor:^(NSURL *urlToUse) { 
                                           // do your NSFileManager stuff here using urlToUse }
];

filePresenter表示,如果是presenter,得要做协调,但你不用参与到协调中,因为你是提出要求的人。block里的url需要用来读或写的,该url是一个协调的url,它代表了你传进去的url。可以把协调想象为检查所有其他的file presenter,确保一切ok,如果是删除的话,也要锁定文件,强迫它们关闭。都设置好后,它会给你一个删除文件的url,然后就可以删除它了,所有其他还是老的url的presenter可能都会被关闭。

有两个documentState因为延迟和共享访问,发生的概率大了很多,一是EditingDisabled,另一个是InConflict。SavingError状态发生可能需要尝试重新保存。

在冲突的情况下怎么做呢?iCloud会保存所有的版本,它会把你的文档状态设为InConflict,你必须要解决这一冲突。如何解决冲突呢?它会有API能查看所有的版本,让你来决定是合并两个版本的变化,还是只要最新版本。一旦解决了所有的冲突,应该删除所有不打算使用的版本,只保留当前版本。

EditingDisabled状态得更要重视,这个过程是暂时的,基本上如果编辑被禁用时就不要去保存。这些状态是一直随着时间和设备的使用在变化的,所以需要观察documentState,它们变化后会发通知。

有个界面让用户选择是否要把文档保存到iCloud很重要。NSFileManager有个方法可以让你在云端和本地来回移动文件,这总是在主线程之外执行,主要的原因是file presenter需要参与进来,而它们可能是在主线程,不想因为这个也在主线程而进入死锁。这边有一个例子:

dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        NSURL *localURL = ...; // url of document to transfer to iCloud
        NSString *name = [localURL lastPathComponent];
        NSURL *iCloudURL = [iCloudDocumentsURL URLByAppendingPathComponent:name]; 
        NSError *error = nil; 
        [[[NSFileManager alloc] init] setUbiquitous:YES itemAtURL:localURL destinationURL:iCloudURL error:&error]; // move the document });

这是一种根本性的协调,是在移动整个文档,这里是将一个东西从本地沙箱中移动到云端,localURL是本地沙盒的document目录下的某个文件,调用setUbiquitous:YES就是同步到云,itemAtURL是文件目前的位置,destinationURL是在云端的位置。也可以反过来,调用setUbiquitous:NO,itemAtURL将是在云端的位置,destinationURL是文件目前的位置。

Demo

这个demo主要演示从云端获取文件,每个文件将是一个photographer的fetch,将有多个文档就可以进行编辑了,当改变文件清单或改变提取结果后,如何自动更新到其他设备上。

DocumentViewController.h文件代码:

#import <UIKit/UIKit.h> // implement this protocol if you want DocumentViewController to be able to segue to you
 @protocol DocumentViewControllerSegue <NSObject> 
@property (nonatomic, strong) UIManagedDocument *document; 
@end

 @interface DocumentViewController : UITableViewController
@end

DocumentViewController.m文件代码:

#import "DocumentViewController.h"
 #import <CoreData/CoreData.h>
#import "AskerViewController.h" 
// 1. Add a UITableViewController of this class to the storyboard as the rootViewController of the UINavigationController
// Step 2 in cellForRowAtIndexPath: 
// 4. Add Model "documents" which is an NSArray of NSURL
// 7. Add property for an iCloud metadata query called iCloudQuery
 @interface DocumentViewController() <AskerViewControllerDelegate>
 @property (nonatomic, strong) NSArray *documents; // of NSURL
 @property (nonatomic, strong) NSMetadataQuery *iCloudQuery;
@end

 @implementation DocumentViewController
@synthesize documents = _documents;
@synthesize iCloudQuery = _iCloudQuery;
// 5. Implement documents setter to sort the array of urls (and only reload if actual changes)
// Step 6 below in UITableViewDataSource section 
- (void)setDocuments:(NSArray *)documents { 
       documents = [documents sortedArrayUsingComparator:^NSComparisonResult(NSURL *url1, NSURL *url2) {
                  return [[url1 lastPathComponent] caseInsensitiveCompare:[url2 lastPathComponent]]; }];
      if (![_documents isEqualToArray:documents]) {
           _documents = documents;
          [self.tableView reloadData]; } 
}

#pragma mark - iCloud Query 
// 8. Implement getter of iCloudQuery to lazily instantiate it (set it to find all Documents files in cloud) 
// Step 9 in viewWillAppear:
// 10. Add ourself as observer for both initial iCloudQuery results and any updates that happen later 
// Step 11 at the very bottom of this file, then step 12 in viewWillAppear: again. 
// 36. Observe changes to the ubiquious key-value store 
 
 
- (NSMetadataQuery *)iCloudQuery { 
      if (!_iCloudQuery) {
            _iCloudQuery = [[NSMetadataQuery alloc] init];
            _iCloudQuery.searchScopes = [NSArray arrayWithObject:NSMetadataQueryUbiquitousDocumentsScope];
            _iCloudQuery.predicate = [NSPredicate predicateWithFormat:@"%K like '*'", NSMetadataItemFSNameKey];
           [[NSNotificationCenter defaultCenter] addObserver:self 
                                                    selector:@selector(processCloudQueryResults:) 
                                                        name:NSMetadataQueryDidFinishGatheringNotification 
                                                      object:_iCloudQuery]; 
          [[NSNotificationCenter defaultCenter] addObserver:self 
                                                   selector:@selector(processCloudQueryResults:)
                                                       name:NSMetadataQueryDidUpdateNotification
                                                     object:_iCloudQuery]; 
         [[NSNotificationCenter defaultCenter] addObserver:self 
                                                  selector:@selector(ubiquitousKeyValueStoreChanged:)
                                                      name:NSUbiquitousKeyValueStoreDidChangeExternallyNotification
                                                    object:[NSUbiquitousKeyValueStore defaultStore]]; 
    }
    return _iCloudQuery;

// 37. Reload the table whenever the ubiquitous key-value store changes 
// (Don't miss step 38!)
 - (void)ubiquitousKeyValueStoreChanged:(NSNotification *)notification {
   [self.tableView reloadData]; 
// 15. Add a methods to get the URL for the entire cloud and for the Documents directory in the cloud
// 16. Click Add Entitlements in the Summary tab of the Targets section of the Project to enable iCloud for this app 
// (The application is now capable of displaying a list of documents in the cloud.) 
// Step 17 is to segue (see Segue section below). 
- (NSURL *)iCloudURL {
    return [[NSFileManager defaultManager] URLForUbiquityContainerIdentifier:nil]; 
}
 
 
- (NSURL *)iCloudDocumentsURL {
    return [[self iCloudURL] URLByAppendingPathComponent:@"Documents"]; 

// 14. Extract the file package that the passed url is contained in and return it 
- (NSURL *)filePackageURLForCloudURL:(NSURL *)url { 
     if ([[url path] hasPrefix:[[self iCloudDocumentsURL] path]]) {
         NSArray *iCloudDocumentsURLComponents = [[self iCloudDocumentsURL] pathComponents];
         NSArray *urlComponents = [url pathComponents];
         if ([iCloudDocumentsURLComponents count] < [urlComponents count]) {
              urlComponents = [urlComponents subarrayWithRange:NSMakeRange(0, [iCloudDocumentsURLComponents count]+1)];
              url = [NSURL fileURLWithPathComponents:urlComponents]; 
         }
    } 
    return url;
}

// 13. Handle changes to the iCloudQuery's results by iterating through and adding file packages to our Model 
- (void)processCloudQueryResults:(NSNotification *)notification {
     [self.iCloudQuery disableUpdates]; 
     NSMutableArray *documents = [NSMutableArray array];
     int resultCount = [self.iCloudQuery resultCount];
     for (int i = 0; i < resultCount; i++) {
           NSMetadataItem *item = [self.iCloudQuery resultAtIndex:i];
           NSURL *url = [item valueForAttribute:NSMetadataItemURLKey]; // this will be a file, not a directory 
           url = [self filePackageURLForCloudURL:url];
           if (url && ![documents containsObject:url]) [documents addObject:url]; // in case a file package contains multiple files, don't add twice 
     } 

     self.documents = documents;
     [self.iCloudQuery enableUpdates];
}

#pragma mark - View Controller Lifecycle 
// 9. Start up the iCloudQuery in viewWillAppear: if not already started
// 12. Turn iCloudQuery updates on and off as we appear/disappear from the screen 
// 38. Since changes that WE make to the ubiquitous key-value store don't generate an NSNotification,
// we are responsible for updating our UI when we change it.
// We'll be cheap here and just reload ourselves each time we appear!
// Probably would be a lot better to have our own internal NSNotification or some such. 
- (void)viewWillAppear:(BOOL)animated {
    [super viewWillAppear:animated]; 
    [self.tableView reloadData]; 
    // step 38: ugh! 
    if (![self.iCloudQuery isStarted]) [self.iCloudQuery startQuery];
     [self.iCloudQuery enableUpdates]; 
 
 
- (void)viewWillDisappear:(BOOL)animated {
    [self.iCloudQuery disableUpdates]; 
    [super viewWillDisappear:animated]; 

#pragma mark - Autorotation 
// 3. Autorotation YES in all orientations 
// Back to the top for step 4. 
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation { return YES; }

#pragma mark - UITableViewDataSource 
// 6. Implement UITableViewDataSource number of rows in section and cellForRowAtIndexPath: using Model 
// Back to top for step 7.
 - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section { return [self.documents count]; } 
// 2. Set the reuse identifier of the prototype to be "Document Cell" and set in cellForRowAtIndexPath:
// 33. Set the subtitle of the cell to whatever string is in the ubiquitious key-value store under the document name 
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { 
     static NSString *CellIdentifier = @"Document Cell"
     UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
     if (cell == nil) { 
          cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];
     } // Configure the cell... 
     NSURL *url = [self.documents objectAtIndex:indexPath.row];
     cell.textLabel.text = [url lastPathComponent];
     cell.detailTextLabel.text = [[NSUbiquitousKeyValueStore defaultStore] objectForKey:[url lastPathComponent]];
     return cell;

// Convenience method for logging errors returned through NSError 
- (void)logError:(NSError *)error inMethod:(SEL)method {
    NSString *errorDescription = error.localizedDescription; 
    if (!errorDescription) errorDescription = @"???"
    NSString *errorFailureReason = error.localizedFailureReason;
    if (!errorFailureReason) errorFailureReason = @"???";
    if (error) NSLog(@"[%@ %@] %@ (%@)", NSStringFromClass([self class]), NSStringFromSelector(method), errorDescription, errorFailureReason); 

// 25. Remove the url from the cloud in a coordinated manner (and in a separate thread)
// (At this point, the application is capable of both adding and deleting documents from the cloud.)
// The next step is to be able to edit the documents themselves in PhotographersTableViewController (step 26).
// 34. Remove any ubiquitous key-value store entry for this document too (since we're deleting it) 
// Next step is to actually set the key-value store entry for a document. Back in PhotographersTVC (step 35).
 - (void)removeCloudURL:(NSURL *)url {
    [[NSUbiquitousKeyValueStore defaultStore] removeObjectForKey:[url lastPathComponent]];
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^
            NSFileCoordinator *coordinator = [[NSFileCoordinator alloc] initWithFilePresenter:nil]; 
            NSError *coordinationError; 
            [coordinator coordinateWritingItemAtURL:url options:NSFileCoordinatorWritingForDeleting
                         error:&coordinationError byAccessor:^(NSURL *newURL) { 
                                         NSError *removeError;
                                         [[NSFileManager defaultManager] removeItemAtURL:newURL error:&removeError];
                                         [self logError:removeError inMethod:_cmd]; 
                                         // _cmd means "this method" (it's a SEL) 
                                         // should also remove log files in CoreData directory in the cloud! 
                                         // i.e., delete the files in [self iCloudCoreDataLogFilesURL]/[url lastPathComponent] 
                         }]; 
            [self logError:coordinationError inMethod:_cmd]; 
       });

// 24. Make documents deletable by removing them from the Model, and from the table, and from the cloud.
// (Note that we access _documents directly here! Ack! That's bad form! We should probably find a better way.)
 - (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath { 
     if (editingStyle == UITableViewCellEditingStyleDelete) {
          NSURL *url = [self.documents objectAtIndex:indexPath.row]; 
          NSMutableArray *documents = [self.documents mutableCopy];
          [documents removeObject:url]; 
          _documents = documents;
          // Argh!
          [tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationFade]; 
          [self removeCloudURL:url]; 
     }
}
#pragma mark - Segue 
- (NSURL *)iCloudCoreDataLogFilesURL { 
      return [[self iCloudURL] URLByAppendingPathComponent:@"CoreData"];

// 19. Set persistentStoreOptions in the document before segueing 
// (Both the automatic schema-migration options and the "logging-based Core Data" options are set.) 
// (The application is now capable of showing the contents of documents in the cloud.) 
// See step 20 in PhotographersTableViewController (adding a spinner to better see what's happening). 
- (void)setPersistentStoreOptionsInDocument:(UIManagedDocument *)document { 
     NSMutableDictionary *options = [NSMutableDictionary dictionary]; 
     [options setObject:[NSNumber numberWithBool:YES] forKey:NSMigratePersistentStoresAutomaticallyOption];
     [options setObject:[NSNumber numberWithBool:YES] forKey:NSInferMappingModelAutomaticallyOption]; 
     [options setObject:[document.fileURL lastPathComponent] forKey:NSPersistentStoreUbiquitousContentNameKey];
     [options setObject:[self iCloudCoreDataLogFilesURL] forKey:NSPersistentStoreUbiquitousContentURLKey];
     document.persistentStoreOptions = options; 
}
// 17. In the storyboard, create a Push segue called "Show Document" from this VC to our old Photomania VC chain 
// 18. Prepare for segue by getting the URL at the segued-from row, creating a document, and setting it in destination 
// (Note the generic mechanism we use to get the segued-from indexPath.) 
// (Note how we use a protocol (DocumentViewControllerSegue) to generically segue to any destination.) 
// 21. Add a + button to the storyboard which segues Modally to an AskerViewController (get this from KitchenSink) 
// (Note, you will have to add the questionLabel and answerTextFields to the AskerViewController scene.) 
// 22. Modify prepare for segue to set ourself as the delegate and set the question of the AskerViewController. 
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender { 
    if ([segue.identifier isEqualToString:@"New Document"]) {
           AskerViewController *asker = (AskerViewController *)segue.destinationViewController; 
           asker.delegate = self; asker.question = @"New document name:"
   } else {
           NSIndexPath *indexPath = nil; 
           if ([sender isKindOfClass:[NSIndexPath class]]) {
                   indexPath = (NSIndexPath *)sender; 
           } else if ([sender isKindOfClass:[UITableViewCell class]]) { 
                   indexPath = [self.tableView indexPathForCell:sender]; 
           } else if (!sender || (sender == self) || (sender == self.tableView)) { 
                   indexPath = [self.tableView indexPathForSelectedRow]; 
           }

          if (indexPath && [segue.identifier isEqualToString:@"Show Document"]) {
               if ([segue.destinationViewController conformsToProtocol:@protocol(DocumentViewControllerSegue)]) {
                      NSURL *url = [self.documents objectAtIndex:indexPath.row]; 
                      [segue.destinationViewController setTitle:[url lastPathComponent]];
                      UIManagedDocument *document = [[UIManagedDocument alloc] initWithFileURL:url];
                      [self setPersistentStoreOptionsInDocument:document];
                      // make cloud Core Data documents efficient! 
                      [segue.destinationViewController setDocument:document]; 
               }
         }
    }

// 23. Implement AVC delegate to create an NSURL in the cloud with the chosen name, add it to our Model,
// then segue to create it (and we must dismiss the AVC too) 
// (It is now possible to create documents in the cloud using the application!) 
- (void)askerViewController:(AskerViewController *)sender didAskQuestion:(NSString *)question andGotAnswer:(NSString *)answer { 
      NSURL *url = [[self iCloudDocumentsURL] URLByAppendingPathComponent:answer];
      NSMutableArray *documents = [self.documents mutableCopy];
      [documents addObject:url];
      self.documents = documents;
      int row = [self.documents indexOfObject:url];
      NSIndexPath *indexPath = [NSIndexPath indexPathForRow:row inSection:0];
      [self performSegueWithIdentifier:@"Show Document" sender:indexPath];
      [self dismissModalViewControllerAnimated:YES];
}
#pragma mark - Dealloc 
// 11. Remove ourself as an observer (of anything) when we leave the heap 
- (void)dealloc { 
[[NSNotificationCenter defaultCenter] removeObserver:self];
} @end

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值