Sie sind auf Seite 1von 108

iCloud

Build your Data Castle in the Air


Tom Harrington @atomicbird

Whats Ahead
Concepts APIs Pitfalls

What is iCloud?

What is iCloud?
Sync service Transfers user data between devices Runs in background Per-app sandboxing (as usual)

What isnt iCloud?


Dropbox File browser iCloud is about Your Stuff

Infrastructure
When using iCloud you Dont need your own sync servers Dont need your own network protocol Dont need to pay for storage/bandwidth

Availability
iOS 5+ Free accounts get 5GB of storage This includes iCloud device backups

Conicts
iCloud helps with conicts But does not solve the problem

iCloud Concepts and Terms

Ubiquitous

Background Syncing
Ubiquity daemon, ubd You save changes, ubd sends them to the cloud ubd downloads changes from iCloud Which means....

Your data can change without warning

iCloud Containers
Location for your apps data in iCloud Every iCloud enabled app has at least one Containers correspond to a directory

File Coordinator
Data can be changed by your app or by ubd Need a reader/writer lock to coordinate access Use NSFileCoordinator when reading or writing

File Presenter
Companion to the le coordinator Adopt NSFilePresenter to be notied of changes When the le coordinator makes a change, it calls the
le presenter.

APIs
Key-value store Core Data Document

Tip
~/Library/Mobile Documents/

Enabling iCloud

Yo dawg I heard you like provisioning...

iCloud APIs

Is iCloud Available?

NSFileManager *fileManager = [NSFileManager defaultManager]; NSURL *cloudURL = [fileManager URLForUbiquityContainerIdentifier:nil];

File URL pointing to your iCloud container directory

Key-Value Store

NSUbiquitousKeyValueStore
API is very similar to NSUserDefaults Global dictionary with convenience accessors

(NSString *)stringForKey:(NSString *)aKey; (NSArray *)arrayForKey:(NSString *)aKey; (NSDictionary *)dictionaryForKey:(NSString *)aKey; (NSData *)dataForKey:(NSString *)aKey; (long long)longLongForKey:(NSString *)aKey; (double)doubleForKey:(NSString *)aKey; (BOOL)boolForKey:(NSString *)aKey; (void)setString:(NSString *)aString forKey:(NSString *)aKey; (void)setData:(NSData *)aData forKey:(NSString *)aKey; (void)setArray:(NSArray *)anArray forKey:(NSString *)aKey; (void)setDictionary:(NSDictionary *)aDictionary forKey:(NSString *)aKey; (void)setLongLong:(long long)value forKey:(NSString *)aKey; (void)setDouble:(double)value forKey:(NSString *)aKey; (void)setBool:(BOOL)value forKey:(NSString *)aKey;

Key Value Store API


No equivalent to -registerDefaults: The -synchronize method writes to disk only Can use synchronize to check iCloud availability
- (BOOL)synchronize;

Key Value Store


Limited to 64kB per app Recommended for app state Not recommended for user defaults

KV Store: Conicts
Last setting into iCloud wins. No conicts, so no conict resolution

NSUbiquitousKeyValueStoreDidChangeExternallyNotification

Core Data, meet iCloud

iCloud Core Data


Data store is still in your app sandbox New options when setting up the stack Adding iCloud to existing apps is surprisingly easy

Internals, briey
Data store les do not get synced Transaction logs written to the iCloud container Transactions are synced and replayed Only changed records are synced

Do not put SQLite les in iCloud

Demo Code: Recipes

- (void)dealloc { [managedObjectContext__ release]; [managedObjectContext__ release]; [managedObjectContext__ release]; [recipeListController release]; [tabBarController release]; [window release]; [super dealloc]; }

Persistent Store Setup


NSPersistentStoreUbiquitousContentNameKey

Arbitrary string, iCloud name for persistent store Sub-directory in the iCloud container Optional (but a good idea)

NSPersistentStoreUbiquitousContentURLKey

File coordination is automatic

- (NSPersistentStoreCoordinator *)persistentStoreCoordinator { if (persistentStoreCoordinator__ != nil) { return persistentStoreCoordinator__; } persistentStoreCoordinator__ = [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel: [self managedObjectModel]]; [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(mergeChangesFrom_iCloud:) name:NSPersistentStoreDidImportUbiquitousContentChangesNotification object:persistentStoreCoordinator__]; NSString *storePath = [[self applicationDocumentsDirectory] stringByAppendingPathComponent:@"Recipes.sqlite"]; dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ // More to come }); return persistentStoreCoordinator__; }

- (NSPersistentStoreCoordinator *)persistentStoreCoordinator { if (persistentStoreCoordinator__ != nil) { return persistentStoreCoordinator__; } persistentStoreCoordinator__ = [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel: [self managedObjectModel]]; [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(mergeChangesFrom_iCloud:) name:NSPersistentStoreDidImportUbiquitousContentChangesNotification object:persistentStoreCoordinator__]; NSString *storePath = [[self applicationDocumentsDirectory] stringByAppendingPathComponent:@"Recipes.sqlite"]; dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ // More to come }); return persistentStoreCoordinator__; }

- (NSPersistentStoreCoordinator *)persistentStoreCoordinator { if (persistentStoreCoordinator__ != nil) { return persistentStoreCoordinator__; } persistentStoreCoordinator__ = [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel: [self managedObjectModel]]; [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(mergeChangesFrom_iCloud:) name:NSPersistentStoreDidImportUbiquitousContentChangesNotification object:persistentStoreCoordinator__]; NSString *storePath = [[self applicationDocumentsDirectory] stringByAppendingPathComponent:@"Recipes.sqlite"]; dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ // More to come }); return persistentStoreCoordinator__; }

dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ NSURL *storeUrl = [NSURL fileURLWithPath:storePath]; NSURL *cloudURL = [[NSFileManager defaultManager] URLForUbiquityContainerIdentifier:nil]; NSString* coreDataCloudContent = [[cloudURL path] stringByAppendingPathComponent:@"recipes_v3"]; cloudURL = [NSURL fileURLWithPath:coreDataCloudContent]; NSDictionary* options = [NSDictionary dictionaryWithObjectsAndKeys: ! ! ! ! ! @"com.apple.coredata.examples.recipes.3", NSPersistentStoreUbiquitousContentNameKey, ! ! ! ! ! cloudURL, NSPersistentStoreUbiquitousContentURLKey, ! ! ! ! ! [NSNumber numberWithBool:YES], NSMigratePersistentStoresAutomaticallyOption, ! ! ! ! ! [NSNumber numberWithBool:YES], NSInferMappingModelAutomaticallyOption, ! ! ! ! ! nil]; NSError *error = nil; [persistentStoreCoordinator__ lock]; if (![persistentStoreCoordinator__ addPersistentStoreWithType:NSSQLiteStoreType configuration:nil URL:storeUrl options:options error:&error]) { // Handle error... } [persistentStoreCoordinator__ unlock]; dispatch_async(dispatch_get_main_queue(), ^{ NSLog(@"asynchronously added persistent store!"); [[NSNotificationCenter defaultCenter] postNotificationName:@"RefetchAllDatabaseData" object:self userInfo:nil]; }); });

! ! ! ! !

! ! ! ! !

! ! ! ! !

dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ NSURL *storeUrl = [NSURL fileURLWithPath:storePath]; NSURL *cloudURL = [[NSFileManager defaultManager] URLForUbiquityContainerIdentifier:nil]; NSString* coreDataCloudContent = [[cloudURL path] stringByAppendingPathComponent:@"recipes_v3"]; cloudURL = [NSURL fileURLWithPath:coreDataCloudContent]; NSDictionary* options = [NSDictionary dictionaryWithObjectsAndKeys: ! ! ! ! ! @"com.apple.coredata.examples.recipes.3", NSPersistentStoreUbiquitousContentNameKey, ! ! ! ! ! cloudURL, NSPersistentStoreUbiquitousContentURLKey, ! ! ! ! ! [NSNumber numberWithBool:YES], NSMigratePersistentStoresAutomaticallyOption, ! ! ! ! ! [NSNumber numberWithBool:YES], NSInferMappingModelAutomaticallyOption, ! ! ! ! ! nil]; NSError *error = nil; [persistentStoreCoordinator__ lock]; if (![persistentStoreCoordinator__ addPersistentStoreWithType:NSSQLiteStoreType configuration:nil URL:storeUrl options:options error:&error]) { // Handle error... } [persistentStoreCoordinator__ unlock]; dispatch_async(dispatch_get_main_queue(), ^{ NSLog(@"asynchronously added persistent store!"); [[NSNotificationCenter defaultCenter] postNotificationName:@"RefetchAllDatabaseData" object:self userInfo:nil]; }); });

! ! ! ! !

! ! ! ! !

! ! ! ! !

dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ NSURL *storeUrl = [NSURL fileURLWithPath:storePath]; NSURL *cloudURL = [[NSFileManager defaultManager] URLForUbiquityContainerIdentifier:nil]; NSString* coreDataCloudContent = [[cloudURL path] stringByAppendingPathComponent:@"recipes_v3"]; cloudURL = [NSURL fileURLWithPath:coreDataCloudContent]; NSDictionary* options = [NSDictionary dictionaryWithObjectsAndKeys: ! ! ! ! ! @"com.apple.coredata.examples.recipes.3", NSPersistentStoreUbiquitousContentNameKey, ! ! ! ! ! cloudURL, NSPersistentStoreUbiquitousContentURLKey, ! ! ! ! ! [NSNumber numberWithBool:YES], NSMigratePersistentStoresAutomaticallyOption, ! ! ! ! ! [NSNumber numberWithBool:YES], NSInferMappingModelAutomaticallyOption, ! ! ! ! ! nil]; NSError *error = nil; [persistentStoreCoordinator__ lock]; if (![persistentStoreCoordinator__ addPersistentStoreWithType:NSSQLiteStoreType configuration:nil URL:storeUrl options:options error:&error]) { // Handle error... } [persistentStoreCoordinator__ unlock]; dispatch_async(dispatch_get_main_queue(), ^{ NSLog(@"asynchronously added persistent store!"); [[NSNotificationCenter defaultCenter] postNotificationName:@"RefetchAllDatabaseData" object:self userInfo:nil]; }); });

! ! ! ! !

! ! ! ! !

! ! ! ! !

dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ NSURL *storeUrl = [NSURL fileURLWithPath:storePath]; NSURL *cloudURL = [[NSFileManager defaultManager] URLForUbiquityContainerIdentifier:nil]; NSString* coreDataCloudContent = [[cloudURL path] stringByAppendingPathComponent:@"recipes_v3"]; cloudURL = [NSURL fileURLWithPath:coreDataCloudContent]; NSDictionary* options = [NSDictionary dictionaryWithObjectsAndKeys: ! ! ! ! ! @"com.apple.coredata.examples.recipes.3", NSPersistentStoreUbiquitousContentNameKey, ! ! ! ! ! cloudURL, NSPersistentStoreUbiquitousContentURLKey, ! ! ! ! ! [NSNumber numberWithBool:YES], NSMigratePersistentStoresAutomaticallyOption, ! ! ! ! ! [NSNumber numberWithBool:YES], NSInferMappingModelAutomaticallyOption, ! ! ! ! ! nil]; NSError *error = nil; [persistentStoreCoordinator__ lock]; if (![persistentStoreCoordinator__ addPersistentStoreWithType:NSSQLiteStoreType configuration:nil URL:storeUrl options:options error:&error]) { // Handle error... } [persistentStoreCoordinator__ unlock]; dispatch_async(dispatch_get_main_queue(), ^{ NSLog(@"asynchronously added persistent store!"); [[NSNotificationCenter defaultCenter] postNotificationName:@"RefetchAllDatabaseData" object:self userInfo:nil]; }); });

! ! ! ! !

! ! ! ! !

! ! ! ! !

Getting Incoming Changes

When changes download...


ubd downloads transaction logs and replays them Notication posted when save is complete This notication will post more than once

- (void)mergeChangesFrom_iCloud:(NSNotification *)notification { NSManagedObjectContext* moc = [self managedObjectContext]; [moc mergeChangesFromContextDidSaveNotification:notification]; [[NSNotificationCenter defaultCenter] postNotificationName:@"RefreshAllViews" object:self userInfo:[note userInfo]]; }

Conict Handling

Johnny Doe john@apple.com

John Doe john@apple.com

Johnny Dough john@apple.com

Johnny Dough johnny@apple.com

John Doe johnny@apple.com

John Doe jd@apple.com

John Doe john@apple.com

John Doe ????@apple.com

John Doe johnny@apple.com

Core Data Migration


Syncing only happens if the data model is the same Different versions of an app may not sync Syncing resumes after upgrade Automatic lightweight migration works

Ubiquitous Documents

UIDocument

UIDocument
Asynchronous block-based reading and writing Auto-saving Flat le and le packages

UIDocument with iCloud


Acts as a le coordinator and presenter Detects conicts Notications API to help resolve

UIManagedDocument
Concrete subclass of UIDocument Stores document data in Core Data Each instance has its own Core Data stack

UIDocument Concepts

Outside the (sand)box


iCloud documents are not located in your app sandbox Located in your iCloud container Create the document in the sandbox Move it to iCloud

Finding documents
You cant just scan the iCloud container for documents Use NSMetadataQuery This is what powers Spotlight on Macs

Why search?
Documents might not exist locally (yet) Metadata may be all there is Outgoing data is pushed aggressively Incoming data is downloaded on demand

Document State
UIDocumentStateNormal UIDocumentStateClosed UIDocumentStateInConflict UIDocumentStateSavingError UIDocumentStateEditingDisabled

UIDocumentStateChangedNotification

Demo Code: CloudNotes

Subclassing UIDocument
@interface NoteDocument : UIDocument @property (strong, readwrite) NSString *documentText; @end

Subclassing UIDocument
- (BOOL)loadFromContents:(id)contents ofType:(NSString *)typeName error:(NSError *__autoreleasing *)outError; - (id)contentsForType:(NSString *)typeName error:(NSError *__autoreleasing *)outError;

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

- (id)contentsForType:(NSString *)typeName error:(NSError *__autoreleasing *)outError { if ([[self documentText] length] == 0) { [self setDocumentText:@"New note"]; } return [NSData dataWithBytes:[[self documentText] UTF8String] length:[[self documentText] length]]; }

Creating a NoteDocument

- (NSURL*)ubiquitousDocumentsDirectoryURL { NSURL *ubiquitousContainerURL = [[NSFileManager defaultManager] URLForUbiquityContainerIdentifier:nil]; NSURL *ubiquitousDocumentsURL = [ubiquitousContainerURL URLByAppendingPathComponent:@"Documents"]; if (ubiquitousDocumentsURL != nil) { if (![[NSFileManager defaultManager] fileExistsAtPath:[ubiquitousDocumentsURL path]]) { NSError *createDirectoryError = nil; BOOL created = [[NSFileManager defaultManager] createDirectoryAtURL:ubiquitousDocumentsURL withIntermediateDirectories:YES attributes:0 error:&createDirectoryError]; if (!created) { NSLog(@"Error creating directory at %@: %@", ubiquitousDocumentsURL, createDirectoryError); } } } else { NSLog(@"Error getting ubiquitous container URL"); } return ubiquitousDocumentsURL; }

- (NSURL*)ubiquitousDocumentsDirectoryURL { NSURL *ubiquitousContainerURL = [[NSFileManager defaultManager] URLForUbiquityContainerIdentifier:nil]; NSURL *ubiquitousDocumentsURL = [ubiquitousContainerURL URLByAppendingPathComponent:@"Documents"]; if (ubiquitousDocumentsURL != nil) { if (![[NSFileManager defaultManager] fileExistsAtPath:[ubiquitousDocumentsURL path]]) { NSError *createDirectoryError = nil; BOOL created = [[NSFileManager defaultManager] createDirectoryAtURL:ubiquitousDocumentsURL withIntermediateDirectories:YES attributes:0 error:&createDirectoryError]; if (!created) { NSLog(@"Error creating directory at %@: %@", ubiquitousDocumentsURL, createDirectoryError); } } } else { NSLog(@"Error getting ubiquitous container URL"); } return ubiquitousDocumentsURL; }

- (void)createFileNamed:(NSString *)filename { NSURL *localFileURL = [[self localDocumentsDirectoryURL] URLByAppendingPathComponent:filename]; NoteDocument *newDocument = [[NoteDocument alloc] initWithFileURL:localFileURL]; [newDocument saveToURL:localFileURL forSaveOperation:UIDocumentSaveForCreating completionHandler:^(BOOL success) { if (success) { dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ NSURL *destinationURL = [[self ubiquitousDocumentsDirectoryURL] URLByAppendingPathComponent:filename]; NSError *moveToCloudError = nil; BOOL success = [[NSFileManager defaultManager] setUbiquitous:YES itemAtURL:[newDocument fileURL] destinationURL:destinationURL error:&moveToCloudError]; if (!success) { NSLog(@"Error moving to iCloud: %@", moveToCloudError); } }); } }]; }

- (void)createFileNamed:(NSString *)filename { NSURL *localFileURL = [[self localDocumentsDirectoryURL] URLByAppendingPathComponent:filename]; NoteDocument *newDocument = [[NoteDocument alloc] initWithFileURL:localFileURL]; [newDocument saveToURL:localFileURL forSaveOperation:UIDocumentSaveForCreating completionHandler:^(BOOL success) { if (success) { dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ NSURL *destinationURL = [[self ubiquitousDocumentsDirectoryURL] URLByAppendingPathComponent:filename]; NSError *moveToCloudError = nil; BOOL success = [[NSFileManager defaultManager] setUbiquitous:YES itemAtURL:[newDocument fileURL] destinationURL:destinationURL error:&moveToCloudError]; if (!success) { NSLog(@"Error moving to iCloud: %@", moveToCloudError); } }); } }]; }

- (void)createFileNamed:(NSString *)filename { NSURL *localFileURL = [[self localDocumentsDirectoryURL] URLByAppendingPathComponent:filename]; NoteDocument *newDocument = [[NoteDocument alloc] initWithFileURL:localFileURL]; [newDocument saveToURL:localFileURL forSaveOperation:UIDocumentSaveForCreating completionHandler:^(BOOL success) { if (success) { dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ NSURL *destinationURL = [[self ubiquitousDocumentsDirectoryURL] URLByAppendingPathComponent:filename]; NSError *moveToCloudError = nil; BOOL success = [[NSFileManager defaultManager] setUbiquitous:YES itemAtURL:[newDocument fileURL] destinationURL:destinationURL error:&moveToCloudError]; if (!success) { NSLog(@"Error moving to iCloud: %@", moveToCloudError); } }); } }]; }

- (void)createFileNamed:(NSString *)filename { NSURL *localFileURL = [[self localDocumentsDirectoryURL] URLByAppendingPathComponent:filename]; NoteDocument *newDocument = [[NoteDocument alloc] initWithFileURL:localFileURL]; [newDocument saveToURL:localFileURL forSaveOperation:UIDocumentSaveForCreating completionHandler:^(BOOL success) { if (success) { dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ NSURL *destinationURL = [[self ubiquitousDocumentsDirectoryURL] URLByAppendingPathComponent:filename]; NSError *moveToCloudError = nil; BOOL success = [[NSFileManager defaultManager] setUbiquitous:YES itemAtURL:[newDocument fileURL] destinationURL:destinationURL error:&moveToCloudError]; if (!success) { NSLog(@"Error moving to iCloud: %@", moveToCloudError); } }); } }]; }

Opening and Closing


[[self currentDocument] openWithCompletionHandler:^(BOOL success) { [[self detailViewController] setDocument:document]; }];

if (!([[self currentDocument] documentState] & UIDocumentStateClosed)) { [[self currentDocument] closeWithCompletionHandler:^(BOOL success) { }]; }

Finding iCloud documents

NSMetadataQuery
Congure with an NSPredicate Give it a scope to search Posts notications when results are available

NSMetadataQuery
New search scopes Noties when results have been found Just let it keep running
NSMetadataQueryUbiquitousDocumentsScope NSMetadataQueryUbiquitousDataScope

NSMetadataQuery *query = [[NSMetadataQuery alloc] init]; [query setSearchScopes:[NSArray arrayWithObject:NSMetadataQueryUbiquitousDocumentsScope]]; [query setPredicate:[NSPredicate predicateWithFormat:@"%K ENDSWITH '.txt'", NSMetadataItemFSNameKey]]; [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(fileListReceived) name:NSMetadataQueryDidFinishGatheringNotification object:query]; [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(fileListReceived) name:NSMetadataQueryDidUpdateNotification object:query]; [query startQuery];

NSMetadataQuery *query = [[NSMetadataQuery alloc] init]; [query setSearchScopes:[NSArray arrayWithObject:NSMetadataQueryUbiquitousDocumentsScope]]; [query setPredicate:[NSPredicate predicateWithFormat:@"%K ENDSWITH '.txt'", NSMetadataItemFSNameKey]]; [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(fileListReceived) name:NSMetadataQueryDidFinishGatheringNotification object:query]; [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(fileListReceived) name:NSMetadataQueryDidUpdateNotification object:query]; [query startQuery];

NSMetadataQuery *query = [[NSMetadataQuery alloc] init]; [query setSearchScopes:[NSArray arrayWithObject:NSMetadataQueryUbiquitousDocumentsScope]]; [query setPredicate:[NSPredicate predicateWithFormat:@"%K ENDSWITH '.txt'", NSMetadataItemFSNameKey]]; [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(fileListReceived) name:NSMetadataQueryDidFinishGatheringNotification object:query]; [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(fileListReceived) name:NSMetadataQueryDidUpdateNotification object:query]; [query startQuery];

But is it available?
Look it up on the documents le URL
NSNumber *isIniCloud = nil; if ([fileURL getResourceValue:&isIniCloud forKey:NSURLUbiquitousItemIsDownloadedKey error:nil]) { }

NSURLIsUbiquitousItemKey NSURLUbiquitousItemIsDownloadedKey NSURLUbiquitousItemIsDownloadingKey NSURLUbiquitousItemPercentDownloadedKey NSURLUbiquitousItemIsUploadedKey NSURLUbiquitousItemIsUploadingKey NSURLUbiquitousItemPercentUploadedKey NSURLUbiquitousItemHasUnresolvedConflictsKey

Forcing Download
NSFileManager* fm = [NSFileManager defaultManager]; [fm startDownloadingUbiquitousItemAtURL:file error:nil];

Autosave
Mark the document as dirty Either directly or by registering undo actions Periodically it auto-saves its contents

{ ! ! ! }

(void)textViewDidChange:(UITextView *)textView [[self document] setDocumentText:[textView text]]; // Trigger auto-save [[self document] updateChangeCount:UIDocumentChangeDone];

Document Conicts
UIDocumentStateInConict Can retrieve conicted versions with NSFileVersion Must resolve conicts, because conict versions stick
around until you do

NSFileVersion
+ (NSArray *)unresolvedConflictVersionsOfFileAtURL:(NSURL *)fileURL; + (NSFileVersion *)currentVersionOfFileAtURL:(NSURL *)fileURL;

+ (NSFileVersion *)otherVersionsOfFileAtURL:(NSURL *)fileURL;

- (NSURL *)URL;

if (documentState & UIDocumentStateInConflict) { NSURL *documentURL = [[self document] fileURL]; NSArray *conflictVersions = [NSFileVersion unresolvedConflictVersionsOfItemAtURL:documentURL]; for (NSFileVersion *fileVersion in conflictVersions) { [fileVersion setResolved:YES]; } [NSFileVersion removeOtherVersionsOfItemAtURL:documentURL error:nil]; }

Document State Changes

Conict States
UIDocumentStateEditingDisabled UIDocumentStateEditingDisabled & UIDocumentStateInConflict UIDocumentStateEditingDisabled UIDocumentStateNormal

Non-conict updates
UIDocumentStateEditingDisabled UIDocumentStateNormal

No way to check user quota

Conict Resolution

Go make some happy little iClouds


iCloud Recipes
http://tinyurl.com/iCloudRecipes

CloudNotes
https://github.com/atomicbird/CloudNotes

Das könnte Ihnen auch gefallen