Developing document based applications usually involves creating a subclass of UIDocument with the necessary functionality to save and load a custom file type to disk.

Depending on the model structure of the application, the document data may need to be stored as a single file or a collection of one or more files which are created and updated in tandem. The latter is also known as a document package. Document packages are a special kind of folders (which contain one or more files) presented to the user as a single file. For example in Finder, we can right click on document packages and choose the “Show Package Contents” option to reveal the contents of the folder.

To implement a document package (which is in effect a folder), the UIDocument object makes use of a FileWrapper:

// MARK: - Saving and loading

var documentFileWrapper = FileWrapper(directoryWithFileWrappers: [:])

override func contents(forType typeName: String) throws -> Any {
    // Saving the document...

    if let packageWrapper = documentFileWrapper.fileWrappers?[DocumentFileNames.metaFile.rawValue] {
        documentFileWrapper.removeFileWrapper(packageWrapper)
    }

    let packageData = try NSKeyedArchiver.archivedData(withRootObject: package, requiringSecureCoding: true)

    self.documentFileWrapper.addRegularFile(withContents: packageData, preferredFilename: DocumentFileNames.metaFile.rawValue)

    return self.documentFileWrapper
}

override func load(fromContents contents: Any, ofType typeName: String?) throws {
    // Loading the document...

    guard let fileWrapper = contents as? FileWrapper else {
        throw MyAppErrors.CannotLoadFileWrappers
    }

    guard let packageWrapper = fileWrapper.fileWrappers?[DocumentFileNames.metaFile.rawValue], let packageData = packageWrapper.regularFileContents else {
        throw MyAppErrors.CannotLoadDocumentComponentPackage
    }

    guard let loadedPackage = try? NSKeyedUnarchiver.unarchiveTopLevelObjectWithData(packageData) as? LarningPackage else {
        throw MyAppErrors.CannotUnarchiveDocumentComponentPackage
    }

    self.package = loadedPackage
    self.documentFileWrapper = fileWrapper
}

For each custom file type supported by the application, a corresponding exported UTI (universal type identifier) is required.

To make the document appear as a single file in the document browser, the document’s exported UTI must conform to the UTI com.apple.package.

Adding an exported UTI in Xcode 11

An extra step to enable the system to see the custom file type as a document package is described in Apple’s guide for creating document packages

This can be achieved by including the LSTypeIsPackage key with a value of true in the Document Types section as an additional document property or by editing the Info.plist:

Adding additional document properties to the export UTI type in Xcode 11