I am using Core Data to store my user's data. I want to offer a Share button in the UI to export the data wherever they wish. I made a class conforming to UIActivityItemSource
. It is successful when it simply returns a Data object from activityViewController:itemForActivityType
but the file has a system-provided file name. Therefore I'm now trying now to generate a UIDocument
on the fly, save it to the file system, and return the URL
(UIDocument.fileURL
) to the activity view controller. The problem is UIDocument.save
is async but I can't return from activityViewController:itemForActivityType
until the file is saved. My latest code looks like this:
The class init:
let saveQueue = DispatchQueue(label: "saveQueue")
let saveSemaphore = DispatchSemaphore(value: 0)
...
func activityViewController(_ activityViewController: UIActivityViewController, itemForActivityType activityType: UIActivity.ActivityType?) -> Any? {
var success = false
self.saveQueue.async {
self.document.save(to: self.document.fileURL,
for: .forOverwriting) {(didSucceed) in
success = didSucceed
self.saveSemaphore.signal()
}
}
saveSemaphore.wait()
return success ? document.fileURL : nil
}
The behavior is the async code starts, but the save's completion function is never called. What have(n't) I done wrong?
My final solution, per Casey's help, was to override UIDocument.save
.
override func save(to url: URL, for saveOperation: UIDocument.SaveOperation, completionHandler: ((Bool) -> Void)? = nil) {
do {
let contents = try self.contents(forType: GlobalConst.exportType) as! Data
try contents.write(to: url, options: [.atomicWrite])
completionHandler?(true)
} catch {
print("write error: \(error)")
completionHandler?(false)
}
}
I have yet to understand why the superclass save was a problem, but I can let it slide.
In order to get it to work I had to create a subclass of UIDocument and override func save(to:for:completionHandler:)
class MyDocument: UIDocument {
var body: String = "Hello world, this is example content!"
override func save(to url: URL, for saveOperation: UIDocument.SaveOperation, completionHandler: ((Bool) -> Void)? = nil) {
do {
try self.body.write(toFile: url.path, atomically: true, encoding: .utf8)
completionHandler?(true)
} catch {
print("write error: \(error)")
completionHandler?(false)
}
}
}
Your code stays exactly the same, just make sure self.document
is of type MyDocument
(or whatever subclass you likely already have).
let document = MyDocument(fileURL: URL(fileURLWithPath: NSHomeDirectory() + "/Documents/output.txt"))