22.0~22.6 iCloud

22.0. IntroductioniCloud

假设一位同事在办公室玩手游,玩到第12关,回到家后用pad玩,却得从第一关开始玩起。这真不好,如果在pad上也能同步在第12关,那该多好啊。iCloud能解决这个问题。

要使用iCloud服务,你需要正确配置证书

22.1. Setting Up Your App for iCloud

工程配置

简述版:

1,创建iCloud enabled的App ID

2,创建关联这个appID的新证书并下载安装

3,Xcode创建新应用,并关联刚安装的新证书

4,在Capabilities页签,打开iCloud开关

详细:

1,创建应用,假设是com.pixolity.ios.cookbook.icloudapp 

2,创建App ID,勾选iCloud




3,创建新的development provision profile ,记住要关联刚才的app id

4,创建完后,下载并安装(拖到iTunes安装)


5,Xcode创建新app,bundle ID 设置为上面写的App ID。如果输错,你的应用将用不了那个证书

6,target中选择刚安装的证书

7,Capabilities中打开iCloud开关




编译一下,如果编译器说entitlements file找不到,请先核查下Code Signing Entitlements的路径,有可能你把它改成这样就可以:$(SRCROOT)/$(TARGET_NAME)/


22.2. Storing and Synchronizing Dictionaries in iCloud

保存和同步数据

NSUbiquitousKeyValueStore

其保存的数据会根据证书文件的信息保存到你的iCloud 账户中,换句话说,你只管用NSUbiquitousKeyValueStore来保存数据,不用担心与别的数据冲突。

在上一节中,我们Capabilities中设置勾选iCloud,然而想用NSUbiquitousKeyValueStore类,我们还需勾选Use key-value store.

NSUbiquitousKeyValueStore的用法跟NSUserDefaults很像。可以保存string,Boolean,integer,float and other values.每个值都需要一个键来对应。他们的不同是,一个保存在云端,一个保存在本地的.plist文件中,当删除应用时,这些数据会被删除。

在iCloud,一个应用实例用一个唯一的id来保存数据,其由3部分组成:

Team ID:开发者ID

Reverse domain-style of company identifier: 公司域名反转

App identifier and optional suffix:应用ID和后缀

-(void)testStoreSyn

{

    NSUbiquitousKeyValueStore *kvoStore =

    [NSUbiquitousKeyValueStore defaultStore];

    NSString *stringValue = @"My String";

    NSString *stringValueKey = @"MyStringKey";

    BOOL boolValue = YES;

    NSString *boolValueKey = @"MyBoolKey";

    BOOL mustSynchronize = NO;

    if ([[kvoStore stringForKey:stringValueKey] length] == 0){

        NSLog(@"Could not find the string value in iCloud. Setting...");

        [kvoStore setString:stringValue forKey:stringValueKey];

        mustSynchronize = YES;

    } else {

        NSLog(@"Found the string in iCloud, getting...");

        stringValue = [kvoStore stringForKey:stringValueKey];

    }

    if ([kvoStore boolForKey:boolValueKey] == NO){

        NSLog(@"Could not find the boolean value in iCloud. Setting...");

        [kvoStore setBool:boolValue forKey:boolValueKey];

        mustSynchronize = YES;

    } else {

        NSLog(@"Found the boolean in iCloud, getting...");

        boolValue = [kvoStore boolForKey:boolValueKey];

    }

    if (mustSynchronize){

        if ([kvoStore synchronize]){

            NSLog(@"Successfully synchronized with iCloud.");

        } else {

            NSLog(@"Failed to synchronize with iCloud.");

        }

    }

}

第一次运行:

2014-08-12 15:09:42.305 ggcloud[855:907] Could not find the string value in iCloud. Setting...

2014-08-12 15:09:42.309 ggcloud[855:907] Could not find the boolean value in iCloud. Setting...

2014-08-12 15:09:42.347 ggcloud[855:907] Successfully synchronized with iCloud.




同步之后运行:

2014-08-12 15:10:11.438 ggcloud[868:907] Found the string in iCloud, getting...

2014-08-12 15:10:11.442 ggcloud[868:907] Found the boolean in iCloud, getting...




22.3. Creating and Managing Folders for Apps in iCloud

保存文件到iCloud


-(void)testCreatAndManageFolder

{

    NSFileManager *fileManager = [[NSFileManager alloc] init];

    NSString *teamID = <# Put your team ID here #>;

    NSString *bundleId = [[NSBundle mainBundle] bundleIdentifier];

    NSString *rootFolderIdentifier = [NSString stringWithFormat:

                                      @"%@.%@",

                                      teamID, bundleId];

    NSURL *containerURL =

    [fileManager URLForUbiquityContainerIdentifier:rootFolderIdentifier];

    NSString *documentsDirectory =

    [[containerURL path]

     stringByAppendingPathComponent:@"Documents"];

    BOOL isDirectory = NO;

    BOOL mustCreateDocumentsDirectory = NO;

    if ([fileManager fileExistsAtPath:documentsDirectory isDirectory:&isDirectory]){

        if (isDirectory == NO){

            mustCreateDocumentsDirectory = YES;

        }

    } else {

        mustCreateDocumentsDirectory = YES;

    }

    if (mustCreateDocumentsDirectory){

        NSLog(@"Must create the directory.");

        NSError *directoryCreationError = nil;

        if ([fileManager createDirectoryAtPath:documentsDirectory withIntermediateDirectories:YES

                                    attributes:nil

                                         error:&directoryCreationError]){

            NSLog(@"Successfully created the folder.");

        } else {

            NSLog(@"Failed to create the folder with error = %@",

            directoryCreationError);

        }

    } else {

        NSLog(@"This folder already exists.");

    }

    

}


打印:

2014-08-12 15:29:55.300 ggcloud[945:907] Must create the directory.

2014-08-12 15:29:55.304 ggcloud[945:907] Successfully created the folder.

再运行一次:

2014-08-12 15:30:26.426 ggcloud[961:907] This folder already exists.


或:

-(void)testCreateAndManageFloder2

{

    NSString *teamID = <# Put your team ID here #>;

    NSString *containerID = [[NSBundle mainBundle] bundleIdentifier];

    NSString *documentsDirectory = nil;

    if ([self createIcloudDirectory:@"Documents3" recursiveCreation:YES

                             teamID:teamID

                    iCloudContainer:containerID

                          finalPath:&documentsDirectory]){

        NSLog(@"Successfully created the directory in %@", documentsDirectory);

    } else {

        NSLog(@"Failed to create the directory.");

    }

}


- (BOOL) createIcloudDirectory:(NSString *)paramDirectory

             recursiveCreation:(BOOL)paramRecursiveCreation

                        teamID:(NSString *)paramTeamID

               iCloudContainer:(NSString *)paramContainer

                     finalPath:(NSString **)paramFinalPath{

    BOOL result = NO;

    NSFileManager *fileManager = [[NSFileManager alloc] init];

    NSString *rootFolderIdentifier = [NSString stringWithFormat:

                                      @"%@.%@", paramTeamID, paramContainer];

    NSURL *containerURL =[fileManager URLForUbiquityContainerIdentifier:rootFolderIdentifier];

    NSString *documentsDirectory =

    [[containerURL path]

     stringByAppendingPathComponent:paramDirectory];

    if (paramFinalPath != nil){

        *paramFinalPath = documentsDirectory;

    }

    BOOL isDirectory = NO;

    BOOL mustCreateDocumentsDirectory = NO;

    if ([fileManager fileExistsAtPath:documentsDirectory isDirectory:&isDirectory]){

        if (isDirectory == NO){

            mustCreateDocumentsDirectory = YES;

        }

    } else {

        mustCreateDocumentsDirectory = YES;

    }

    if (mustCreateDocumentsDirectory){

        NSLog(@"Must create the directory.");

        NSError *directoryCreationError = nil;

        if ([fileManager createDirectoryAtPath:documentsDirectory

                   withIntermediateDirectories:paramRecursiveCreation

                                    attributes:nil

                                         error:&directoryCreationError]){

            result = YES;

            NSLog(@"Successfully created the folder.");

        } else {

            NSLog(@"Failed to create the folder with error = %@",directoryCreationError);

            }

    } else {

        NSLog(@"This folder already exists.");

        result = YES;

    }

    return result;

}


打印:

2014-08-12 15:35:20.908 ggcloud[1018:907] Must create the directory.

2014-08-12 15:35:20.912 ggcloud[1018:907] Successfully created the folder.

2014-08-12 15:35:20.914 ggcloud[1018:907] Successfully created the directory in /private/var/mobile/Library/Mobile Documents/<# Put your team ID here #>~com~gaozgao~ggcloud/Documents3


运行之后,可以在手机->设置->iCloud->Storage&Backup->Documents&DATA 找到你的应用,在这里可以看到你之前创建的文件。(在这里还看不到,到下一节就能看到)


22.4. Searching for Files and Folders in iCloud


Use the NSMetadataQuery class.


-(void)testSearch

{

    /* Listen for a notification that gets fired when the metadata query

     has finished finding the items we were looking for */

    [[NSNotificationCenter defaultCenter]

     addObserver:self

     selector:@selector(handleMetadataQueryFinished:)

     name:NSMetadataQueryDidFinishGatheringNotification

     object:nil];

    /* Create our query now */

    self.metadataQuery = [[NSMetadataQuery alloc] init];

    NSArray *searchScopes = [[NSArray alloc] initWithObjects:

                             NSMetadataQueryUbiquitousDocumentsScope, nil];

    [self.metadataQuery setSearchScopes:searchScopes];

    NSPredicate *predicate = [NSPredicate predicateWithFormat:

                              @"%K like %@",

                              NSMetadataItemFSNameKey,

                              @"*"];

    [self.metadataQuery setPredicate:predicate];

    if ([self.metadataQuery startQuery]){

        NSLog(@"Successfully started the query.");

    } else {

        NSLog(@"Failed to start the query.");

    }

}


- (NSURL *) urlForDocumentsFolderIniCloud{

    NSURL *result = nil;

    NSString *teamID = kMyTeamID;

    NSString *containerID = [[NSBundle mainBundle] bundleIdentifier];

    NSString *teamIDAndContainerID = [[NSString alloc] initWithFormat:@"%@.%@",

                                      teamID, containerID];

    NSFileManager *fileManager = [[NSFileManager alloc] init];

    NSURL *appiCloudContainerURL =

    [fileManager URLForUbiquityContainerIdentifier:teamIDAndContainerID];

    result = [appiCloudContainerURL URLByAppendingPathComponent:@"Documents"

                                                    isDirectory:YES];

    if ([fileManager fileExistsAtPath:[result path]] == NO){

        /* The Documents directory does NOT exist in our app's iCloud

         container; attempt to create it now */

        NSError *creationError = nil;

        BOOL created = [fileManager createDirectoryAtURL:result

                             withIntermediateDirectories:YES

                                              attributes:nil

                                                   error:&creationError];

        if (created){

            NSLog(@"Successfully created the Documents folder in iCloud.");

        } else {

            NSLog(@"Failed to create the Documents folder in \

                  iCloud. Error = %@", creationError);

            result = nil;

        }

    } else {

        /* the Documents directory already exists in our app's

         iCloud container; we don't have to do anything */

    }

    return result;

}


- (NSURL *) urlForRandomFileInDocumentsFolderInIcloud{

    NSURL *result = nil;

    NSUInteger randomNumber = arc4random() % NSUIntegerMax;

    NSString *randomFileName = [[NSString alloc] initWithFormat:@"%lu.txt", (unsigned long)randomNumber];

    /* Check in the metadata query if this file already exists */

    __block BOOL fileExistsAlready = NO;

    [self.metadataQuery.results enumerateObjectsUsingBlock:

     ^(NSMetadataItem *item, NSUInteger idx, BOOL *stop) {

         NSString *itemFileName = [item valueForAttribute:NSMetadataItemFSNameKey];

         if ([itemFileName isEqualToString:randomFileName]){

             NSLog(@"This file already exists. Aborting...");

             fileExistsAlready = YES;

             *stop = YES;

         }

     }];

    if (fileExistsAlready){

        return nil;

    }

    result = [[self urlForDocumentsFolderIniCloud]

              URLByAppendingPathComponent:randomFileName];

    return result;

}


- (NSURL *) urlForRandomFileInDocumentsFolderForFileWithName :(NSString *)paramFileName{

    NSFileManager *fileManager = [[NSFileManager alloc] init];

    NSURL *documentsUrl = [fileManager URLForDirectory:NSDocumentDirectory

                                              inDomain:NSUserDomainMask

                                     appropriateForURL:Nil

                                                create:YES

                                                 error:nil];

    return [documentsUrl URLByAppendingPathComponent:paramFileName];

}


- (void) enumerateMetadataResults:(NSArray *)paramResults{

    [paramResults enumerateObjectsUsingBlock:

     ^(NSMetadataItem *item, NSUInteger index, BOOL *stop) {

         NSString *itemName = [item valueForAttribute:NSMetadataItemFSNameKey];

         NSURL *itemURL = [item valueForAttribute:NSMetadataItemURLKey];

         NSNumber *itemSize = [item valueForAttribute:NSMetadataItemFSSizeKey];

         NSLog(@"Item name = %@", itemName);

         NSLog(@"Item URL = %@", itemURL);

         NSLog(@"Item Size = %llu",

               (unsigned long long)[itemSize unsignedLongLongValue]);

     }];

}


- (void) handleMetadataQueryFinished:(id)paramSender{

    NSLog(@"Search finished");

    if ([[paramSender object] isEqual:self.metadataQuery] == NO){

        NSLog(@"An unknown object called this method. Not safe to proceed.");

        return;

    }

    /* Stop listening for notifications as we are not expecting

     anything more */

    [[NSNotificationCenter defaultCenter] removeObserver:self];

    /* We are done with the query, let's stop the process now */

    [self.metadataQuery disableUpdates];

    [self.metadataQuery stopQuery];

    [self enumerateMetadataResults:self.metadataQuery.results];

    if ([self.metadataQuery.results count] == 0){

        NSLog(@"No files were found.");

    }

    NSURL *urlForFileIniCloud =

    [self urlForRandomFileInDocumentsFolderInIcloud];

    if (urlForFileIniCloud == nil){

        NSLog(@"Cannot create a file with this URL. URL is empty.");

        return;

    }

    NSString *fileName = [[[urlForFileIniCloud path]

                           componentsSeparatedByString:@"/"] lastObject];

    NSURL *urlForFileInAppSandbox =

    [self urlForRandomFileInDocumentsFolderForFileWithName:fileName];

    NSString *fileContent =

    [[NSString alloc] initWithFormat:@"Content of %@",

     [[self urlForRandomFileInDocumentsFolderInIcloud] path]];

    /* Save the file temporarily in the app bundle and then move

     it to the cloud */

    NSError *writingError = nil;

    BOOL couldWriteToAppSandbox =

    [fileContent writeToFile:[urlForFileInAppSandbox path]

                  atomically:YES

                    encoding:NSUTF8StringEncoding

                       error:&writingError];

    /* If cannot save the file, just return from method because it

     won't make any sense to continue as we, ideally, should have

     stored the file in iCloud from the app sandbox but here, if an

     error has occurred, we cannot continue */

    if (couldWriteToAppSandbox == NO){

        NSLog(@"Failed to save the file to app sandbox. Error = %@",

              writingError);

        return;

    }

    NSFileManager *fileManager = [[NSFileManager alloc] init];

    /* Now move the file to the cloud */

    NSError *ubiquitousError = nil;

    BOOL setUbiquitousSucceeded = [fileManager setUbiquitous:YES

                                                   itemAtURL:urlForFileInAppSandbox

                                              destinationURL:urlForFileIniCloud

                                                       error:&ubiquitousError];

    if (setUbiquitousSucceeded){

        NSLog(@"Successfully moved the file to iCloud.");

        /* The file has been moved from App Sandbox to iCloud */

    } else {

        NSLog(@"Failed to move the file to iCloud with error = %@",

              ubiquitousError);

    }

}


运行了3次之后:

2014-08-12 16:19:30.479 ggcloud[1154:907] Successfully started the query.

2014-08-12 16:19:30.644 ggcloud[1154:907] Search finished

2014-08-12 16:19:30.646 ggcloud[1154:907] Item name = 3555629546.txt

2014-08-12 16:19:30.647 ggcloud[1154:907] Item URL = file://localhost/private/var/mobile/Library/Mobile%20Documents/<# Put your team ID here #>~com~gaozgao~ggcloud/Documents/3555629546.txt

2014-08-12 16:19:30.648 ggcloud[1154:907] Item Size = 111

2014-08-12 16:19:30.650 ggcloud[1154:907] Item name = 4290254931.txt

2014-08-12 16:19:30.651 ggcloud[1154:907] Item URL = file://localhost/private/var/mobile/Library/Mobile%20Documents/<# Put your team ID here #>~com~gaozgao~ggcloud/Documents/4290254931.txt

2014-08-12 16:19:30.652 ggcloud[1154:907] Item Size = 110

2014-08-12 16:19:30.723 ggcloud[1154:907] Successfully moved the file to iCloud.





22.5. Storing User Documents in iCloud

CloudDocument.h文件

#import <UIKit/UIKit.h>


@class CloudDocument;


@protocol CloudDocumentProtocol<NSObject>

- (void) cloudDocumentChanged:(CloudDocument *)paramSender;

@end


@interface CloudDocument : UIDocument


@property (nonatomic, strong) NSString *documentText;

@property (nonatomic, weak) id<CloudDocumentProtocol> delegate;

/* Designated Initializer */

- (id) initWithFileURL:(NSURL *)paramURL delegate:(id<CloudDocumentProtocol>)paramDelegate;


@end


CloudDocument.m文件

#import "CloudDocument.h"


@implementation CloudDocument


- (id) initWithFileURL:(NSURL *)paramURL

              delegate:(id<CloudDocumentProtocol>)paramDelegate{

    self = [super initWithFileURL:paramURL];

    if (self != nil){

        if (paramDelegate == nil){

            NSLog(@"Warning: no delegate is given.");

        }

        _delegate = paramDelegate;

    }

    return self;

}


- (id) initWithFileURL:(NSURL *)paramURL{

    return [self initWithFileURL:paramURL

                        delegate:nil];

}


- (id) contentsForType:(NSString *)typeName

                 error:(NSError *__autoreleasing *)outError{

    if ([self.documentText length] == 0){

        self.documentText = @"New Document";

    }

    return [self.documentText dataUsingEncoding:NSUTF8StringEncoding];

}


- (BOOL) loadFromContents:(id)contents

                   ofType:(NSString *)typeName

                    error:(NSError *__autoreleasing *)outError{

    NSData *data = (NSData *)contents;

    if ([data length] == 0){

        self.documentText = @"New Document";

    } else {

        self.documentText = [[NSString alloc]

                             initWithData:data

                             encoding:NSUTF8StringEncoding];

    }

    if ([_delegate respondsToSelector:@selector(cloudDocumentChanged:)]){

        [_delegate cloudDocumentChanged:self];

    }

    return YES;

}


@end


ViewController.h文件

#import <UIKit/UIKit.h>


@interface ViewController : UIViewController


@end


ViewController.m文件

#import "ViewController.h"

#import "CloudDocument.h"


#define kMyTeamID @"你的teamID"


@interface ViewController ()<CloudDocumentProtocol, UITextViewDelegate>


@property (nonatomic, strong) CloudDocument *cloudDocument;

@property (nonatomic, strong) UITextView *textViewCloudDocumentText;

@property (nonatomic, strong) NSMetadataQuery *metadataQuery;


@end


@implementation ViewController


- (void)viewDidLoad

{

    [super viewDidLoad];

    [self listenForKeyboardNotifications];

    self.view.backgroundColor = [UIColor brownColor];

    [self setupTextView];

    [self startSearchingForDocumentIniCloud];

}


- (void)didReceiveMemoryWarning

{

    [super didReceiveMemoryWarning];

    // Dispose of any resources that can be recreated.

}


- (NSURL *) urlForDocumentsDirectoryInIcloud{

    NSURL *result = nil;

    NSString *teamID = kMyTeamID;

    NSString *containerID = [[NSBundle mainBundle] bundleIdentifier];

    NSString *teamIdAndContainerId = [NSString stringWithFormat:@"%@.%@",

                                      teamID,

                                      containerID];

    NSFileManager *fileManager = [[NSFileManager alloc] init];

    NSURL *iCloudURL = [fileManager

                        URLForUbiquityContainerIdentifier:teamIdAndContainerId];

    NSURL *documentsFolderURLIniCloud =

    [iCloudURL URLByAppendingPathComponent:@"Documents"

                               isDirectory:YES];

    /* If it doesn't exist, create it */

    if ([fileManager

         fileExistsAtPath:[documentsFolderURLIniCloud path]] == NO){

        NSLog(@"The documents folder does NOT exist in iCloud. Creating...");

        NSError *folderCreationError = nil;

        BOOL created = [fileManager createDirectoryAtURL:documentsFolderURLIniCloud

                             withIntermediateDirectories:YES

                                              attributes:nil

                                                   error:&folderCreationError];

        if (created){

            NSLog(@"Successfully created the Documents folder in iCloud.");

            result = documentsFolderURLIniCloud;

        } else {

            NSLog(@"Failed to create the Documents folder in iCloud. \

                  Error = %@", folderCreationError);

        }

    } else {

        NSLog(@"The Documents folder already exists in iCloud.");

        result = documentsFolderURLIniCloud;

    }

    return result;

}


- (NSURL *) urlForFileInDocumentsDirectoryIniCloud{

    return [[self urlForDocumentsDirectoryInIcloudURLByAppendingPathComponent:@"UserDocument.txt"];

}



- (void) setupTextView{

    /* Create the text view */

    CGRect textViewRect = CGRectMake(20.0f,

                                     20.0f,

                                     self.view.bounds.size.width - 40.0f,

                                     self.view.bounds.size.height - 40.0f);

    self.textViewCloudDocumentText = [[UITextView alloc] initWithFrame:

                                      textViewRect];

    self.textViewCloudDocumentText.delegate = self;

    self.textViewCloudDocumentText.font = [UIFont systemFontOfSize:20.0f];

    [self.view addSubview:self.textViewCloudDocumentText];

}


- (void) listenForKeyboardNotifications{

    /* As we have a text view, when the keyboard shows on screen, we want to

     make sure our textview's content is fully visible so start

     listening for keyboard notifications */

    [[NSNotificationCenter defaultCenter] addObserver:self

                                             selector:@selector(handleKeyboardWillShow:)

                                                 name:UIKeyboardWillShowNotification

                                               object:nil];

    [[NSNotificationCenter defaultCenter] addObserver:self

                                             selector:@selector(handleKeyboardWillHide:)

                                                 name:UIKeyboardWillHideNotification

                                               object:nil];

}


- (void) startSearchingForDocumentIniCloud{

    /* Start searching for existing text documents */

    self.metadataQuery = [[NSMetadataQuery alloc] init];

    NSPredicate *predicate = [NSPredicate predicateWithFormat:@"%K like %@",

                              NSMetadataItemFSNameKey,

                              @"*"];

    [self.metadataQuery setPredicate:predicate];

    NSArray *searchScopes = [[NSArray alloc] initWithObjects:

                             NSMetadataQueryUbiquitousDocumentsScope,

                             nil];

    [self.metadataQuery setSearchScopes:searchScopes];

    NSString *metadataNotification = NSMetadataQueryDidFinishGatheringNotification;

    [[NSNotificationCenter defaultCenter] addObserver:self

                                             selector:@selector(handleMetadataQueryFinished:)

                                                 name:metadataNotification

                                               object:nil];

    [self.metadataQuery startQuery];

}


- (void) handleMetadataQueryFinished:(NSNotification *)paramNotification{

    /* Make sure this is the metadata query that we were expecting... */

    NSMetadataQuery *senderQuery = (NSMetadataQuery *)[paramNotification object];

    if ([senderQuery isEqual:self.metadataQuery] == NO){

        NSLog(@"Unknown metadata query sent us a message.");

        return;

    }

    [self.metadataQuery disableUpdates];

    /* Now we stop listening for these notifications because we don't really

     have to, any more */

    NSString *metadataNotification =

    NSMetadataQueryDidFinishGatheringNotification;

    [[NSNotificationCenter defaultCenter] removeObserver:self

                                                    name:metadataNotification

                                                  object:nil];

    [self.metadataQuery stopQuery];

    NSLog(@"Metadata query finished.");

    /* Let's find out if we had previously created this document in the user's

     cloud space because if yes, then we have to avoid overwriting that

     document and just use the existing one */

    __block BOOL documentExistsIniCloud = NO;

    NSString *FileNameToLookFor = @"UserDocument.txt";

    NSArray *results = self.metadataQuery.results;

    [results enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {

         NSMetadataItem *item = (NSMetadataItem *)obj;

         NSURL *itemURL = [item valueForAttribute:NSMetadataItemURLKey];

         NSString *lastComponent = [[itemURL pathComponents] lastObject];

         if ([lastComponent isEqualToString:FileNameToLookFor]){

             if ([itemURL

                  isEqual:[self urlForFileInDocumentsDirectoryIniCloud]]){

                 documentExistsIniCloud = YES;

                 *stop = YES; }

         }

    }];

    NSURL *urlOfDocument = [self urlForFileInDocumentsDirectoryIniCloud];

    self.cloudDocument = [[CloudDocument alloc] initWithFileURL:urlOfDocument delegate:self];

    __weak ViewController *weakSelf = self;

    /* If the document exists, open it */

    if (documentExistsIniCloud){

        NSLog(@"Document already exists in iCloud. Loading it from there...");

        [self.cloudDocument openWithCompletionHandler:^(BOOL success) {

            if (success){

                ViewController *strongSelf = weakSelf;

                NSLog(@"Successfully loaded the document from iCloud.");

                strongSelf.textViewCloudDocumentText.text = strongSelf.cloudDocument.documentText;

            } else {

                NSLog(@"Failed to load the document from iCloud.");

            }

        }];

    } else {

        NSLog(@"Document does not exist in iCloud. Creating it...");

        /* If the document doesn't exist, ask the CloudDocument class to

         save a new file on that address for us */

        [self.cloudDocument

         saveToURL:[self urlForFileInDocumentsDirectoryIniCloud] forSaveOperation:UIDocumentSaveForCreating completionHandler:^(BOOL success) {

             if (success){

                 NSLog(@"Successfully created the new file in iCloud.");

                 ViewController *strongSelf = weakSelf;

                 strongSelf.textViewCloudDocumentText.text =

                 strongSelf.cloudDocument.documentText;

             } else {

                 NSLog(@"Failed to create the file.");

             }

         }];

    }

}


- (void) textViewDidChange:(UITextView *)textView{

    self.cloudDocument.documentText = textView.text;

    [self.cloudDocument updateChangeCount:UIDocumentChangeDone];

}


- (void) cloudDocumentChanged:(CloudDocument *)paramSender{

    self.textViewCloudDocumentText.text = paramSender.documentText;

}


- (void) handleKeyboardWillShow:(NSNotification *)paramNotification{

    NSDictionary *userInfo = [paramNotification userInfo];

    NSValue *animationDurationObject =

    [userInfo valueForKey:UIKeyboardAnimationDurationUserInfoKey];

    NSValue *keyboardEndRectObject =

    [userInfo valueForKey:UIKeyboardFrameEndUserInfoKey];

    double animationDuration = 0.0;

    CGRect keyboardEndRect = CGRectMake(0.0f, 0.0f, 0.0f, 0.0f);

    [animationDurationObject getValue:&animationDuration];

    [keyboardEndRectObject getValue:&keyboardEndRect];

    UIWindow *window = [[[UIApplication sharedApplication] delegate] window];

    /* Convert the frame from window's coordinate system to

     our view's coordinate system */

    keyboardEndRect = [self.view convertRect:keyboardEndRect

                                    fromView:window];

    [UIView animateWithDuration:animationDuration animations:^{

        CGRect intersectionOfKeyboardRectAndWindowRect =

        CGRectIntersection(self.view.frame, keyboardEndRect);

        CGFloat bottomInset =

        intersectionOfKeyboardRectAndWindowRect.size.height;

        self.textViewCloudDocumentText.contentInset =

        UIEdgeInsetsMake(0.0f,

                         0.0f,

                         bottomInset,

                         0.0f);

    }];

}


- (void) handleKeyboardWillHide:(NSNotification *)paramNotification{

    if (UIEdgeInsetsEqualToEdgeInsets (self.textViewCloudDocumentText.contentInset,

                                       UIEdgeInsetsZero)){

        /* Our text view's content inset is intact so no need to

         reset it */

        return;

    }

    NSDictionary *userInfo = [paramNotification userInfo];

    NSValue *animationDurationObject =

    [userInfo valueForKey:UIKeyboardAnimationDurationUserInfoKey];

    double animationDuration = 0.0;

    [animationDurationObject getValue:&animationDuration];

    [UIView animateWithDuration:animationDuration animations:^{

        self.textViewCloudDocumentText.contentInset = UIEdgeInsetsZero;

    }];

}


@end


打印:

2014-08-12 17:38:33.435 ggcloud[1244:907] Metadata query finished.

2014-08-12 17:38:33.450 ggcloud[1244:907] The Documents folder already exists in iCloud.

2014-08-12 17:38:33.502 ggcloud[1244:907] The Documents folder already exists in iCloud.

2014-08-12 17:38:33.505 ggcloud[1244:907] Document already exists in iCloud. Loading it from there...

2014-08-12 17:38:33.550 ggcloud[1244:907] Successfully loaded the document from iCloud.


22.6. Managing the State of Documents in iCloud

删除同步时产生的冲突或其他问题

UIDocumentStateChangedNotification:当iCloud文档状态发生改变时会广播这个消息。可以监听这个消息并分析其documentState属性

typedef NS_OPTIONS(NSUInteger, UIDocumentState) {

    UIDocumentStateNormal          = 0,

    UIDocumentStateClosed          = 1 << 0, // The document has either not been successfully opened, or has been since closed. Document properties may not be valid.

    UIDocumentStateInConflict      = 1 << 1, // Conflicts exist for the document's fileURL. They can be accessed through +[NSFileVersion otherVersionsOfItemAtURL:].

    UIDocumentStateSavingError     = 1 << 2, // An error has occurred that prevents the document from saving.

    UIDocumentStateEditingDisabled = 1 << 3  // Set before calling -disableEditing. The document is is busy and it is not currently safe to allow user edits. -enableEditing will be called when it becomes safe to edit again.

};

UIDocumentStateNormal:一切正常,没有冲突

UIDocumentStateClosed:文档没有被正常打开,或打开后又关闭了。这种状态下应不允许用户编辑文档。建议不要用弹窗的方式提醒用户,而是在图形上显示不能编辑的标志。

UIDocumentStateInConflict:冲突。比如有多人同时编辑同一个文档。这时可以在编程上帮助解决冲突,或是让用户选择保留哪个版本。

UIDocumentStateSavingError:保存失败。当然你仍然可以不提醒用户,而只是先把它保存在本地,后续在进行提交

UIDocumentStateEditingDisabled:由于某种错误,文档不能再被编辑了。这时你也不该让用户再继续编辑文档了。



在上一节中修改和增加

- (void)viewDidLoad

{

    [super viewDidLoad];

//    [self listenForKeyboardNotifications];

//    self.view.backgroundColor = [UIColor brownColor];

//    [self setupTextView];

//    [self startSearchingForDocumentIniCloud];

    

    [self listenForDocumentStateChangesNotification];

    [self listenForKeyboardNotifications];

    self.view.backgroundColor = [UIColor brownColor];

    [self setupTextView];

    [self startSearchingForDocumentIniCloud];

}



- (void) listenForDocumentStateChangesNotification{

    /* Start listening for the Document State Changes notification */

    [[NSNotificationCenter defaultCenter]

     addObserver:self

     selector:@selector(handleDocumentStateChanged:)

     name:UIDocumentStateChangedNotification

     object:nil];

}


- (void) handleDocumentStateChanged:(NSNotification *)paramNotification{

    NSLog(@"Document state has changed");

    NSLog(@"Notification Object = %@", [paramNotification object]);

    NSLog(@"Notification Object Class = %@",

          NSStringFromClass([[paramNotification object] class]));

    CloudDocument *senderDocument = (CloudDocument *)paramNotification.object;

    NSLog(@"Document State = %d", senderDocument.documentState);

    /* Since we don't yet know how to solve conflicts, we will disallow

     the user from editing the document if an error of any sort has happened.

     Later when we will learn about handling conflicts, we will handle

     these issues more gracefully */

    if (senderDocument.documentState & UIDocumentStateInConflict){

        NSLog(@"Conflict found in the document.");

        self.textViewCloudDocumentText.editable = NO;

    }

    if (senderDocument.documentState & UIDocumentStateClosed){

        NSLog(@"Document is closed.");

        self.textViewCloudDocumentText.editable = NO;

    }

    if (senderDocument.documentState & UIDocumentStateEditingDisabled){

        NSLog(@"Editing is disabled on this document.");

        self.textViewCloudDocumentText.editable = NO;

    }

    if (senderDocument.documentState & UIDocumentStateNormal){

        NSLog(@"Things are normal. We are good to go.");

        self.textViewCloudDocumentText.editable = YES;

    }

    if (senderDocument.documentState & UIDocumentStateSavingError){

        NSLog(@"A saving error has happened.");

        self.textViewCloudDocumentText.editable = NO;

    }

}


打印:


2014-08-13 10:40:08.270 ggcloud[1386:907] Metadata query finished.

2014-08-13 10:40:08.281 ggcloud[1386:907] The Documents folder already exists in iCloud.

2014-08-13 10:40:08.321 ggcloud[1386:907] The Documents folder already exists in iCloud.

2014-08-13 10:40:08.324 ggcloud[1386:907] Document already exists in iCloud. Loading it from there...

2014-08-13 10:40:08.474 ggcloud[1386:907] Document state has changed

2014-08-13 10:40:08.475 ggcloud[1386:907] Notification Object = <CloudDocument: 0x2081bbe0> fileURL: file://localhost/private/var/mobile/Library/Mobile%20Documents/<# Put your team ID here #>~com~gaozgao~ggcloud/Documents/UserDocument.txt documentState: [Normal]

2014-08-13 10:40:08.477 ggcloud[1386:907] Notification Object Class = CloudDocument

2014-08-13 10:40:08.478 ggcloud[1386:907] Document State = 0

2014-08-13 10:40:08.480 ggcloud[1386:907] Successfully loaded the document from iCloud.


编译文本后:

Aug 13 10:41:19 enheng ggcloud[1386] <Error>: CGContextSaveGState: invalid context 0x0

Aug 13 10:41:19 enheng ggcloud[1386] <Error>: CGContextDrawLinearGradient: invalid context 0x0

Aug 13 10:41:19 enheng ggcloud[1386] <Error>: CGContextSetFillColorWithColor: invalid context 0x0

Aug 13 10:41:19 enheng ggcloud[1386] <Error>: CGContextFillRects: invalid context 0x0

Aug 13 10:41:19 enheng ggcloud[1386] <Error>: CGContextFillRects: invalid context 0x0

Aug 13 10:41:19 enheng ggcloud[1386] <Error>: CGContextSetFillColorWithColor: invalid context 0x0

Aug 13 10:41:19 enheng ggcloud[1386] <Error>: CGContextFillRects: invalid context 0x0

Aug 13 10:41:19 enheng ggcloud[1386] <Error>: CGContextSaveGState: invalid context 0x0

Aug 13 10:41:19 enheng ggcloud[1386] <Error>: CGContextDrawLinearGradient: invalid context 0x0

Aug 13 10:41:19 enheng ggcloud[1386] <Error>: CGContextSetFillColorWithColor: invalid context 0x0

Aug 13 10:41:19 enheng ggcloud[1386] <Error>: CGContextFillRects: invalid context 0x0

Aug 13 10:41:19 enheng ggcloud[1386] <Error>: CGContextFillRects: invalid context 0x0

Aug 13 10:41:19 enheng ggcloud[1386] <Error>: CGContextSetFillColorWithColor: invalid context 0x0

Aug 13 10:41:19 enheng ggcloud[1386] <Error>: CGContextFillRects: invalid context 0x0



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值