views:

226

answers:

3

Ok, I am creating a document-based application in Cocoa and the document's file type is actually a package. Within that package is an XML settings file, a SQLite database and a zip file which is downloaded at runtime. Now the only thing that changes, really, is the XML settings file as the other ones can be recreated at run-time.

Each one of these packages will have one and only one window, hence my desire to use document-based. These files can also be copied, renamed, moved, etc. just like any other file that is part of such an architecture.

But I am completely lost as how to implement this in the Documentation Framework! It seems everywhere I look in the docs it's always talking about in-memory representations of the files which you then write out using the path presented to you in one of the NSDocument overrides (since Cocoa may move it, etc.) But again, I'm using a SQLite database that sits on disk, not in memory.

I have looked all over for overridable methods that would still give me things like dirty-state checking of the doc, open and save file dialog support and the like, but I can't seem to find anything that just says 'Here's a file URL... Open it as you see fit' althought I did get close at the application's delegate level, at least for the opening.

So let's assume that's working as expected. How do I implement the save/save-as where I want to control everything that is written to disk or not? I don't want to (not can I) mess around with data structures or the like. I just want to be given a psth that the user selects in the 'Save As' dialog (for new) and be able to write what I need to there. Simple. But again, the 50+ page document from developer.apple.com about Document-based architecture tells me where to overload a lot of things, but every one seems to stem from some in-memory representation of the document, which again, is not what my package is. Technically, only the internal XML file is what would be tied to the document. Everything else is just support for it.

So? Anyone? Takers?

Mark

+1  A: 

I can't seem to find anything that just says 'Here's a file URL... Open it as you see fit'

Implement the readFromURL:ofType:error: method in your document class. Alternatively, since your document type is a package type, implement the readFromFileWrapper:ofType:error: method.

You don't have to read the data into memory; you can do whatever you want in whichever method you implement, including opening the database.

How do I implement the save/save-as where I want to control everything that is written to disk or not?

Implement the writeToURL:ofType:error: method or the fileWrapperOfType:error: method.

If you had or could easily create data in memory, you would implement the readFromData:ofType:error: and dataOfType:error: methods. The URL-based and file-wrapper-based methods are for cases where data in memory is not an option. And the primary use of file wrappers is for package types like yours.

Peter Hosey
A: 

That was of course the very first thing that I tried, but if you read the developer documentation--specifically the Cocoa Document-Based Architecture--here's what it says about those very methods...

During writing, your document may be asked to write its contents to a different location or using a different file type. Again, your overridden method should be able to determine everything it needs to do the writing from the passed-in parameters.

If your override cannot determine all of the information it needs from the passed-in parameters, consider overriding another method. For example, if you see the need to invoke fileURL from within an override of readFromData:ofType:error:, perhaps you should instead override readFromURL:ofType:error:. For another example, if you see the need to invoke fileURL from within an override of writeToURL:ofType:error:, perhaps you should instead override writeToURL:ofType:forSaveOperation:originalContentsURL:error:.

In other words, it seems to say that you can't assume the URL that is passed to you is the actual place on disk where the 'something' is eventually written to, which wreaks havoc when dealing with database files that are opened by URL. Maybe I'm missing something.

But ok... forget I read that and simply even try to just override those methods. I do and return TRUE for each, (I log the URL so I can see what is being passed in), I get this error on 'Save As' after you have chosen a filename...

2009-10-28 14:31:51.548 XPanel[1001:a0f] dataOfType:error: is a subclass responsibility but has not been overridden.

...but when you look at the documentation for that it says the default implementation throws an exception because you must override one of the other implementations above... which I obviously just did! Plus, again, this can't be represented as simple data!

So grasping at straws here, I overrode that one too and just returned nil, since again, you can't represent what I'm doing with a NSData object. Then I get a 'Can't be saved' message.

WTF?! Why is it calling that thing anyway??!!

...and that's when I gave up and posted this here.

Now if YOU can give me a simple example that perhaps doesn't even actually read or write a file but instead just logs the URL, that would be perfect. Not to useful but still, it should work... I just can't seem to implement get it to.

MarqueIV
“it seems to say that you can't assume the URL that is passed to you is the actual place on disk where the 'something' is eventually written to, …” No, it does not say that. That URL is where you should save the document (package, in your case).
Peter Hosey
“`dataOfType:error:` is a subclass responsibility but has not been overridden.” Then you didn't override either of the other methods, either. Or, perhaps you sent `[super writeToURL:…]` or `[super fileWrapperOfType:…]`? You're not supposed to invoke the superclass implementation from the reading and writing methods.
Peter Hosey
No, I didn't send anything to Super. That's what's confusing me. I did exactly as I said in my comments above. I even cut and pasted the signature straight from the documentation so I know I got the signature right.
MarqueIV
Also, again, I know that the passed-in URL is where it's expecting you to write the file, but that's not what I was saying. If you read the documentation closely, it says that might not be the *final* destination of the file. In other words, you may be asked to first write it to a temporary (i.e. why you can't use fileUrl directly) and the framework may then move it to its final destination after so it can do things like make backups of the original file, etc. In my case, you can't ever move it because part of the package is a SQLite database that you never actually write as a whole.
MarqueIV
A: 

Actually, I found it. It's not the 'writeTo' methods, but rather the 'saveTo' methods you want to override. When I did that, the saving code worked as I expected, including automatic save panel support. For clarity, this is the one I chose...

saveToURL:ofType:forSaveOperation:error:

and it works like a champ! Not too confusing now, was it! Sheesh!!!

MarqueIV