Re: Saving only required files in a file wrapper?
Many thanks again for your replies; much appreciated. On the surface, it look as though Graham's suggested -writeToURL:ofType:forSaveOperation:originalContentsURL:error: would be the ideal solution, allowing me to write just individual files for an NSSaveOperation or copy the whole project for an NSSaveAsOperation. The trouble is that the documentation is really unclear on this. It seems to say that you should use this method for exactly what I want but then it says that you can't rely on the URLs passed in! Firstly, the docs say the following about this method: You can override this method instead of one of the three simple writing methods... if your document writing machinery needs access to the on-disk representation of the document revision that is about to be overwritten. Which is exactly what I want - I want access to the on-disk package where I know all of the contents are safe so that I can only save a handful of changed files within the package, without having to overwrite the entire package (which could take a long time for large packages). However, it then goes on to say: The value of absoluteURL is often not the same as [self fileURL]. Other times it is not the same as the URL for the final save destination. Likewise, absoluteOriginalContentsURL is often not the same value as [self fileURL]. Huh? It adds that If absoluteOriginalContentsURL is nil, either the document has never been saved or the user deleted the document file since it was opened. That bit makes sense. It is the above sentence that is worrying. I can see why absoluteURL wouldn't be the same as [self fileURL] if the save operation was NSSaveAsOperation (though the docs don't go into this). But outside of the document never having been saved or it having been deleted so that absoluteOriginalContentsURL is nil, why wouldn't absoluteOriginalContentsURL be the same as [self fileURL]? On the other hand, if absoluteOriginalContentsURL represents a copy the OS has made of the package, then everything should be there and I should still be able to write to it and have the OS write everything back safely. Right? I hope. That is, I would expect this method - given that it says that you can use this method if your document writing machinery needs access to the on-disk representation of the document revision that is about to be overwritten - to at least provide a copy of the entire package at absoluteOriginalContentsURL even if that is not the original -fileURL. (To be really safe I could always check inside absoluteOriginalContentsURL to ensure that a file I know should be in every package is there...) My experience certainly suggests that -fileURL will generally be the same as the passed-in URL. In my -readFromURL:... method, I have just realised that I'm calling -fileURL instead of absoluteURL, which I absolutely shouldn't be doing. But in two years not one of the thousands of users of my app has complained that their project wouldn't open. In the case of my app, though, the package is guaranteed to be on disk before -readFromURL: is ever called (you can't create a blank project without it already having a package on disk), so it seems that -absoluteURL in -readFromURL: only differs from [self fileURL] if the document is a blank, unsaved one. (Obviously I can't rely on this behaviour and will be changing it to use -absoluteURL forthwith.) Sorry for the long, rambling reply. I'm just trying to get my head around the best way of doing this, and the (usually excellent) docs seem a little obtuse in this area. Many thanks again, Keith - Original Message From: Ken Thomases [EMAIL PROTECTED] To: Graham Cox [EMAIL PROTECTED] Cc: Keith Blount [EMAIL PROTECTED]; Cocoa-Dev List cocoa-dev@lists.apple.com Sent: Thursday, May 8, 2008 3:36:05 AM Subject: Re: Saving only required files in a file wrapper? On May 7, 2008, at 9:06 PM, Graham Cox wrote: On 8 May 2008, at 10:26 am, Keith Blount wrote: The trouble with all of these methods is that they tell you not to rely on fileURL My interpretation of that advice is that at the time the read... and write... methods are called, the document hasn't set up -fileURL, so in that sense you can't rely on it (i.e. don't call -fileURL from within these methods). But the URL passed to the methods themselves as a parameter is definitely reliable. So provided you use the parameter you'll be fine. I think it means more than that. NSDocument tries to be smart about atomic writes and backups. The URL passed into the write... methods is not expected to be the same as the document's current location on disk. You are to write the document, in whole, to the temporary location provided to you (about which no assumptions should be made), and then NSDocument will take care of swapping the newly-written document with the old document and deleting the old document. Obviously, this runs directly counter to Keith's desires
Re: Saving only required files in a file wrapper?
Apologies for so soon a follow up, but I've just been experimenting with -writeToURL:ofType:forSaveOperation:originalContentsURL:error: and the results are disastrous. It turns out that *every single time* it is invoked, absoluteURL is a temporary location and *not* the original URL. So to use this method successfully I would need to copy the whole file package across to that location every time, which is exactly what I _don't_ want to do. This seems a little insane to me. There must be a mechanism in place that just lets you save into a package folder without having to write the whole thing out every time... Thanks again and all the best, Keith - Original Message From: Ken Thomases [EMAIL PROTECTED] To: Graham Cox [EMAIL PROTECTED] Cc: Keith Blount [EMAIL PROTECTED]; Cocoa-Dev List cocoa-dev@lists.apple.com Sent: Thursday, May 8, 2008 3:36:05 AM Subject: Re: Saving only required files in a file wrapper? On May 7, 2008, at 9:06 PM, Graham Cox wrote: On 8 May 2008, at 10:26 am, Keith Blount wrote: The trouble with all of these methods is that they tell you not to rely on fileURL My interpretation of that advice is that at the time the read... and write... methods are called, the document hasn't set up -fileURL, so in that sense you can't rely on it (i.e. don't call -fileURL from within these methods). But the URL passed to the methods themselves as a parameter is definitely reliable. So provided you use the parameter you'll be fine. I think it means more than that. NSDocument tries to be smart about atomic writes and backups. The URL passed into the write... methods is not expected to be the same as the document's current location on disk. You are to write the document, in whole, to the temporary location provided to you (about which no assumptions should be made), and then NSDocument will take care of swapping the newly-written document with the old document and deleting the old document. Obviously, this runs directly counter to Keith's desires. Unfortunately, I don't know how to override these smarts in NSDocument, other than perhaps what I described earlier with NSFileWrapper. -Ken Be a better friend, newshound, and know-it-all with Yahoo! Mobile. Try it now. http://mobile.yahoo.com/;_ylt=Ahu06i62sR8HDtDypao8Wcj9tAcJ ___ 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 [EMAIL PROTECTED]
Re: Saving only required files in a file wrapper?
On May 8, 2008, at 7:30 AM, Keith Blount wrote: Apologies for so soon a follow up, but I've just been experimenting with -writeToURL:ofType:forSaveOperation:originalContentsURL:error: and the results are disastrous. It turns out that *every single time* it is invoked, absoluteURL is a temporary location and *not* the original URL. So to use this method successfully I would need to copy the whole file package across to that location every time, which is exactly what I _don't_ want to do. This seems a little insane to me. There must be a mechanism in place that just lets you save into a package folder without having to write the whole thing out every time... Thanks again and all the best, Keith I went through some of this recently - I'm using a helper tool to actually save the files, and I had to figure some of this out to get that working right. absoluteURL is going to always be a temp location because that's how the document system works - it writes to the temp location, then overwrites the original location if that's successful. Keeps file corruption to a minimum. absoluteOriginalContentsURL should probably be the original URL. The only time it's not likely to be the original path (what's returned by [self fileURL]) is if the user has moved / renamed the original file in between when it was opened and when it's being saved. (I'm fairly certain this is why you're told not to use -fileURL when writing - because that's not updated if the user moves / renames the file while it's being edited.) If you don't want to rely on that, there's nothing stopping you from creating a new ivar to save the URL passed in when you read the file. Though that can break if the user moves / renames the file while it's being edited. I know, not likely, but you can't be 100% certain on that. If you'd like to do this, but want to be certain, you could get the original path when you read and convert it to a file alias, then resolve the alias when you're writing - then you're guaranteed to have the correct path (this is what I actually decided to do). The only way that would break is if the user moves the file to a different hard drive, and I believe the normal NSDocument methods wouldn't work correctly in that case anyway (I believe the user gets shown a dialog saying the original file couldn't be found). Once you have the original path (whichever way you choose to get it), you can do what you were before to save the files in the wrapper. Or, if you want to, you can save it to the temp location, and then overwrite the individual files yourself using NSFileManager. Though if you do that, *make sure to delete the temp file* before returning (if you use the same path as absoluteURL for the temp file, anyway). If you don't, the document system will copy it over anyway and you'll wind up with your original problem. I had an interesting time figuring that last part out. In my case, the original files aren't actually writable by anyone but root, and it would fail when it tried to copy the temp files over the original files (even though my helper tool had actually already done that). Made for an interesting case where the file was actually saved but the app didn't think so. Hope that helps out some. -- Darkshadow (aka Michael Nickerson) http://www.nightproductions.net ___ 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 [EMAIL PROTECTED]
Re: Saving only required files in a file wrapper?
Hi, Many thanks for your reply, much appreciated - it all helps. I've spent the last couple of hours digging deeper into this, and I think what I'm going to try to do next is override at a slightly lower level, by overriding -writeSafelyToURL:ofType:forSaveOperation:error:. I found the part of the documentation that shows a diagram of the sequence of save events, and it turns out that this is the method that sets up the temporary folders and URLs that get passed into -writeToURL:... If I override and do my writing inside the package there, I think it should work. The docs say that you should always call super inside that method, but I found an Apple sample project, QTKitCreateMovie, that doesn't, and which shows this method being overridden to do something similar to what I want to do. Certainly that is the method that creates the behaviour that I want to change, so I am hopeful... Thanks again and all the best, Keith - Original Message From: Michael Nickerson [EMAIL PROTECTED] To: Keith Blount [EMAIL PROTECTED] Cc: Cocoa-Dev List cocoa-dev@lists.apple.com Sent: Thursday, May 8, 2008 5:45:10 PM Subject: Re: Saving only required files in a file wrapper? On May 8, 2008, at 7:30 AM, Keith Blount wrote: Apologies for so soon a follow up, but I've just been experimenting with -writeToURL:ofType:forSaveOperation:originalContentsURL:error: and the results are disastrous. It turns out that *every single time* it is invoked, absoluteURL is a temporary location and *not* the original URL. So to use this method successfully I would need to copy the whole file package across to that location every time, which is exactly what I _don't_ want to do. This seems a little insane to me. There must be a mechanism in place that just lets you save into a package folder without having to write the whole thing out every time... Thanks again and all the best, Keith I went through some of this recently - I'm using a helper tool to actually save the files, and I had to figure some of this out to get that working right. absoluteURL is going to always be a temp location because that's how the document system works - it writes to the temp location, then overwrites the original location if that's successful. Keeps file corruption to a minimum. absoluteOriginalContentsURL should probably be the original URL. The only time it's not likely to be the original path (what's returned by [self fileURL]) is if the user has moved / renamed the original file in between when it was opened and when it's being saved. (I'm fairly certain this is why you're told not to use -fileURL when writing - because that's not updated if the user moves / renames the file while it's being edited.) If you don't want to rely on that, there's nothing stopping you from creating a new ivar to save the URL passed in when you read the file. Though that can break if the user moves / renames the file while it's being edited. I know, not likely, but you can't be 100% certain on that. If you'd like to do this, but want to be certain, you could get the original path when you read and convert it to a file alias, then resolve the alias when you're writing - then you're guaranteed to have the correct path (this is what I actually decided to do). The only way that would break is if the user moves the file to a different hard drive, and I believe the normal NSDocument methods wouldn't work correctly in that case anyway (I believe the user gets shown a dialog saying the original file couldn't be found). Once you have the original path (whichever way you choose to get it), you can do what you were before to save the files in the wrapper. Or, if you want to, you can save it to the temp location, and then overwrite the individual files yourself using NSFileManager. Though if you do that, *make sure to delete the temp file* before returning (if you use the same path as absoluteURL for the temp file, anyway). If you don't, the document system will copy it over anyway and you'll wind up with your original problem. I had an interesting time figuring that last part out. In my case, the original files aren't actually writable by anyone but root, and it would fail when it tried to copy the temp files over the original files (even though my helper tool had actually already done that). Made for an interesting case where the file was actually saved but the app didn't think so. Hope that helps out some. -- Darkshadow (aka Michael Nickerson) http://www.nightproductions.net Be a better friend, newshound, and know-it-all with Yahoo! Mobile. Try it now. http://mobile.yahoo.com/;_ylt=Ahu06i62sR8HDtDypao8Wcj9tAcJ ___ Cocoa-dev mailing list (Cocoa-dev@lists.apple.com) Please do
Re: Saving only required files in a file wrapper?
Hi Ken, Many thanks for your response... Hmm, I think you're right. I overrided -saveDocument: because it looked as though -writeToURL: and all of the other -write... methods expected you to write all the data at any one time. It's possible for my file packages to run to hundreds of megabytes, so I only ever want to write the changed files at any save point (usually only ever one or two RTFD files within the package actually require saving at any one time). There's not much in the docs about doing this - everything I can find seems to assume that you will save everything, even when it comes to packages (as file wrappers), at once, which is why I took a screwy way. I still wonder what the best way of doing it is. -writeToURL:ofType:forSaveOperation:originalContentsURL:error: perhaps? Also, is it possible that -writeToURL: is getting called behind the back of -saveDocument:? I considered this but assumed (always a bad mistake) that it would be safe as I thought that all of the -write... methods did nothing unless you overrode them. Many thanks again. All the best, Keith P.S. I've updated the thread title to better reflect the problem. - Original Message From: Ken Thomases [EMAIL PROTECTED] To: Keith Blount [EMAIL PROTECTED] Cc: cocoa-dev@lists.apple.com Sent: Wednesday, May 7, 2008 4:03:23 PM Subject: Re: Deep sleep and file wrappers? I don't have experience with NSDocument-based applications, so take the following with a grain of salt... On May 6, 2008, at 1:27 PM, Keith Blount wrote: I have an application that saves its information as a file package. My NSDocument subclass overrides -saveDocument: to save individual files within the project folder (because there can be hundreds of files and I wouldn't want to save the whole file wrapper every time a single file is changed). From the docs, this doesn't seem like the right way to customize saving. Overriding -saveDocument: may catch some cases of save attempts, but I suspect there are other times when other methods are invoked directly. This document describes what you need to override to customize how the document is saved: http://developer.apple.com/documentation/Cocoa/Conceptual/Documents/Tasks/SubclassNSDocument.html I never got to the bottom of the issue, and now another user has reported the same thing. To recap: whilst my app was open, the user's computer went into deep sleep (this was the case with the first two users; the third user cannot remember the exact circumstances but says his computer has had deep sleep problems recently). When it came out of deep sleep, everything in the project's file wrapper - that is, everything in the folder-with- extension on disk - had been wiped. All that was in there were the few files that were auto-saved _after_ deep sleep. I can think of two possibilities: 1) some other method was invoked to save the document, but since you didn't override it, it produced an empty document. The framework then replaced the existing document on disk with the new empty document. 2) The framework sometimes juggles files to perform an atomic document save operation. The writing methods of NSDocument are told to write to a different, temporary location, then the original and the newly saved document are swapped, then the backup is removed (unless - keepBackupFile is overridden to return YES). If this happens in your case, the new document will not be complete because of the way you're only saving part of the document. Good luck finding and fixing the problem, Ken Be a better friend, newshound, and know-it-all with Yahoo! Mobile. Try it now. http://mobile.yahoo.com/;_ylt=Ahu06i62sR8HDtDypao8Wcj9tAcJ ___ 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 [EMAIL PROTECTED]
Re: Saving only required files in a file wrapper?
Hi, On May 7, 2008, at 3:08 PM, Keith Blount wrote: Many thanks for your response... Sure thing. I'm happy to flail in the dark and guess wildly with the best of them. ;) Hmm, I think you're right. I overrided -saveDocument: because it looked as though -writeToURL: and all of the other -write... methods expected you to write all the data at any one time. It's possible for my file packages to run to hundreds of megabytes, so I only ever want to write the changed files at any save point (usually only ever one or two RTFD files within the package actually require saving at any one time). There's not much in the docs about doing this - everything I can find seems to assume that you will save everything, even when it comes to packages (as file wrappers), at once, which is why I took a screwy way. Yeah, it's hard to see how one can accomplish what you're trying to do. I wonder... what happens if you compose a directory NSFileWrapper from an existing directory (which will recursively fill it with file wrappers referencing the directory's contents), then remove the wrappers for the individual items you want to replace, then add new file wrappers for those items, then save that whole file wrapper to the desired destination location? Are there any smarts in there to avoid doing a full copy of all of the items which weren't changed, perhaps using hard links or the like? I still wonder what the best way of doing it is. - writeToURL:ofType:forSaveOperation:originalContentsURL:error: perhaps? Well, that seems to give you the greatest flexibility, and gives you both the new and old URLs, but -- by the very fact that you're given two URLs -- the explicit fact is that you are creating a new document on disk rather than doing a minimal modification of the old one. So, you'd have to perform the copy of all of the parts that are the same between the two, which is what you're trying to avoid. Also, is it possible that -writeToURL: is getting called behind the back of -saveDocument:? I considered this but assumed (always a bad mistake) that it would be safe as I thought that all of the - write... methods did nothing unless you overrode them. I don't know. That's certainly one of the possibilities I considered. I think you have to assume it's possible. For instance, I would not expect the auto-save features of NSDocument to go through - saveDocument:. -saveDocument: is an action method, intended to be triggered by the Save menu item (and similar GUI controls). So, I would expect that it would have all sorts of high-level functionality like running a save panel if the document hasn't been saved before. Because of that, the framework wouldn't use it when it needs to do something low-level. Cheers, 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 [EMAIL PROTECTED]
Re: Saving only required files in a file wrapper?
On 8 May 2008, at 10:26 am, Keith Blount wrote: The trouble with all of these methods is that they tell you not to rely on fileURL My interpretation of that advice is that at the time the read... and write... methods are called, the document hasn't set up -fileURL, so in that sense you can't rely on it (i.e. don't call -fileURL from within these methods). But the URL passed to the methods themselves as a parameter is definitely reliable. So provided you use the parameter you'll be fine. If you call other parts of your code that need the URL from inside these methods, you also need to pass them the URL directly, don't let them fetch it using -fileURL. A potential issue is if your users use Save As.. to save to a completely new file. In that case there is: writeToURL:ofType:forSaveOperation:originalContentsURL:error: so you have the original file URL as well so you can copy across other parts of the package to the new file. Again, there's no need to call - fileURL at that time, which may or may not refer to the original file. hth, G. ___ 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 [EMAIL PROTECTED]
Re: Saving only required files in a file wrapper?
On May 7, 2008, at 9:06 PM, Graham Cox wrote: On 8 May 2008, at 10:26 am, Keith Blount wrote: The trouble with all of these methods is that they tell you not to rely on fileURL My interpretation of that advice is that at the time the read... and write... methods are called, the document hasn't set up -fileURL, so in that sense you can't rely on it (i.e. don't call -fileURL from within these methods). But the URL passed to the methods themselves as a parameter is definitely reliable. So provided you use the parameter you'll be fine. I think it means more than that. NSDocument tries to be smart about atomic writes and backups. The URL passed into the write... methods is not expected to be the same as the document's current location on disk. You are to write the document, in whole, to the temporary location provided to you (about which no assumptions should be made), and then NSDocument will take care of swapping the newly-written document with the old document and deleting the old document. Obviously, this runs directly counter to Keith's desires. Unfortunately, I don't know how to override these smarts in NSDocument, other than perhaps what I described earlier with NSFileWrapper. -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 [EMAIL PROTECTED]