tl;dr: It's a bug in the Swift Data type. Solution: In your UIDocument contents(forType:)
implementation, change this:
return data
to this:
return NSData(data:data)
More info: Note that merely casting data
to NSData won't help:
return data as NSData
That doesn't get us any further because it's the bridging that's the problem. We're already bridging to NSData and it isn't helping. You have to create a completely new object that is not a Swift Data object.
Even more info: For future generations who come along and want to test this, the crash can be reliably reproduced as follows. Make a new Single View project, with an app delegate and a ViewController as usual. In the ViewController.swift file, put this:
import UIKit
class WhatsUpDoc: UIDocument {
var array = [String]()
override func load(fromContents contents: Any, ofType typeName: String?) throws {}
override func contents(forType typeName: String) throws -> Any {
let data = NSKeyedArchiver.archivedData(withRootObject: array)
return data // comment out this line to avoid the crash
return NSData(data:data)
}
}
class ViewController: UIViewController {
var doc : WhatsUpDoc?
override func viewDidLoad() {
super.viewDidLoad()
let documentsDirectory = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first!
let baseURL = documentsDirectory.appendingPathComponent("Untitled")
let doc = WhatsUpDoc(fileURL: baseURL)
self.doc = doc
self.doc!.save(to:self.doc!.fileURL, for: .forCreating)
}
}
Configure your project to have a deployment target of 9.0, and make sure you've got a 9.0 simulator SDK on hand. In Window > Devices, give yourself an iPad 2 simulator. Set that simulator as the project's destination. Run, and crash. Comment out the line that says to comment it out, and don't crash.
Post-analysis Q&A:
Wait, so what exactly _is_ the bug? You're not saying you can't write a Swift Data object to disk, are you?
No, it's got something to do with the peculiar threaded nature of writing during a UIDocument file save. Note that we are crashing in a background thread (thread 10 or 11 in the OP's screen shots). And what we are crashing in is a method of SwiftNSData
, the NSData wrapped by a Swift Data. We're trying to enumerate the data's bytes on this background thread, and we can't; presumably this isn't thread-safe on certain device types, i.e. 32-bit devices. My solution is to get Swift Data out of the picture completely.
Okay, so it turns out this bug is known; the answer is given here: https://stackoverflow.com/a/41163395/341994 How about that?
Okay, but I figured it out independently in response to the current question. I didn't find out about the other question and answer until later, when it occurred to me to do a search. So I've marked the current question as a duplicate, but I'm leaving my answer for historical purposes, esp. as it gives a clear test case (which the other question does not).
By the way, I do not know why using NSMutableData(data:)
would be better than my solution of using NSData(data:)
, but that's what Apple says to do, so let's just take it as gospel.