saveDocument:

  • Hello,

    please, could you clarify one thing for me? In document-based apps, if I
    were to implement my own document controller, how would I trigger the
    saving of a file? In other words, in menu there is an item which fires
    "saveDocument:" and in document there is a method "saveDocument:" - how
    does the action travels through document controller? In yet another words:

    (Menu) saveDocument: -> (Document controller) ? -> (Document) saveDocument:

    Do I have the picture right? Is it ok to write something like (in the
    controller):

    - (void)saveDocument:(id)sender {
        [[self currentDocument] saveDocument:sender];
    }

    I think I just need to pass "saveDocument:" from the menu to the document,
    am I right?

    Kind regards,

    Ecir Hana
  • On 8 May 2012, at 11:11 AM, ecir hana wrote:

    > please, could you clarify one thing for me? In document-based apps, if I
    > were to implement my own document controller, how would I trigger the
    > saving of a file? In other words, in menu there is an item which fires
    > "saveDocument:" and in document there is a method "saveDocument:" - how
    > does the action travels through document controller? In yet another words:
    >
    > (Menu) saveDocument: -> (Document controller) ? -> (Document) saveDocument:
    >
    > Do I have the picture right? Is it ok to write something like (in the
    > controller):
    >
    > - (void)saveDocument:(id)sender {
    > [[self currentDocument] saveDocument:sender];
    > }
    >
    > I think I just need to pass "saveDocument:" from the menu to the document,
    > am I right?

    I'll get this wrong if I answer in the amount of time I have. Look up "responder chain" in the Mac OS X documentation.

    To oversimplify:

    Menu commands typically go to the "first responder" — whatever has the UI focus. If the focused object doesn't implement the menu item's method, the event system shops the event up a logical hierarchy called the "responder chain" (e.g. text field -> window -> document -> application) until it finds an object that does implement the command. The first object that implements the command executes it.

    File > Save Document sends saveDocument: to the first responder. Because the chain goes from bottom up, the saveDocument: message arrives at the document object directly, without mediation from the application's document controller.

    — F
  • On Tue, May 8, 2012 at 6:33 PM, Fritz Anderson <fritza...>wrote:

    >
    > I'll get this wrong if I answer in the amount of time I have. Look up
    > "responder chain" in the Mac OS X documentation.
    >
    > To oversimplify:
    >
    > Menu commands typically go to the "first responder" — whatever has the UI
    > focus. If the focused object doesn't implement the menu item's method, the
    > event system shops the event up a logical hierarchy called the "responder
    > chain" (e.g. text field -> window -> document -> application) until it
    > finds an object that does implement the command. The first object that
    > implements the command executes it.
    >
    > File > Save Document sends saveDocument: to the first responder. Because
    > the chain goes from bottom up, the saveDocument: message arrives at the
    > document object directly, without mediation from the application's document
    > controller.

    Thanks.

    I apologize but I still don't quite understand. If I just
    implement saveDocument: at the document and leave document controller
    without it, the menu item is still disabled. When I add saveDocument: to
    the controller as well, it works. I mean, if I try to visualize it, if I
    just alloc:init: a document it just floats there in memory, how would a
    menu know about it? So I need to create it via the document controller, no?
    If this is the case, how to propagate the saveDocument: event though
    the document controller to the document?

    To put it differently, you seems to be saying that that the responder chain
    flows the other way than what I thought. Ok, but then how does it know
    about the document? When I were doing this in Xcode, I would just connect
    the menu item to the first responder and the document from IB would receive
    the saveDocument:, right? But there has to be a document controller, I
    imagine it is somehow "implied" or "implicit" as in not exposed to the
    user, which does the heavy lifting, do I understand this correctly? If so,
    what exactly does this document controller do?

    I'm sorry for such basic questions I'm very new to all this.
  • On 9 May 2012, at 4:45 AM, ecir hana wrote:

    > When I were doing this in Xcode, I would just connect
    > the menu item to the first responder and the document from IB would receive
    > the saveDocument:, right?

    "the document from IB" worries me. Your NIB should not include an instance of your document (NSDocument-subclass) object. The NSDocument object gets created by the global NSDocumentController (see later in this message). The NSDocument is represented in the NIB as "File's Owner." An in-NIB NSDocument won't get patched into the responder chain, and will disrupt all the connections you make in the NIB.

    > But there has to be a document controller, I
    > imagine it is somehow "implied" or "implicit" as in not exposed to the
    > user, which does the heavy lifting, do I understand this correctly? If so,
    > what exactly does this document controller do?

    You may be using the phrase "document controller" loosely. In a document-based application, there is one and only one instance of NSDocumentController (or a subclass). It handles the creation and destruction of NSDocument (subclass) objects. In the simplest case, an NSDocument is responsible for all the "controller" tasks (mediating between model and view), in the "MVC" sense, for a document and its views. But that's a different concept.

    NSDocument objects are part of the responder chain, which is how they get action messages. In the Documentation organizer, search the Lion documentation for "responder chain," and select the result labeled "The Responder Chain." That should tell you everything you need to know.

    — F

    --
    Fritz Anderson -- Xcode 4 Unleashed: Due 21 May 2012 -- <http://x4u.manoverboard.org/>
  • On May 9, 2012, at 02:45 , ecir hana wrote:

    > I apologize but I still don't quite understand. If I just
    > implement saveDocument: at the document and leave document controller
    > without it, the menu item is still disabled. When I add saveDocument: to
    > the controller as well, it works. I mean, if I try to visualize it, if I
    > just alloc:init: a document it just floats there in memory, how would a
    > menu know about it? So I need to create it via the document controller, no?
    > If this is the case, how to propagate the saveDocument: event though
    > the document controller to the document?

    It's hard to know what you're asking, because you're either using the wrong terminology, or you're trying to do something very strange.

    NSDocumentController is a frameworks object -- a singleton -- that provides shared behavior relating to all documents (such as maintaining the "Recent Documents" menu). Except perhaps in some extreme conditions, you wouldn't write your own. Very occasionally, it's necessary to subclass the NSDocumentController and force the singleton to your own subclass, but this is rare.

    NSDocument -- well, your subclass of it -- is a *controller* object (in the MVC sense) that represents your document file and the windows used to edit it.

    The NSDocumentController and your NSDocument-subclass instances are both in the responder chain. (There's a different responder chain running through each document object.) For information, see:

    https://developer.apple.com/library/mac/#documentation/Cocoa/Conceptual/Eve
    ntOverview/EventArchitecture/EventArchitecture.html#//apple_ref/doc/uid/100
    00060i-CH3-SW9


    and study the pictures under the heading "Responder Chain for Action Messages" -- especially figure 1-10 for document-based apps.

    Thus, for example, the 'saveDocument:' action will be normally be delivered to the NSDocument-subclass object directly, because it is in the responder chain that's active when the menu item is chosen. Note that NSDocument *has* a 'saveDocument:' method; NSDocumentController doesn't.

    The document architecture is described in great detail here:

    https://developer.apple.com/library/mac/#documentation/DataManagement/Conce
    ptual/DocBasedAppProgrammingGuideForOSX/Introduction/Introduction.html


    You're absolutely wasting your time if you're trying to write Cocoa apps without understanding the event architecture (first reference, above). You're absolutely wasting your time if you're trying to write a document-based app without understanding document architecture (second reference).
  • On 09/05/2012, at 7:45 PM, ecir hana wrote:

    > I'm sorry for such basic questions I'm very new to all this.

    We did warn you that taking yourself down this path wasn't going to be the best idea. Now you're seeing why.

    If you build a document-based app using Xcode's template, it will work correctly from the very first time you run it. Then you can see how it's put together, and all of these things will slowly become obvious.

    Analogy: if you take apart a clock, you might not understand at first how it works, but you have the correct pieces which, when put together properly, would make a working clock. By studying these pieces, you could work out how it works. What you are doing is starting with sheets of brass and miscellaneous screws and trying to make a working clock without having any knowledge of how a clock should work, or how it is typically made, beyond perhaps some idea about how gears work.

    At the very least if you insist on this approach, you should read all of the documentation thoroughly, so you understand the (many) concepts that lie behind Cocoa's architecture. The behaviour of an application is not down to just one object's behaviour, but the intricate interaction between many different objects. Stumbling across the correct connections to all of those objects more or less by chance is very improbable. But starting with an app already correctly working and connected will give you something to study.

    --Graham
  • On Thu, May 10, 2012 at 2:24 AM, Graham Cox <graham.cox...> wrote:

    >
    > If you build a document-based app using Xcode's template, it will work
    > correctly from the very first time you run it. Then you can see how it's
    > put together, and all of these things will slowly become obvious.
    >

    I'm doing precisely this. I run two projects in parallel, one by Xcode, one
    by hand and compare the findings. The problem I have right now is that the
    one from Xcode just works and I don't know why, it seems to do lots of
    things under the hood. For instance, MyDocument.m does not even have to
    have dataOfType:error: or readFromData:ofType:error: in order to show "Save
    as" enabled in the menu. On the other hand, yes, that's something I totally
    did not anticipate so I guess I learned something new.

    > At the very least if you insist on this approach, you should read all of
    > the documentation thoroughly, so you understand the (many) concepts that
    > lie behind Cocoa's architecture.
    >

    I'm reading, really.
  • On Wed, May 9, 2012 at 5:40 PM, Quincey Morris <
    <quinceymorris...> wrote:

    > Very occasionally, it's necessary to subclass the NSDocumentController and
    > force the singleton to your own subclass, but this is rare.
    >
    >
    This is what I try to do - subclass the
    NSDocumentController in applicationWillFinishLaunching:.

    Thus, for example, the 'saveDocument:' action will be normally be delivered
    > to the NSDocument-subclass object directly, because it is in the responder
    > chain that's active when the menu item is chosen. Note that NSDocument
    > *has* a 'saveDocument:' method; NSDocumentController doesn't.
    >

    Ok. My NSDocument has "saveDocument:", and my NSDocumentController doesn't
    have it. But the menu has "Save as" disabled.

    Thanks for the links, there is also:

    http://developer.apple.com/library/mac/documentation/Cocoa/Conceptual/MenuL
    ist/Articles/EnablingMenuItems.html
  • It works now! The problem was, that I was creating the window manager with
    "init:", instead of the initializers from the docs.

    Thank you all for the replies, they were very helpful!
  • On 10/05/2012, at 7:12 PM, ecir hana wrote:

    > This is what I try to do - subclass the
    > NSDocumentController in applicationWillFinishLaunching:.

    Subclassing NSDocumentController is extremely rare, and in almost every case, unnecessary. If you're doing this as a matter of course because you think you need to, you're almost certainly mistaken. I have subclassed this once, and that was because I needed to exclude certain files specifically from the Recent Items menu, and that was the sole reason. Forget about this class, it really just sits there and does its thing and you don't need to worry about it. Its main job is to create the relevant document instance when a file is opened from disk - after that the document itself is fairly autonomous. One of the main inputs to NSDocumentController is the app's property list (info.plist).

    > Ok. My NSDocument has "saveDocument:", and my NSDocumentController doesn't
    > have it. But the menu has "Save as" disabled.

    There is a lot of interaction between the document and the application's info.plist, which describes the file types that the app can read and write and how those files types map to document (sub)classes. There is also the "dirty" state of the document to consider which in turn is influenced strongly by the state of any associated undo manager.

    Again, stumbling across the correct precise set of connections by trying to build this bottom-up is unlikely to work.

    > For instance, MyDocument.m does not even have to have dataOfType:error: or readFromData:ofType:error: in order to show "Save as" enabled in the menu

    Indeed, that is quite true, and perfectly logical. Because the state of the menu has nothing to do with the actual data read or written by the document to disk, but by its internal state. After all, the user needs to choose a destination for a Save As before data can be written to that file, so the menu must be enabled and handled long before the document is asked to supply the file data to be written.

    Also, reading data from a file has very little influence on the Save As menu, except that it will clear the dirty state initially. How the document interprets a file's content is up to it, and it is not required to maintain that file data internally or keep the file open, for example. It is very common for a document to read a file into some totally different internal form, manage that while the document is open, then write it out to a file when required.

    --Graham
previous month may 2012 next month
MTWTFSS
  1 2 3 4 5 6
7 8 9 10 11 12 13
14 15 16 17 18 19 20
21 22 23 24 25 26 27
28 29 30 31      
Go to today