Cocoa Application Framework with Packages - cocoa

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

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.

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!!!

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.

Related

How does URL's resourceValues(forKeys: [.typeIdentifierKey]) determine the UTI?

I've got a URL, and I'm calling resourceValues(forKeys: [.typeIdentifierKey]) on it, to find out the UTIs it conforms to. Works great.
Does this method simply check whether it's a directory (like with stat(2), to report "public.folder"), and if it's not a directory, the filename extension (and if none, then just "public.data", "public.item")?
That seems to be the case, but I can't find any documentation that says for sure how it determines the type.
Rationale: I've got a bunch of URLs whose UTIs I want to check against a known value, and I know they're not folders, so I could save a ton of I/O calls if I were sure that that's all that this method is doing.
UPDATE: I looked in swift-corelibs-foundation/URL.swift, but it just passes through to the NSURL method of the same name. The swift-corelibs-foundation/NSURL.swift method just throws NSUnimplemented(). I tried looking in CFLite, which has an implementation of CFURL, but I can't seem to find anything there related to UTIs. AFAICT, there's no open-source implementation of this method from Apple.

Organising your classes in a cocoa app

This question could apply to all languages and frameworks but I'm looking for something a bit more 'cocoa specific'. I come from a Java background and I've noticed that learning objective-c is a lot more than just syntax, it's almost a completely different way of thinking.
What I've been having the most trouble with, must be the way one has to organise your classes. Sure all basic OOP(Object Oriented Programming) rules apply, and using MVC patterns where you can is recommended. But with me being used to Java I just need to set a few things straight and make sure I've got the right idea:
So for the sake of simplicity let just focus on one part of an app - Logging a user in. You'd have your .xib file for the UI (called Login.xib), you'd have your class that handles your data (connecting to a web service, called LoginModel.m) and you'd have your controller that acts as the middle man between your front-end and data (Called LoginController.m).
Is this is a pretty good example of applying MVC to a Cocoa app? And if it is, does that mean that you'd have 6 files created for this (since you have header files and implementation files). 6 files just to handle something simple as logging a user in. You can imagine how many you'd end up with for an entire app, even the most simplest of ones...
So my question is - Am I doing something wrong? Do I have the wrong idea? Or is the idea of too many files and too long method names just something I need to get used to since my brain is still working in 'Java Mode'?
Your ideas on how to handle that outlined above are fully correct. There is nothing bad about having a lot of files in the project. It does help a lot when you want to reuse code or if for example login details change and you don't want to edit multiple code locations.
Nevertheless, you may combine in such simple cases model class and controller class, especially if your model data can for example be stored in an NSDictionary and such. Only if you have complex model objects, that will run a lot of their own code, it will be better to separate them out.
Variable and method names can't be too long ;) always use a good name that especially describes the functionality or task. You usually don't have to type them often, but Xcode autocomplete will deal with that easily.

Which is the most efficient way to access the value of a control?

Of the two choices I have to access the value of a control which is the most efficient?
getComponent("ControlName").getValue();
or
dataSource.getItemValue("FieldName");
I find that on occasion the getComponent does not seem to return the current value, but accessing the dataSource seems to be more reliable. So does it make much difference from a performance perspective which one is used?
The dataSource.getValue seems to work everywhere that I have tried it. However, when working with rowData I still seem to need to do a rowData.getColumnValue("Something"). rowData.getValue("Something") fails.
Neither. The fastest syntax is dataSource.getValue ("FieldName"). The getItemValue method is only reliable on the document data source, whereas the getValue method is not only also available on view entries accessed via a view data source (although in that context you would pass it the programmatic name of a view column, which is not necessarily the same name as a field), but will also be available on any custom data sources that you develop or install (e.g. third-party extension libraries). Furthermore, it does automatic type conversion that you'd have to do yourself if you used getItemValue instead.
Even on very simple pages, dataSource.getValue ("FieldName") is 5 times as fast as getComponent ("id").getValue (), because, as Fredrik mentions, first it has to find the component, and then ask it what the value is... which, behind the scenes, just asks the data source anyway. So it will always be faster to just ask the data source yourself.
NOTE: the corresponding write method is dataSource.setValue ("FieldName", "NewValue"), not dataSource.replaceItemValue ("FieldName", "NewValue"). Both will work, but setValue also does the same type conversion that getValue does, so you can pass it data that doesn't strictly conform to the old Domino Java API and it usually just figures out what the value needs to be converted to in order to be "safe" for Domino to store.
I would say that the most efficient way is to get the value directly from the datasource.
Because if you use getComponent("ControlName").getValue(); you will do a get on the component first and then a getValue from that. So do a single get from the datasource is more efficient if you ask me.

How to save a QStandardItemModel?

I'm currently writing an application that plays podcasts. I'm representing all the feeds and the episodes within them as QStandardItem objects within a QStandardItemModel. Right now, I don't have a way to save this model--when the application closes, the feed model goes up in smoke. I looked at using QSettings, but that only works for datatypes that fall under QVariant.
Looking at this post gave me some hope, but I think I'm doing something wrong. I've got the following code in the constructor for my application.
//Expand QVatiant to use QStandardItemModel
qRegisterMetaType<QStandardItemModel>("QStandardItemModel");
That, however, gives me this error at compile time.
/ [...] QtSDK/Desktop/Qt/4.8.1/gcc/lib/QtGui.framework/Versions/4/Headers/qstandarditemmodel.h:424: error: 'QStandardItemModel::QStandardItemModel(const QStandardItemModel&)' is private
Ah. That reminds me of this caveat from the Qt documentation for QMetaType, here.
Any class or struct that has a public default constructor, a public copy constructor and a public destructor can be registered.
So, where do I go from here? Qt is behaving exactly as it should, so this approach won't work. I'm thinking of saving off the model as an xml file, but that seems like a ton of effort. This seems like a pretty common problem--I just don't know where to look for the answer.
Here's the best solution I could come up with: Create a method that saves the model into an XML document, and call it whenever I change the model (e.g. add or remove a podcast). I don't have the actual source code on hand, but since there's no real easy way to save the data structure wholesale, this is the best solution.

How to modify behaviour of NSManagedObject class in reusable way which can be accessed in Bindings?

Xcode auto-generates the class implementations for your NSMO entities - great.
But we often need to customize them. If you ever forget that you customized these files, Xcode will happily "delete" (overwrite) and remove all your code.
So ... a classic trick was to:
Create the NSManagedObject's in Apple's model view
Generate the classes
Create new classes which use Categories to extend the original classes, adding the modified behaviour
Import the custom-category-headers rather than the base NSMO headers, thereby getting the "new" behaviour
This works great: put custom code in the category, and when you auto-generate files using Xcode, you never lose anything.
But ... now I'm using Bindings / Mac OS code, and Bindings are great, but I have no idea how to make a Binding "import" the derived header (with the category, and the modified methods / custom behaviour)?
e.g. if I have an ArrayController (very common) that's holding NSMO instances, you normally tell it the "Entity Name" (e.g. "MyCoreDataEntity"), and it requests the NSMO with that class name. But that will never load the category, so it will never pick up the customized version of the class.
How do you get around this? Either: how do you load in the category-version of a class?
OR: how do you write custom code without using categories and AVOID Xcode deleting all your code when it feels like it?
I must admit since I use cocoa touch there is no binding available - so I really do not know if my suggestion is applicable in your case.
However, maybe this helps?
There is an alternative to categories for core data additions - not as "sophisticated" as categories - I know.
One may use #include statements:
There are two alternatives:
create a new ClassFile, delete the include "header.h", (delete the header.h), put the extra code there. It compiles but brings the two warnings: (which are understandable) [WARN]warning: no rule to process file '$(PROJECT_DIR)/Classes/../included_dataStuff' of type text for architecture armv6 [WARN]warning: no rule to process file '$(PROJECT_DIR)/Classes/../included_dataStuff' of type text for architecture armv7
create a new "empty" file and put the extra code there. This does not produce any warnings.
The difference between 1 and 2 is that while the code formatting remains in the first alternatve (having to accept the 2 warnings) in the second all the code format is lost and its treated like normal text (but there is no warning)
I guess I would prefer the first. Of course, the only modification to the generated code file would be the #include statement.
The easiest solution would be to subclass NSArrayController, import the category and then use the subclass in IB. That way your bindings should automatically know of the category.
MoGenerator used to be good at generating custom classes without having to overwrite anything. I'm tinkering with updating it or something like it for Xcode 4.x since the original authors don't seem to have the time to do so.

Resources