replaceItemAtURL fails without error on iOS but works fine on OSX
Asked Answered
B

2

17

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
Bunion answered 4/2, 2011 at 15:19 Comment(11)
Have added the code above Jonathan - any input appreciated.Bunion
I'm running into the same problem. I've tried placing creating file pointed to by newItemURL in the directories corresponding to both NSTemporaryDirectory() and NSCachesDirectory, and it still fails without any error. Did you have any luck?Bowles
Unfortunately not. I ended up just working around it by implementing it myself, which really sucks as a solution.Bunion
Can resultingItemURL really be nil? It doesn't say so in the docs.Satanic
@Satanic - thanks for the suggestion, you're right the docs don't say that it's OK for resultingItemURL to be nil, so I amended the code to include non-nil values for both it and backupItemName 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.Bunion
Did you figure this out? I'm hitting this too.Bundy
Unfortunately not - I'm still running with my custom logic and not using replaceItemAtURL at all ...Bunion
Your sample code is actually broken and does not work as you describe. I ran that code (copied/pasted into Xcode) on an iPhone running iOS 4.3.3; tmpDir is nil after the call to URLForDirectory:etc:, and as a result u2 is also nil and the call to replaceItemAtURL:etc: crashes the app. What are you actually doing? This code is not it.Lithographer
I was excited to find this question, but sad to see that no one has any kind of answer. I'm running into the exact same thing: the method returns NO, but doesn't set the error to tell me why it doesn't work. Argh!Heyde
I've run this code on iOS 6 and it works (returns YES). So this might be a bug that's been addressed since iOS 4.Stoneman
What's wrong with rename()?Augustina
R
1

I have experienced issues with all the NSFileManager methods using an URL on iOS. However, all the methods using Path work. So I think you should use removeItemAtPath:error:and copyItemAtPath:toURL:error: for that purpose.

Hope it helps

Rhebarhee answered 7/9, 2011 at 14:59 Comment(0)
S
-1

In mac file system is not case sensitive, but in IOS it. Even though you cant have two files with same name but with different case at one location, the path is case sensitive. So if file is has .JPEG and in your code you are passing link with .jpeg it will fail. It may not be the case with you but just what to share

Although strangely it should give you error.

Sodomite answered 20/2, 2013 at 8:19 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.