I'm making a custom keyboard, and I want to build an asset (specifically, a trie dictionary) in the container app and then access it when the keyboard extension launches via a shared container. To work up to that, I'm starting with this fairly basic class:
class Person: NSObject, NSCoding {
let name: String
let age: Int
init(name: String, age: Int) {
self.name = name
self.age = age
}
required init(coder decoder: NSCoder) {
self.name = decoder.decodeObject(forKey: "name") as? String ?? ""
self.age = decoder.decodeInteger(forKey: "age")
}
func encode(with coder: NSCoder) {
coder.encode(name, forKey: "name")
coder.encode(age, forKey: "age")
}
}
Great. I've got my shared container set up, and in viewDidLoad function of the ViewController for the container app I load up the shared container as such (a few test variables and the custom object):
let shared = UserDefaults(suiteName: "group.test_keyboard")
shared!.set("test", forKey:"key_string")
shared!.set(false, forKey:"key_boolean")
shared!.set(34, forKey:"key_int")
let newPerson = Person(name: "Joe", age: 10)
let encodedData = NSKeyedArchiver.archivedData(withRootObject: newPerson)
shared!.set(encodedData, forKey: "people")
To test it out, I added the following code to viewWillAppear:
let shared = UserDefaults(suiteName: "group.test_keyboard")
let test_string = shared?.string(forKey: "key_string")
if(test_string != nil){
print("string-\(test_string!)")
}
else{
print("nil")
}
// retrieving a value for a key
if let data = shared?.data(forKey: "people"){
let test_person = NSKeyedUnarchiver.unarchiveObject(with: data) as? Person
print("-\(test_person!.name)") // Joe
let data_size = String(data.count)
print("-\(data_size)")
} else {
print("There is an issue")
}
Everything checks out fine. But when I go into the KeyboardViewController and try to access things, retrieving the simple string works fine:
let shared = UserDefaults(suiteName: "group.test_keyboard")
shared?.synchronize()
let test_string = shared?.string(forKey: "key_string")
if(test_string != nil){
(textDocumentProxy as UIKeyInput).insertText(test_string!)
}
else{
(textDocumentProxy as UIKeyInput).insertText("nil")
}
But then if I try to access the custom object Person, as such:
if let data = shared?.data(forKey: "people") {
(textDocumentProxy as UIKeyInput).insertText("found")
(textDocumentProxy as UIKeyInput).insertText(String(data.count))
let myPeople = NSKeyedUnarchiver.unarchiveObject(with: data) as? Person
}
I get a crash with the following error response:
2017-05-10 14:34:46.938253-0700 test_keyboard[3196:857207] viewServiceDidTerminateWithError:: Error Domain=_UIViewServiceInterfaceErrorDomain Code=3 "(null)" UserInfo={Message=Service Connection Interrupted}
The data is there, if I do a simple data.count check on both sides it's the same size (269). If I comment out the NSKeyedUnarchiver line, there's no problem, so I'm certain that's where the issue is. What on earth is happening that causes it to crash inside the Keyboard Extension when it works just fine in the container app?