4 Replies
      Latest reply on Jul 21, 2019 6:30 PM by orion98mc
      orion98mc Level 1 Level 1 (0 points)

        Hi everyone,

         

        I'm building a Wiki editor/viewer document based app for one of my projects.

        The Document file type is a bundle which contains this hierarchy:

         

        Example.wikidoc:

             - stylesheet.css

             - metadata.json

             - wiki/

                  [From here all the wiki articles I.e files and folders containing files]

         

        When creating an "untitled" document, in the init() of my NSDocument subclass (Document) I create a temporary "wiki/" directory somewhere on the file system (The App is Sandboxed). I store the "wiki/" directory URL (var wikiRoot: URL) to be used in my view controller to interact with the wiki engine.

         

        From there, the user starts editing/adding pages, they are saved in the wikiRoot directory.

        So far so good... the user saves the document:

         

        1) in my override of NSDocument.write(to url: URL, ofType: String)

        I create a document file wrapper (documentFileWrapper) and add the regular contents for the stylesheet and the metadata and a directory filewrapper for the wiki directory (no content specified)

         

        2) I then call:  try documentFileWrapper.write(to: url, options: [], originalContentsURL: nil)

        It saves the contents, with everything. The files and the wiki directory with everything in it. I can check this by showing the contents of the document bundle in the Finder.

         

        BUT I should now be changing the wikiRoot URL to reflect the relocated saved document bundle.

         

        Problem is the url provided in override func write(to url: URL, ofType typeName: String) throws Is not the real saved URL, maybe an intermediary URL while creating the bundle. For example on my system:

         

             /var/folders/c9/fhk113dd19j0vh_kv6fpfsvw0000gn/T/com.montecarlo-computing.WikiEditor/TemporaryItems/(A Document Being Saved By WikiEditor 3)/test.wikidoc

         

        And of course the document.fileURL is nil...

        How am I supposed to get the real new "wiki/" URL of the saved document bundle?

         

        Any advice would be really really appreciated!

         

        Thanks and regards,

        Thierry

        • Re: Request advice on NSDocument subclass
          QuinceyMorris Level 8 Level 8 (6,010 points)

          >>How am I supposed to get the real new "wiki/" URL of the saved document bundle?

           

          You shouldn't.

           

          If you did, when the user makes subsequent editing changes, you would be changing the saved document directly. That would be the wrong thing to do. A document's disk representation shouldn't change until the user explicitly saves the document.

           

          You should continue to create and use a temp directory for "wikiRoot" when creating a new document. When opening an existing document, you should also copy its wikiRoot subdirectory into a temp wikiRoot directory.

           

          With that conceptual behavior in place, you could then look for optimizations to avoid copying (say) individual files inside the document's wikiRoot until you have to.

           

          Note that it is perfectly legal, at save time, for your document's top-level directory wrapper to have sub-wrappers pointing to files or directories in both the document and your temp directory. NSDocument is smart enough to avoid re-copying individual files that aren't changed at save time (unchanged since the last save, that is).

           

          That means you can use the directory wrapper you get when opening an existing document to (effectively) track which individual files have changed. You can keep the wrapper up-to-date always (by replacing file sub-wrappers for edited files with wrappers to your temp files), or simply delete file sub-wrappers for edited files (and insert new wrappers for edited files at save time). Either strategy works well.

          • Re: Request advice on NSDocument subclass
            orion98mc Level 1 Level 1 (0 points)

            Thank you QuinceyMorris, you made my day!

             

            You are right, conceptually it was a dead end. I took your advice and made the required changes, so far it seems to work as expected.

            I also overrode var isDocumentEdited: Bool to prevent users quitting the app without saving their "wiki" work.

             

            I must admit that working with FileWrappers was not that straight forward to me. May be the documentation was not as clear as it could be.

             

            Best regards,

            Thierry

              • Re: Request advice on NSDocument subclass
                john daniel Level 3 Level 3 (390 points)

                Quincy’s advice is good, but maybe you should take it to the next level. What value are you really getting from FileWrappers anyway? My suggestion would be to use a temporary directory for editing. Then, when you want to save, just zip the entire temporary directory. You can zip to an NSData and use the NSData persistence routines in NSDocument.

                 

                If you wanted to be even more clever, you could run the whole thing from RAM with no temporary directories at all.

                  • Re: Request advice on NSDocument subclass
                    orion98mc Level 1 Level 1 (0 points)

                    Hi John,

                     

                    Thanks for your clever inputs.

                     

                    However, the app, let's call it WikiEditor is just a tool to build a Wiki in a document bundle that is then integrated to an iOS app to serve as a Help Content. I have made a "WikiKit" library that can be used on both mac and iOS. On iOS I just display the content of the document bundle's wiki/ directory and reads the meta data from few other files of the document bundle as well. I don't think there would be a real benefit for me to zip the wiki contents at least in this use case.