I'm implementing a manually-triggered migration process for a CoreData-based app, and after the migration completes successfully, I'm trying to move the migrated DB back over the top of the original one using replaceItemAtURL:withItemAtURL:backupItemName:options:resultingItemURL:error:
.
The problem is that on iOS, nothing I do will make this method return YES, however it also never puts anything into the error pointer to allow you to see what's going wrong.
I'd read things elsewhere (e.g. http://www.cocoabuilder.com/archive/cocoa/287790-nsdoc-magic-file-watcher-ruins-core-data-migration.html) indicating that not shutting down all the CoreData objects (e.g. NSMigrationManager, NSManagedObjectModel etc) before attempting the replace might be the cause, but that wasn't it. I even implemented a little two file create-and-swap thing that didn't involve CoreData DBs at all to verify that the CoreData stuff didn't have anything to do with it.
I then noticed in the official documentation that the newitemURL
is supposed to be in a directory deemed appropriate for temporary files. I assumed that that meant a directory returned by URLForDirectory:inDomain:appropriateForURL:create:error:
using NSItemReplacementDirectory
as the search path.
That didn't work either! I ended up falling back to implementing the replacement logic using separate operations, but this is non-atomic and unsafe and all that bad stuff.
Does anyone have a working snippet of code that runs on iOS that either return YES from a call to replaceItemAtURL
or actually puts error information into the error pointer?
Any help much appreciated.
EDIT - Test code included below. This runs in application:didFinishLaunchingWithOptions:
on the main thread.
NSFileManager *fm = [[NSFileManager alloc] init];
NSError *err = nil;
NSURL *docDir = [NSURL fileURLWithPath:[self applicationDocumentsDirectory]];
NSURL *tmpDir = [fm URLForDirectory:NSItemReplacementDirectory
inDomain:NSUserDomainMask
appropriateForURL:docDir
create:NO
error:&err];
NSURL *u1 = [docDir URLByAppendingPathComponent:@"f1"];
NSURL *u2 = [tmpDir URLByAppendingPathComponent:@"f2"];
NSURL *repl = nil;
[fm createFileAtPath:[u1 path]
contents:[[NSString stringWithString:@"Hello"]
dataUsingEncoding:NSUTF8StringEncoding]
attributes:nil];
[fm createFileAtPath:[u2 path]
contents:[[NSString stringWithString:@"World"]
dataUsingEncoding:NSUTF8StringEncoding]
attributes:nil];
BOOL test = [fm replaceItemAtURL:u1 withItemAtURL:u2 backupItemName:@"f1backup"
options:0 resultingItemURL:&repl error:&err];
// At this point GDB shows test to be NO but error is still nil
NSTemporaryDirectory()
andNSCachesDirectory
, and it still fails without any error. Did you have any luck? – BowlesresultingItemURL
to be nil, so I amended the code to include non-nil values for both it andbackupItemName
and still get the same result. The return value from the replace call is NO but err is still nil. Basically still seems broken! Thanks for the suggestion nonetheless. – BunionreplaceItemAtURL
at all ... – BuniontmpDir
is nil after the call toURLForDirectory:etc:
, and as a resultu2
is also nil and the call toreplaceItemAtURL:etc:
crashes the app. What are you actually doing? This code is not it. – Lithographerrename()
? – Augustina