2017-05-10 4 views
1

Ich mache eine benutzerdefinierte Tastatur, und ich möchte ein Asset (speziell ein Trie-Wörterbuch) in der Container-App erstellen und dann darauf zugreifen, wenn die Tastaturerweiterung über einen freigegebenen Container gestartet wird . Um damit zu arbeiten, fange ich mit dieser ziemlich grundlegenden Klasse an:Zugriff auf benutzerdefiniertes Objekt im freigegebenen Container in Swift

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") 
    } 
} 

Großartig. Ich habe meine gemeinsamen Container einrichten bekam, und in viewDidLoad Funktion des Viewcontroller für den Container app ich den gemeinsamen Behälter als solche (ein paar Testvariablen und das benutzerdefinierte Objekt) laden:

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") 

es zu testen aus, ich habe den folgenden Code zu viewWillAppear hinzugefügt:

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") 
    } 

Alles passt gut aus. Aber wenn ich in die KeyboardViewController gehen und versuchen, den Zugriff auf die Dinge, die einfachen String Abrufen funktioniert:

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") 
    } 

Aber dann, wenn ich versuche, das benutzerdefinierte Objekt Person zugreifen zu können, wie zum Beispiel:

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 erhält einen Absturz mit der folgenden Fehlerreaktion:

2017-05-10 14:34:46.938253-0700 test_keyboard[3196:857207] viewServiceDidTerminateWithError:: Error Domain=_UIViewServiceInterfaceErrorDomain Code=3 "(null)" UserInfo={Message=Service Connection Interrupted}

die Daten gibt es, wenn ich auf beiden Seiten eine einfache data.count Prüfung tun, es ist die gleiche Größe (269). Wenn ich die NSKeyedUnarchiver Zeile auskommentiere, gibt es kein Problem, also bin ich mir sicher, dass das das Problem ist. Was in aller Welt passiert, dass es in der Tastaturerweiterung zum Absturz kommt, wenn es in der Container-App gut funktioniert?

Antwort

1

Wahrscheinlich ist es eine Frage des Laufens in verschiedenen Modulen. Ihre App ist ein anderes Modul als die Erweiterung und der vollständig qualifizierte Klassenname enthält den Modulnamen. In der App ist es so etwas wie AppName.Person, aber in der Erweiterung ist es so etwas wie KeyboardExt.Person. NSCoding kann nicht übereinstimmen, so dass es barfs.

Es gibt einige Problemumgehungen. Eine besteht darin, ein Framework zu erstellen, das sowohl von App als auch von Erweiterung verwendet wird, und die freigegebene Klasse dort zu platzieren. Dann wäre der Name mit Modul immer etwas wie "FrameworkName.Person" und alles würde funktionieren.

Die andere besteht darin, dem (Un) -Archivierungsprozess genau mitzuteilen, welcher Name für die Klasse verwendet werden soll. Dann wird der von Ihnen angegebene Name geschrieben und an die entsprechende Klasse angepasst. Bevor irgendwelche Daten zu schreiben, so etwas wie

NSKeyedArchiver.setClassName("Person", for: Person.self) 

Dann, bevor alle Daten zu lesen, das Gegenteil tun:

NSKeyedUnarchiver.setClass(Person.self, forClassName: "Person") 
+0

Dieses sehr vielversprechend aussieht - ich gebe diese einen Schuss! – asetniop

+0

@asetniop Sie haben mich über meine Website und über Twitter kontaktiert, um zu fragen, ob ich das beantworten würde. Wenn es half, bitte markieren Sie es als akzeptiert. Wenn nicht, beschreiben Sie bitte, welche Probleme Sie damit hatten. –

+0

Es hat! (Sorry, langsam zu sein - meine Frau brauchte Macbook für die Ablagerung) Vielen Dank! – asetniop

Verwandte Themen