NSKeyedArchiver并在目标之间共享自定义类 [英] NSKeyedArchiver and sharing a custom class between targets

查看:228
本文介绍了NSKeyedArchiver并在目标之间共享自定义类的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

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

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

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]
}

我遇到了类命名空间问题,即Today扩展中的NSKeyedUnarchiver无法正确解码对象,因此我使用了此答案,并在类定义之前添加@objc:

@objc(Drug)
class Drug: NSObject, NSCoding {
    // Properties, methods etc...
}

这完美地解决了问题.但是,这将是我的应用程序的1.3版本,似乎这破坏了预先存在的数据的取消存档过程(正如我认为的那样).

处理这种情况的最佳方法是什么,就像我刚刚进行此更改一样,新版本的应用程序将对现有用户崩溃!

关于此,我找不到其他答案,而且我不确定NSKeyedArchiver.setClass()方法是否相关,也不确定在哪里使用它.

任何帮助将不胜感激.谢谢.

解决方案

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

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

或者,您可以提供符合 NSKeyedUnarchiverDelegate 的委托,并提供一个 unarchiver(_:cannotDecodeObjectOfClassName:originalClasses:) ,但这在这种情况下可能会过大.


请记住,这适用于向前迁移旧数据.如果您有新版本的应用程序需要将数据发送到旧版本的应用程序,则在存档方面,您需要做的事情与之类似-继续用

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

My app uses a custom class as its data model:

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

I have just created a Today extension and need to access the user’s data from it, so I use NSCoding to persist my data in both the app container and the shared container. These are the save and load functions in the main app:

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]
}

I encountered the problem of class namespacing where the NSKeyedUnarchiver in my Today extension could not decode the object properly, so I used this answer and added @objc before the class definition:

@objc(Drug)
class Drug: NSObject, NSCoding {
    // Properties, methods etc...
}

This solved the problem perfectly. However, this will be version 1.3 of my app, and it seems this breaks the unarchiving process for pre-existing data (as I thought it might).

What is the best way to handle this scenario, as if I just make this change, the new version of the app will crash for existing users!

I cannot find any other answers about this, and I am not sure that the NSKeyedArchiver.setClass() method is relevant, nor am I sure where to use it.

Any help would be gratefully received. Thanks.

解决方案

This is exactly the use case for NSKeyedUnarchiver.setClass(_:forClassName:) — you can migrate old classes forward by associating the current classes for their old names:

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

Alternatively, you can provide a delegate conforming to NSKeyedUnarchiverDelegate and providing a unarchiver(_:cannotDecodeObjectOfClassName:originalClasses:), but that's likely overkill for this scenario.


Keep in mind that this works for migrating old data forward. If you have newer versions of the app that need to send data to old versions of the app, what you'll need to do is similar on the archive side — keep encoding the new class with the old name with NSKeyedArchiver.setClassName(_:for:):

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

If you have this backwards compatibility issue, then unfortunately, it's likely you'll need to keep using the old name as long as there are users of the old version of the app.

这篇关于NSKeyedArchiver并在目标之间共享自定义类的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆