NSKeyedArchiver并在目标之间共享自定义类

时间:2018-02-02 13:30:42

标签: ios swift ios-app-extension nskeyedarchiver nskeyedunarchiver

我的应用使用自定义类作为其数据模型:

var text = await Task.Run(() =>
{
    result = index.lucene_index(filepath, filename, fileContent);
    if (result) {
        return filename;
    }
    return string.Empty;
});
if (!string.IsNullOrEmpty(text)) {
    result_tbx.Text += $"Indexed \t {text} {Environment.NewLine}";
    result_tbx.ScrollToEnd();
}

我刚刚创建了一个Today扩展,需要从中访问用户数据,因此我使用NSCoding将我的数据保存在app容器和共享容器中。这些是主应用程序中的保存和加载功能:

class Drug: NSObject, NSCoding {
    // Properties, methods etc...
}

我遇到了类命名空间的问题,其中我今天扩展中的NSKeyedUnarchiver无法正确解码对象,所以我使用了this answer并在类定义之前添加了@objc:

func saveDrugs() {
    // Save to app container
    let isSuccessfulSave = NSKeyedArchiver.archiveRootObject(drugs, toFile: Drug.ArchiveURL.path)
    if isSuccessfulSave {
        print("Drugs successfully saved locally")
    } else {
        print("Error saving drugs locally")
    }

    // Save to shared container for extension
    let isSuccessfulSaveToSharedContainer = NSKeyedArchiver.archiveRootObject(drugs, toFile: Drug.SharedArchiveURL.path)
    if isSuccessfulSaveToSharedContainer {
        print("Drugs successfully saved to shared container")
    } else {
        print("Error saving drugs to shared container")
    }
}

func loadDrugs() -> [Drug]? {
    return NSKeyedUnarchiver.unarchiveObject(withFile: Drug.ArchiveURL.path) as? [Drug]
}

这完美地解决了这个问题。但是,这将是我的应用程序的1.3版本,似乎这打破了预先存在的数据的解密过程(我认为可能)。

处理此方案的最佳方法是什么,就好像我只是进行此更改一样,新版本的应用程序将因现有用户而崩溃!

我找不到任何其他答案,我不确定@objc(Drug) class Drug: NSObject, NSCoding { // Properties, methods etc... } 方法是否相关,也不确定在哪里使用它。

感谢任何帮助。感谢。

1 个答案:

答案 0 :(得分:3)

这正是NSKeyedUnarchiver.setClass(_:forClassName:)的用例 - 您可以通过将当前类与旧名称相关联来向前迁移旧类:

let unarchiver = NSKeyedUnarchiver(...)
unarchiver.setClass(Drug.self, forClassName: "myApp.Drug")
// unarchive as appropriate

或者,您可以提供符合NSKeyedUnarchiverDelegate并提供unarchiver(_:cannotDecodeObjectOfClassName:originalClasses:)的代理人,但这种情况可能过度。

请注意,这适用于向前迁移旧数据。如果您有较新版本的应用程序需要将数据发送到旧版本的应用程序,那么您需要做的就是在存档方面类似 - 使用{{3}旧名称对新类进行编码}:

let archiver = NSKeyedArchiver(...)
archiver.setClassName("myApp.Drug", for: Drug.self)
// archive as appropriate

如果您遇到此向后兼容性问题,那么很遗憾,只要有旧版本应用的用户,您就可能需要继续使用旧名称。