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
glenc
  • 3,132
  • 2
  • 26
  • 42
  • Have added the code above Jonathan - any input appreciated. – glenc Feb 05 '11 at 21:21
  • 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? – Tony Apr 08 '11 at 14:40
  • Unfortunately not. I ended up just working around it by implementing it myself, which really sucks as a solution. – glenc Apr 08 '11 at 19:12
  • Can resultingItemURL really be nil? It doesn't say so in the docs. – spstanley Apr 09 '11 at 17:04
  • @spstanley - 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. – glenc Apr 10 '11 at 20:50
  • Did you figure this out? I'm hitting this too. – Steven Fisher May 11 '11 at 20:33
  • Unfortunately not - I'm still running with my custom logic and not using `replaceItemAtURL` at all ... – glenc May 12 '11 at 15:46
  • 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. – Tom Harrington Jun 23 '11 at 19:30
  • 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! – Sixten Otto Aug 19 '11 at 04:03
  • 1
    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. – Drew Jan 25 '13 at 06:58
  • What's wrong with `rename()`? – tc. Mar 09 '13 at 02:54

2 Answers2

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

Fran Sevillano
  • 8,103
  • 4
  • 31
  • 45
-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.

Atif Khan
  • 125
  • 1
  • 7