Re: Strange NSFileManager file replacement issue
On Aug 19, 2011, at 12:43 AM, Ken Thomases wrote: On Aug 19, 2011, at 12:38 AM, Ken Thomases wrote: Although it is easy to interpret a temporary directory as provided by the OS being compatible with NSTemporaryDirectory(), I suspect it really means a directory returned by -URLForDirectory:inDomain:appropriateForURL:create:error: with NSItemReplacementDirectory passed for the directory and your ultimate destination URL passed for the url parameter. Hmm. Didn't follow the link to StackOverflow early enough. Post there claims it was tried and didn't help. Sorry to reply to myself twice, but the StackOverflow post passed NO for the shouldCreate parameter of -URLForDirectory:... It's worth trying with YES. The other thing to try is the suggestion from the -replaceItemAtURL: docs for newItemURL. Use a uniquely named directory placed in the same directory as the original item if the temporary directory is not available. Regards, Ken ___ Cocoa-dev mailing list (Cocoa-dev@lists.apple.com) Please do not post admin requests or moderator comments to the list. Contact the moderators at cocoa-dev-admins(at)lists.apple.com Help/Unsubscribe/Update your Subscription: http://lists.apple.com/mailman/options/cocoa-dev/archive%40mail-archive.com This email sent to arch...@mail-archive.com
Re: Strange NSFileManager file replacement issue
On Thu, Aug 18, 2011 at 10:14 PM, Quincey Morris quinceymor...@rivergatesoftware.com wrote: a. What version of iOS did this fail on? The 4.3 simulator (running on Snow Leopard, Xcode 4.0.2). b. Can you assert that the receiver of the 'replace…' method is not nil? (If it was nil, the method call would behave exactly as you describe.) Good thing to check, but since it's returning YES for the -fileExistsAtPath: calls, and works in some of the other cases, I doubt that the NSFileManager is nil. c. Can you show us the actual line of code that does the replacement? I'll post my code when I get in to the office. (I got about this far before I had to leave last night.) Sixten ___ Cocoa-dev mailing list (Cocoa-dev@lists.apple.com) Please do not post admin requests or moderator comments to the list. Contact the moderators at cocoa-dev-admins(at)lists.apple.com Help/Unsubscribe/Update your Subscription: http://lists.apple.com/mailman/options/cocoa-dev/archive%40mail-archive.com This email sent to arch...@mail-archive.com
Re: Strange NSFileManager file replacement issue
On Thu, Aug 18, 2011 at 10:38 PM, Ken Thomases k...@codeweavers.com wrote: My thinking is that -replaceItemAtURL:... is a wrapper around exchangedata() or FSExchangeObjects(). Those functions, and the general operation that they perform, require that the files to be exchanged be on the same file system. It would seem that, on iOS, the application's Document folder is on a different file system from the temporary directory. This is exactly the problem which -URLForDirectory:NSItemReplacementDirectory... is intended to solve. It's to give you a temporary directory that's appropriate for a subsequent -replaceItemAtURL:... call. If true, that certainly makes that method far less useful in the general case than I expected, and really seems restricted to the saving a new copy of an in-memory document and swapping it case. I really don't want to put the in-process download into the Documents tree. (Both because it's potentially visible to the user through iTunes, and because NSTemporaryDirectory() will be swept up occasionally.) I'll see what I can do to test this this morning. I guess the question then becomes: - Abandon the use of the atomic swap altogether, and roll my own copy+remove? - Or introduce an extra step, where I copy the temp file into NSItemReplacementDirectory, and then call replaceItemAtURL? - Or is there some better pattern altogether? I recommend that you file bugs against the documentation for not adequately explaining the requirements on the newItemURL parameter and against the implementation for failing to provide a valid NSError pointer on failure in this case. Definitely. I want to verify that I *can* make it work if the replacement item is already in the Documents tree / replacement directory, and then I'll be spending some time on the bug reporter. Sixten ___ Cocoa-dev mailing list (Cocoa-dev@lists.apple.com) Please do not post admin requests or moderator comments to the list. Contact the moderators at cocoa-dev-admins(at)lists.apple.com Help/Unsubscribe/Update your Subscription: http://lists.apple.com/mailman/options/cocoa-dev/archive%40mail-archive.com This email sent to arch...@mail-archive.com
Re: Strange NSFileManager file replacement issue
On Thu, Aug 18, 2011 at 10:14 PM, Quincey Morris quinceymor...@rivergatesoftware.com wrote: c. Can you show us the actual line of code that does the replacement? Here's the original code (plus the addition of an assert on the file manager). The property self.filePath has the path to the current version of the file that's already on disk (from a previous pass through this code). In my current test case, the paths look something like: self.filePath: /Users/sixten/Library/Application%20Support/iPhone%20Simulator/4.3.2/Applications/840B5926-8107-458E-87ED-ABF0F084BC12/Documents/Subdir/MyFile.pdf tempFilePath: /var/folders/KP/KPl-d+TMHGaIJ6QIjMYNQTI/-Tmp-/2212 // start original code NSFileManager* fileManager = [[NSFileManager alloc] init]; NSAssert(fileManager != nil, @File manager wasn't created.); NSString* directoryPath = [fileManager rd_documentsSubdirectory:self.document.volume]; BOOL isDirectory; NSAssert1([fileManager fileExistsAtPath:tempFilePath isDirectory:isDirectory] isDirectory == NO, @Bad temp file path %@, tempFilePath); NSAssert1([fileManager fileExistsAtPath:directoryPath isDirectory:isDirectory] isDirectory == YES, @Bad document directory %@, directoryPath); if( [desiredName length] == 0 ) { if( self.filePath ) { desiredName = [self.filePath lastPathComponent]; } else { desiredName = [tempFilePath lastPathComponent]; } } NSString* finalPath = [directoryPath stringByAppendingPathComponent:desiredName]; NSError* error = nil; if( [finalPath isEqualToString:self.filePath] || [self.filePath hasPrefix:directoryPath] ) { NSURL* existingFileURL = [NSURL fileURLWithPath:finalPath]; NSURL* newFileURL = [NSURL fileURLWithPath:tempFilePath]; NSString* backupItemName = [NSString stringWithFormat:@__%@__.bak, self.documentId]; NSURL* resultURL = nil; // *** 1 if( [fileManager replaceItemAtURL:existingFileURL withItemAtURL:newFileURL backupItemName:backupItemName options:0 resultingItemURL:resultURL error:error] ) { self.filePath = [resultURL path]; success = YES; } else { // *** ends up here, with no error == nil LOG_GENERAL(LOG_PRIORITY_HIGHEST, @Error attempting to replace »%@« with »%@«: %@\n%@, tempFilePath, finalPath, [error localizedDescription]); NSAssert(NO, @Couldn't move file to designated location); } // *** 2 } else { // ... move the temp file to finalPath, which works just fine, and update self.filePath } [fileManager release]; // end original code This morning, I replaced the code between the *** 1 and *** 2 with the following, which still failed in exactly the same way. It does get create a replacement directory and return it, my temp file moves into that directory without issue, but the swap still mysteriously fails. The value of swapURL looks like: file://localhost/Users/sixten/Library/Application%20Support/iPhone%20Simulator/4.3.2/Applications/840B5926-8107-458E-87ED-ABF0F084BC12/Documents/Subdir/(A%20Document%20Being%20Saved%20By%20MyApp)/MyFile.pdf // *** 1 NSURL* swapURL = [fileManager URLForDirectory:NSItemReplacementDirectory inDomain:NSUserDomainMask appropriateForURL:existingFileURL create:YES error:error]; if( swapURL ) { swapURL = [swapURL URLByAppendingPathComponent:desiredName]; if( [fileManager moveItemAtURL:newFileURL toURL:swapURL error:NULL] ) { if( [fileManager replaceItemAtURL:existingFileURL withItemAtURL:swapURL backupItemName:backupItemName options:0 resultingItemURL:resultURL error:error] ) { self.filePath = [resultURL path]; success = YES; } else { // *** still gets here, with no error set! LOG_GENERAL(LOG_PRIORITY_HIGHEST, @Error attempting to replace »%@« with »%@«: %@\n%@, tempFilePath, finalPath, [error localizedDescription]); NSAssert(NO, @Couldn't move file to designated location); } } } else { LOG_GENERAL(LOG_PRIORITY_HIGHEST, @Error attempting to replace »%@« with »%@«: %@\n%@, tempFilePath, finalPath, [error localizedDescription]); NSAssert(NO, @Couldn't find/create swap location); } // *** 2 ___ Cocoa-dev mailing list (Cocoa-dev@lists.apple.com) Please do not post admin requests or moderator comments to the list. Contact the moderators at cocoa-dev-admins(at)lists.apple.com Help/Unsubscribe/Update your Subscription: http://lists.apple.com/mailman/options/cocoa-dev/archive%40mail-archive.com This email sent to arch...@mail-archive.com
Re: Strange NSFileManager file replacement issue
On Aug 19, 2011, at 7:17 AM, Sixten Otto wrote: On Thu, Aug 18, 2011 at 10:38 PM, Ken Thomases k...@codeweavers.com wrote: Those functions, and the general operation that they perform, require that the files to be exchanged be on the same file system. If true, that certainly makes that method far less useful in the general case than I expected, and really seems restricted to the saving a new copy of an in-memory document and swapping it case. I really don't want to put the in-process download into the Documents tree. (Both because it's potentially visible to the user through iTunes, and because NSTemporaryDirectory() will be swept up occasionally.) Is there any reason why you can't put the downloaded file in your app's private cache directory (.../appdir/Library/Caches), i.e., what gets returned by NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES)? That should certainly be within the bigger app directory hierarchy, and thus a peer of the app's Documents directory. ___ Cocoa-dev mailing list (Cocoa-dev@lists.apple.com) Please do not post admin requests or moderator comments to the list. Contact the moderators at cocoa-dev-admins(at)lists.apple.com Help/Unsubscribe/Update your Subscription: http://lists.apple.com/mailman/options/cocoa-dev/archive%40mail-archive.com This email sent to arch...@mail-archive.com
Re: Strange NSFileManager file replacement issue
On Fri, Aug 19, 2011 at 1:14 PM, Steve Christensen puns...@mac.com wrote: Is there any reason why you can't put the downloaded file in your app's private cache directory (.../appdir/Library/Caches), i.e., what gets returned by NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES)? That should certainly be within the bigger app directory hierarchy, and thus a peer of the app's Documents directory. Can't? No. My preference for NSTemporaryDirectory() was mainly the notion that I had help from the OS in clearing out any old partial downloads that whatever weird circumstances might have orphaned. But I'm not sure that it'd matter. Even when I moved the file from there to the NSItemReplacementDirectory given by the file manager, -replaceItemAtURL:... was still behaving identically. (See my previous message for the details.) Sixten ___ Cocoa-dev mailing list (Cocoa-dev@lists.apple.com) Please do not post admin requests or moderator comments to the list. Contact the moderators at cocoa-dev-admins(at)lists.apple.com Help/Unsubscribe/Update your Subscription: http://lists.apple.com/mailman/options/cocoa-dev/archive%40mail-archive.com This email sent to arch...@mail-archive.com
Re: Strange NSFileManager file replacement issue
On Aug 19, 2011, at 10:29 AM, Sixten Otto wrote: This morning, I replaced the code between the *** 1 and *** 2 with the following, which still failed in exactly the same way. It does get create a replacement directory and return it, my temp file moves into that directory without issue, but the swap still mysteriously fails. I realize this is getting further and further from what you actually want, but you might try using the exchangedata() or FSExchangeObjects() functions directly. At least that way, you should receive a clearer error result. For the latter, you can get FSRefs from NSURLs via CFURLGetFSRef(), since NSURL and CFURLRef are toll-free bridged. The ability to exchange objects has to be supported by the file system. Maybe the file system on an iOS device simply doesn't support it. The errors you get back from the above functions ought to make that clear. Regards, Ken ___ Cocoa-dev mailing list (Cocoa-dev@lists.apple.com) Please do not post admin requests or moderator comments to the list. Contact the moderators at cocoa-dev-admins(at)lists.apple.com Help/Unsubscribe/Update your Subscription: http://lists.apple.com/mailman/options/cocoa-dev/archive%40mail-archive.com This email sent to arch...@mail-archive.com
Strange NSFileManager file replacement issue
I have an iOS app where I'm storing files in the app's Documents directory, and occasionally downloading new versions from the server to replace them. The actual code is split across a number of files and classes, but the end of the process goes like this: - The download of the new data to a temporary file in NSTemporaryDirectory() finishes successfully. - I calculate the path I want to copy it to. - I see that there's already a previous version of the file at that path. - I try to use -[NSFileManager replaceItemAtURL:withItemAtURL:backupItemName:options:resultingItemURL:error:] to swap in the new file. That method returns NO for failure, so I check the error... but it's nil. There's nothing there to tell me what the heck is failing. (In fact, when I wasn't initializing my error pointer to nil, it was left as a garbage pointer, and crashed my app.) My app put the original file there, so (presumably) that path is writable. The file manager does return true from -fileExistsAtPath: for both files. The only thing that Google can turn up is this StackOverflow question. Same exact symptoms. But, of course, no answers. http://stackoverflow.com/questions/4899618/replaceitematurl-fails-without-error-on-ios-but-works-fine-on-osx I can *remove* the file that I can't overwrite, and I can move the temp file into Documents in the case where there isn't already a version on disk. Obviously, neither of those cases uses replaceItemAtURL. Does anyone know what circumstances might cause this failure? Any recommendations on what things I should check next? Sixten ___ Cocoa-dev mailing list (Cocoa-dev@lists.apple.com) Please do not post admin requests or moderator comments to the list. Contact the moderators at cocoa-dev-admins(at)lists.apple.com Help/Unsubscribe/Update your Subscription: http://lists.apple.com/mailman/options/cocoa-dev/archive%40mail-archive.com This email sent to arch...@mail-archive.com
Re: Strange NSFileManager file replacement issue
On Aug 18, 2011, at 21:24 , Sixten Otto wrote: That method returns NO for failure, so I check the error... but it's nil. There's nothing there to tell me what the heck is failing. (In fact, when I wasn't initializing my error pointer to nil, it was left as a garbage pointer, and crashed my app.) My app put the original file there, so (presumably) that path is writable. The file manager does return true from -fileExistsAtPath: for both files. a. What version of iOS did this fail on? b. Can you assert that the receiver of the 'replace…' method is not nil? (If it was nil, the method call would behave exactly as you describe.) c. Can you show us the actual line of code that does the replacement? ___ Cocoa-dev mailing list (Cocoa-dev@lists.apple.com) Please do not post admin requests or moderator comments to the list. Contact the moderators at cocoa-dev-admins(at)lists.apple.com Help/Unsubscribe/Update your Subscription: http://lists.apple.com/mailman/options/cocoa-dev/archive%40mail-archive.com This email sent to arch...@mail-archive.com
Re: Strange NSFileManager file replacement issue
On Aug 18, 2011, at 11:24 PM, Sixten Otto wrote: - The download of the new data to a temporary file in NSTemporaryDirectory() finishes successfully. - I calculate the path I want to copy it to. - I see that there's already a previous version of the file at that path. - I try to use -[NSFileManager replaceItemAtURL:withItemAtURL:backupItemName:options:resultingItemURL:error:] to swap in the new file. That method returns NO for failure, so I check the error... but it's nil. This is something of a guess: The documentation for -replaceItemAtURL:withItemAtURL:backupItemName:options:resultingItemURL:error: is a bit vague in its description of the newItemURL parameter: The item which will replace the originalItemURL. This item should be placed in a temporary directory as provided by the OS, or in a uniquely named directory placed in the same directory as the original item if the temporary directory is not available. Although it is easy to interpret a temporary directory as provided by the OS being compatible with NSTemporaryDirectory(), I suspect it really means a directory returned by -URLForDirectory:inDomain:appropriateForURL:create:error: with NSItemReplacementDirectory passed for the directory and your ultimate destination URL passed for the url parameter. My thinking is that -replaceItemAtURL:... is a wrapper around exchangedata() or FSExchangeObjects(). Those functions, and the general operation that they perform, require that the files to be exchanged be on the same file system. It would seem that, on iOS, the application's Document folder is on a different file system from the temporary directory. This is exactly the problem which -URLForDirectory:NSItemReplacementDirectory... is intended to solve. It's to give you a temporary directory that's appropriate for a subsequent -replaceItemAtURL:... call. For what it's worth, I can reproduce this on Mac OS X (Snow Leopard 10.6.8), when the two files to be exchanged are on different file systems (a.k.a. volumes). I recommend that you file bugs against the documentation for not adequately explaining the requirements on the newItemURL parameter and against the implementation for failing to provide a valid NSError pointer on failure in this case. Regards, Ken ___ Cocoa-dev mailing list (Cocoa-dev@lists.apple.com) Please do not post admin requests or moderator comments to the list. Contact the moderators at cocoa-dev-admins(at)lists.apple.com Help/Unsubscribe/Update your Subscription: http://lists.apple.com/mailman/options/cocoa-dev/archive%40mail-archive.com This email sent to arch...@mail-archive.com
Re: Strange NSFileManager file replacement issue
On Aug 19, 2011, at 12:38 AM, Ken Thomases wrote: Although it is easy to interpret a temporary directory as provided by the OS being compatible with NSTemporaryDirectory(), I suspect it really means a directory returned by -URLForDirectory:inDomain:appropriateForURL:create:error: with NSItemReplacementDirectory passed for the directory and your ultimate destination URL passed for the url parameter. Hmm. Didn't follow the link to StackOverflow early enough. Post there claims it was tried and didn't help. Sorry, Ken ___ Cocoa-dev mailing list (Cocoa-dev@lists.apple.com) Please do not post admin requests or moderator comments to the list. Contact the moderators at cocoa-dev-admins(at)lists.apple.com Help/Unsubscribe/Update your Subscription: http://lists.apple.com/mailman/options/cocoa-dev/archive%40mail-archive.com This email sent to arch...@mail-archive.com