在 Swift 中访问共享容器中的自定义对象

Posted

技术标签:

【中文标题】在 Swift 中访问共享容器中的自定义对象【英文标题】:Access custom object in shared container in Swift 【发布时间】:2017-05-10 21:44:13 【问题描述】:

我正在制作一个自定义键盘,我想在容器应用程序中构建一个资产(特别是 trie 字典),然后在键盘扩展通过共享容器启动时访问它。为此,我将从这个相当基础的课程开始:

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

太好了。我已经设置了我的共享容器,并在容器应用程序的 ViewController 的 viewDidLoad 函数中加载了共享容器(一些测试变量和自定义对象):

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

为了测试它,我在 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")
    

一切正常。但是当我进入 KeyboardViewController 并尝试访问时,检索简单的字符串可以正常工作:

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

但是如果我尝试访问自定义对象 Person,如下所示:

    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

    

我收到以下错误响应的崩溃:

2017-05-10 14:34:46.938253-0700 test_keyboard[3196:857207] viewServiceDidTerminateWithError:: 错误 域=_UIViewServiceInterfaceErrorDomain 代码=3“(空)” UserInfo=Message=服务连接中断

数据在那里,如果我在两侧进行简单的 data.count 检查,它的大小相同(269)。如果我注释掉 NSKeyedUnarchiver 行,就没有问题,所以我确定这就是问题所在。当它在容器应用程序中正常工作时,到底发生了什么导致它在键盘扩展中崩溃?

【问题讨论】:

【参考方案1】:

很可能是在不同模块中运行的问题。您的应用是与扩展不同的模块,完全限定的类名包括模块名。在应用程序中类似于AppName.Person,但在扩展程序中类似于KeyboardExt.PersonNSCoding 无法匹配这些,所以它很糟糕。

有几种解决方法。一种是创建一个应用程序和扩展程序都使用的框架,并将共享类放在那里。然后模块的名称将始终类似于“FrameworkName.Person”,并且一切正常。

另一种是专门告诉(取消)归档过程该类使用什么名称。然后它将写入您指定的名称并将其与适当的类匹配。在写入任何数据之前,请执行以下操作

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

然后在读取任何数据之前,做相反的事情:

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

【讨论】:

这看起来很有希望 - 我会试一试! @asetniop 您通过我的网站和 Twitter 联系了我,询问我是否愿意回答这个问题。如果有帮助,请将其标记为已接受。如果没有,请描述您遇到的问题。 它做到了! (对不起,速度慢了——我妻子需要 macbook 作证)非常感谢!

以上是关于在 Swift 中访问共享容器中的自定义对象的主要内容,如果未能解决你的问题,请参考以下文章

将 Alamofire 调用包装到 Swift 中的自定义对象中?

Flutter 存储共享首选项中的自定义类列表

Swift 中的自定义地图

在 Swift 中将自定义 UITableViewCell 共享到多个 UITableViewController

如何在Swift 3中修复我的自定义UITextField对象?

远程通知中的自定义声音 iOS 10,swift 3