Using NSFileWrapper
and UIDocument
. File writing happens in:
class MyDocument: UIDocument {
var wrapper: NSFileWrapper?
override internal func contentsForType(typeName: String) throws -> AnyObject {
// called by doc.saveToURL
if wrapper != nil {
// resaving an existing doc
}
else {
wrapper = NSFileWrapper.init(directoryWithFileWrappers: [:])
saveMyData()
}
return wrapper!
}
}
wrapper is modified in saveMyData().
What seems to happen is that contentsForType
runs on a separate thread and on rare occasion will be running as wrapper is being modified in saveMyData, leading to 'Collection was mutated while being enumerated' error.
I've read other threads about Objective-C's @synchronized
, but the Swift equivalents still confuse me. What's the best way to avoid the error? Also, what's the best way to test for the error? It happens maybe .001 of the time, on the odd occasion where the save runs while the user's making a simultaneous data change.
One possible solution I read about uses the defer
command. Would it work to include objc_sync_enter
and objc_sync_exit
in saveMyData() in the following way?
func saveMyData() {
if wrapper == nil {
return
}
objc_sync_enter(wrapper)
defer { objc_sync_exit(wrapper) }
let data = NSKeyedArchiver.archivedDataWithRootObject(thumbnail)
if let wrap = wrapper!.fileWrappers!["thumbnail"] {
wrapper!.removeFileWrapper(wrap)
}
wrapper!.addRegularFileWithContents(data, preferredFilename: "thumbnail")
self.updateChangeCount(.Done)
}
Uncertain which thread ends up waiting for the other thread and how.
Thank you!