Call - [NSFileManager setUbiquitous: itemAtURL: destinationURL: error:] never returns

I have a direct Mac OS X-enabled application based on NSDocument in which I am trying to implement an iCloud document repository. I am building with 10.7 SDK.

I prepared my application for storing iCloud documents and included the necessary rights (AFAICT). The application builds, runs, and creates a local container. The document file is ubiquitous correctly (this took some time, but everything seems to work). I am using the NSFileCoordinator API as recommended by Apple. I am sure that I am using the correct UbiquityIdentifier as recommended by Apple (it is edited below tho).

I closely followed the Apple iCloud Document demo instructions in this WWDC 2011 video:

Session 107 Autosave and versions in the lion

My code looks almost identical to the code from this demo.

However, when I call my action to move the current document to the cloud, when I call the method -[NSFileManager setUbiquitous:itemAtURL:destinationURL:error:] I have problems with liveness . He never returns.

Here is the relevant code from my subclass of NSDocument . It is almost identical to the Apple WWDC demo code. Since this is an action, it is called in the main thread (as shown by the Apple demo code). Closing occurs to the end when the -setUbiquitous:itemAtURL:destinationURL:error: method is -setUbiquitous:itemAtURL:destinationURL:error: . I tried moving to the background thread, but it never returns anyway.

It seems that the semaphore is blocking, waiting for a signal that never arrives.

When I run this code in the debugger, my source and destination URLs look correct, so I'm sure they are calculated correctly, and I confirmed that the directories exist on the disk.

Am I doing something obviously wrong, which will cause -setUbiquitous not return?

 - (IBAction)moveToOrFromCloud:(id)sender { NSURL *fileURL = [self fileURL]; if (!fileURL) return; NSString *bundleID = [[[NSBundle mainBundle] infoDictionary] objectForKey:@"CFBundleIdentifier"]; NSString *appID = [NSString stringWithFormat:@"XXXXXXX.%@.macosx", bundleID]; BOOL makeUbiquitous = 1 == [sender tag]; NSURL *destURL = nil; NSFileManager *mgr = [NSFileManager defaultManager]; if (makeUbiquitous) { // get path to local ubiquity container Documents dir NSURL *dirURL = [[mgr URLForUbiquityContainerIdentifier:appID] URLByAppendingPathComponent:@"Documents"]; if (!dirURL) { NSLog(@"cannot find URLForUbiquityContainerIdentifier %@", appID); return; } // create it if necessary [mgr createDirectoryAtURL:dirURL withIntermediateDirectories:NO attributes:nil error:nil]; // ensure it exists BOOL exists, isDir; exists = [mgr fileExistsAtPath:[dirURL relativePath] isDirectory:&isDir]; if (!(exists && isDir)) { NSLog(@"can't create local icloud dir"); return; } // append this doc filename destURL = [dirURL URLByAppendingPathComponent:[fileURL lastPathComponent]]; } else { // get path to local Documents folder NSArray *dirs = [mgr URLsForDirectory:NSDocumentDirectory inDomains:NSUserDomainMask]; if (![dirs count]) return; // append this doc filename destURL = [[dirs objectAtIndex:0] URLByAppendingPathComponent:[fileURL lastPathComponent]]; } NSFileCoordinator *fc = [[[NSFileCoordinator alloc] initWithFilePresenter:self] autorelease]; [fc coordinateWritingItemAtURL:fileURL options:NSFileCoordinatorWritingForMoving writingItemAtURL:destURL options:NSFileCoordinatorWritingForReplacing error:nil byAccessor:^(NSURL *fileURL, NSURL *destURL) { NSError *err = nil; if ([mgr setUbiquitous:makeUbiquitous itemAtURL:fileURL destinationURL:destURL error:&err]) { [self setFileURL:destURL]; [self setFileModificationDate:nil]; [fc itemAtURL:fileURL didMoveToURL:destURL]; } else { NSWindow *win = ... // get my window [self presentError:err modalForWindow:win delegate:nil didPresentSelector:nil contextInfo:NULL]; } }]; } 
+4
source share
5 answers

I just shared this on Twitter with you, but I believe that when using NSDocument you don’t need to do any NSFileCoordinator stuff - just make the document ubiquitous and save.

+3
source

I don’t know if they are the source of your problems, but here are some things that I see:

  • -[NSFileManager URLForUbiquityContainerIdentifier:] may take some time, so you should not reference the main thread. see the Ubiquity Container Search section of this blog post

  • Running this in a global queue means that you should probably use a dedicated NSFileManager rather than +defaultManager .

  • The block passed to the byAccessor part of the coordinated record cannot be called into any particular stream, so you should not manipulate NSWindows or present modal dialogs or any of this block (unless you sent it back to the main queue).

  • I think that almost all iCloud methods on NSFileManager will block until completion. It is possible that what you see is a locking method and never returns because things are not configured properly. I would double and triple your settings, maybe try to simplify the playback case. If it still does not work, try to file an error or contact DTS.

+5
source

Hmm

You tried not to use the ubiquity container identifier in the code (sorry - torn from the project, so I pseudo-encoded a bit):

 NSFileManager *fm = [NSFileManager defaultManager]; NSURL *iCloudDocumentsURL = [[fm URLForUbiquityContainerIdentifier:nil] URLByAppendingPathComponent:@"Documents"]; NSURL *iCloudFileURL = [iCloudDocumentsURL URLByAppendingPathComponent:[doc.fileURL lastPathComponent]]; ok = [fm setUbiquitous:YES itemAtURL:doc.fileURL destinationURL:iCloudRecipeURL error:&err]; NSLog(@"doc moved to iCloud, result: %d (%@)",ok,doc.fileURL.fileURL); 

And then in your file, it's right:

 <key>com.apple.developer.ubiquity-container-identifiers</key> <array> <string>[devID].com.yourcompany.appname</string> </array> 

Other than that, your code looks almost identical to mine (which works - except that I am not using NSDocument, but it is pumping it all).

+1
source

If this is the first place in your code that you are accessing iCloud, look in Console.app for a message like this:

taskgated: killed yourAppID [pid 13532] because its use of the right com.apple.developer.ubiquity-container-identifiers is not allowed

Any time you see this message, delete the application container ~/Library/Containers/<yourAppID> There may also be other useful messages in Console.app to help you solve this problem. I found that removing the application container is the new Clean Project when working with iCloud.

+1
source

Ok, so I was finally able to solve the problem using the Dunk advice. I am pretty sure that the problem I had was the following:

  • Sometime after the WWDC video I used as a guide, Apple completed the ubiquity API and eliminated the need to use the NSFileCoordinator object when saving from an NSDocument subclass.

Thus, the key was to delete both the NSFileCoordinator creation and the call -[NSFileCoordinator coordinateWritingItemAtURL:options:writingItemAtURL:options:error:byAccessor:]

I also transferred this work to a background thread, although I am pretty sure that it was not absolutely necessary to fix the problem (although it was certainly a good idea).

Now I will submit my code to Google crawlers Google in the hope of helping future fearless Xcoders.

Here is my complete solution that works:

 - (IBAction)moveToOrFromCloud:(id)sender { NSURL *fileURL = [self fileURL]; if (!fileURL) { NSBeep(); return; } BOOL makeUbiquitous = 1 == [sender tag]; if (makeUbiquitous) { [self displayMoveToCloudDialog]; } else { [self displayMoveFromCloudDialog]; } dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ [self doMoveToOrFromCloud:makeUbiquitous]; }); } - (void)doMoveToOrFromCloud:(BOOL)makeUbiquitous { NSURL *fileURL = [self fileURL]; if (!fileURL) return; NSURL *destURL = nil; NSFileManager *mgr = [[[NSFileManager alloc] init] autorelease]; if (makeUbiquitous) { NSURL *dirURL = [[MyDocumentController instance] ubiquitousDocumentsDirURL]; if (!dirURL) return; destURL = [dirURL URLByAppendingPathComponent:[fileURL lastPathComponent]]; } else { // move to local Documentss folder NSArray *dirs = [mgr URLsForDirectory:NSDocumentDirectory inDomains:NSUserDomainMask]; if (![dirs count]) return; destURL = [[dirs firstObject] URLByAppendingPathComponent:[fileURL lastPathComponent]]; } NSError *err = nil; void (^completion)(void) = nil; if ([mgr setUbiquitous:makeUbiquitous itemAtURL:fileURL destinationURL:destURL error:&err]) { [self setFileURL:destURL]; [self setFileModificationDate:nil]; completion = ^{ [self hideMoveToFromCloudDialog]; }; } else { completion = ^{ [self hideMoveToFromCloudDialog]; NSWindow *win = [[self canvasWindowController] window]; [self presentError:err modalForWindow:win delegate:nil didPresentSelector:nil contextInfo:NULL]; }; } dispatch_async(dispatch_get_main_queue(), completion); } 
+1
source

Source: https://habr.com/ru/post/1380880/


All Articles