Beta 2 of SwiftUI DocumentGroup does not support complex file formats

I am writing a SwiftUI application, using DocumentGroup. The document UTI I need to use conforms to 'com.apple.package' not 'public.data' since it is a directory of files.

If the exported Filetype conforms to 'public.data, public.content', the DocumentGroup creates a file and passes it to me (and ends up with the name 'Untitled.hawk' (hawk is my extension).

If I change the exported FileType of 'com.apple.package', I get an error and my Document type is never instantiated.

I see the same issue on both the simulator and an iPad.

The error is:

Code Block
2020-07-20 17:06:27.339598-0400 HawkMultiPlatformMockup[19622:2242164] [DocumentManager] Cannot create urlWrapper for url file:///private/var/mobile/Containers/Data/Application/5F0B4A8E-E9AB-491B-A1B2-30FC88198B91/tmp/Untitled.hawk/. error Error Domain=NSPOSIXErrorDomain Code=20 "couldn't issue sandbox extension com.apple.app-sandbox.read-write for '/private/var/mobile/Containers/Data/Application/5F0B4A8E-E9AB-491B-A1B2-30FC88198B91/tmp/Untitled.hawk': Not a directory" UserInfo={NSDescription=couldn't issue sandbox extension com.apple.app-sandbox.read-write for '/private/var/mobile/Containers/Data/Application/5F0B4A8E-E9AB-491B-A1B2-30FC88198B91/tmp/Untitled.hawk': Not a directory}.


Any ideas how to get this to work?

Accepted Reply

I was wrong. It appears that it does, you just need to do it right.

Document.init() is called on when the + is tapped. This allows you to set up the generic data structure. Then write() is called and you can alter the fileWrapper() to be a directory() and then add a child fileWrapper() for files within the directory.

Then, to launch the DocumentEditor, a call is made to Document.ini(fileWrapper: FileWrapper, contentType: UTType) and this is passed to the DocumentEditor. 

Once the Document is created correctly, the system works. Hope this helps someone else struggling with this sequence.

Replies

I was wrong. It appears that it does, you just need to do it right.

Document.init() is called on when the + is tapped. This allows you to set up the generic data structure. Then write() is called and you can alter the fileWrapper() to be a directory() and then add a child fileWrapper() for files within the directory.

Then, to launch the DocumentEditor, a call is made to Document.ini(fileWrapper: FileWrapper, contentType: UTType) and this is passed to the DocumentEditor. 

Once the Document is created correctly, the system works. Hope this helps someone else struggling with this sequence.
@cwooloszynski This is very interesting, can you post some sample code? Really appreciate it.

@cwoloszynski, I'm having the exact issue you discussed and I can't quite understand how you fixed it. For the exported/imported type identifier "conforms to" did you finally use "com.apple.package"?

I want a directory structure that looks like:

     <UserChosenName.pspkg>/.    // directory package containing my document data and images
           PhraseSet.dat   // regular file with serialized data from snapshot
           Images/              // subdirectory for images (populated directly from my app as needed)
                 Image0.png
                 Image1.png
                 ....

I'm using the following class for the FileWrapper to encapsulate what I think I need to do. Can you see something I'm doing incorrectly?


    var snapshot: Data

    init(withSnapshot: Data) {
        self.snapshot = withSnapshot
        let sWrapper = FileWrapper(regularFileWithContents: snapshot)
        let dWrapper = FileWrapper(directoryWithFileWrappers: [:])
        super.init(directoryWithFileWrappers: ["PhraseSet.dat" : sWrapper,
                                               "Images" : dWrapper ])

        // TO BE DONE: create subdirectory for images. Writing of
        // images is done outside of the ReferenceFileDocument
        // functionality.
    }

    override func write(to url: URL,
                        options: FileWrapper.WritingOptions = [],
                        originalContentsURL: URL?) throws  {
        if let fileWrappers = fileWrappers {
            for (_, wrapper) in fileWrappers {
                try wrapper.write(to: url, options: options,
                                  originalContentsURL: originalContentsURL)
            }
        }
    }

    required init?(coder inCoder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
}