Reputation: 405
I'm developing a SwiftUI document-based app that contains some easily serializable data plus multiple images. I'd like to save the document as a package (i.e, a folder) with one file containing the easily serialized data and a subfolder containing the images as separate files. My package directory should look something like this:
<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've created a FileWrapper subclass that sets up the directory structure and adds the serialized snapshot appropriately but when I run the app in an iOS simulator and click on "+" to create a new document the app runs through the PkgFileWrapper init() and write() without error but returns to the browser window without apparently creating anything. I have declared that the Exported and Imported Type Identifiers conform to "com.apple.package". Can anyone suggest a way to get this working?
The PkgFileWrapper class looks like this:
class PkgFileWrapper: FileWrapper {
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 ])
// NOTE: Writing of images is done outside
// of the ReferenceFileDocument functionality.
}
override func write(to: URL,
options: FileWrapper.WritingOptions,
originalContentsURL: URL?) throws {
try super.write(to: to, options: options,
originalContentsURL: originalContentsURL)
}
required init?(coder inCoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
Upvotes: 3
Views: 1129
Reputation: 405
The solution is to not override PkgFileWrapper.write(...). If the directory structure is set up correctly in the init(...) then the files and directories will be created automatically. The overridden write(...) function above has now been corrected.
If you want to write an image to the Images subdirectory, you could do something like the following:
func addImage(image: UIImage, name: String) {
let imageData = image.pngData()!
imageDirWrapper.addRegularFile(withContents: imageData,
preferredFilename: name)
}
The value of imageDirWrapper is the directory wrapper corresponding to the directory that holds your images, as created in PkgFileWrapper.init() above. A key concept you need to keep in mind here is that the "write" function will get called automatically at the appropriate time - you don't explicitly write out your image data. The ReferenceFileDocument class will arrange for that and will also arrange for your app to be passed the appropriate URL for setting up your file wrappers.
The imageDirWrapper variable is set in the required init(...) for the ReferenceFileDocument protocol:
required init(configuration: ReadConfiguration) throws {
phraseSet = PhraseSet()
if configuration.file.isDirectory {
if let subdir = configuration.file.fileWrappers {
// first load in the phraseSet
for (name, wrapper) in subdir {
if name == PkgFileWrapper.phraseSetFileName {
if let data = wrapper.regularFileContents {
phraseSet = try PhraseSet(json: data)
}
}
}
// next load in the images and put them into the phrases.
for (name, wrapper) in subdir {
if name == PkgFileWrapper.imageDirectoryName {
if let imageDir = wrapper.fileWrappers {
imageDirWrapper = wrapper
for (iName, iWrapper) in imageDir {
print("image file: \(iName)")
if let d = iWrapper.regularFileContents {
for p in phraseSet.phrases {
if p.imageName == iName {
// TBD: downsample
var uiD = ImageData(data: d)
if doDownSample {
uiD.uiimageData = downsample(data: d,
to: imageSize)
} else {
_ = uiD.getUIImage()
}
images[iName] = uiD
}
}
}
}
}
}
}
}
} else {
throw CocoaError(.fileReadCorruptFile)
}
You can see here how imageDirWrapper is set by looking through the passed-in directory's subdirectories for the image directory name. Also some bonus code: it first looks through the passed-in directory for the data file and loads it in; then it looks for the image directory and processes it.
Upvotes: 3