ios - 适用于 iOS 和 macOS 的 Swift 框架

标签 ios swift macos frameworks

目前我正在为我的毕业设计开发一个应用程序。问题是,它不仅仅是一个应用程序,它由一个为用户制作的 iOS 应用程序和一个为“所有者”制作的 macOS 应用程序组成。在 mac 应用程序中,所有者可以创建一个文件,该文件在 iOS 应用程序中应该是红色的。

到目前为止,我已经制作了一个脚本,其中包含传输信息所需的所有变量,它可以序列化和反序列化文件并将其分配给 UI。只要我在 macOS 应用程序上工作,一切都运行良好,可以创建文件、反序列化等等,但是当我尝试转到 iOS 应用程序时,我将用作 dataModel 的完全相同的脚本复制到项目中当我尝试反序列化在 macOS 文件中创建的文件时,应用程序崩溃并给我一条错误消息:

cannot decode object of class (ExcursionCreator.ExcursionDataModel) for key (root); the class may be defined in source code or a library that is not linked'

我调查了这个错误,发现我不能只将脚本从一个项目复制到另一个项目,所以我找到了一个来源说我应该只用这个文件创建一个框架,但是我还没有找到任何讨论跨平台框架的有用主题。

简单来说,我的问题是,我是否必须为此目的创建一个框架,如果是的话,我将如何做才能使其在 macOS 和 iOS 上都能正常工作。任何其他建议也将不胜感激。在此先感谢豪尔赫。 :)

更新

正如 danielv 所问,这是我存储信息的代码,它也符合 NSCoding 协议(protocol),因此我可以对其进行序列化和反序列化:

import Foundation

class ExcursionDataModel: NSObject, NSCoding {

    var title: String

    var imagesData = [Data]()
    var thumbnailImageData: Data?
    var shortText = String()
    var completeText = String()

    var difficulty: String = "Hard"
    var duration: String = "Long"

    var isFavourite: Bool = false
    var type: excursionType = .other

    enum excursionType: String {
        case biking
        case rafting
        case other
    }

    static let possibleDifficulties = ["Hard", "Medium", "Easy"]
    static let possibleDurations = ["Long", "Medium", "Short"]

    init (title: String) {
        self.title = title
    }

    override init() {
        self.title = "Title"
    }

    struct PropertyKey {
        static let titleKey = "title"
        static let imagesKey = "images"
        static let thumbnailImageKey = "thumbnailImage"
        static let shortTextKey = "shortText"
        static let completeTextKey = "completeText"
        static let difficultyKey = "difficulty"
        static let durationKey = "duration"
        static let isFavouriteKey = "isFavourite"
        static let typeKey = "type"
    }

    //MARK: - NSCoding

    func encode(with aCoder: NSCoder) {

        aCoder.encode(title, forKey: PropertyKey.titleKey)
        aCoder.encode(imagesData, forKey: PropertyKey.imagesKey)
        aCoder.encode(thumbnailImageData, forKey: PropertyKey.thumbnailImageKey)
        aCoder.encode(shortText, forKey: PropertyKey.shortTextKey)
        aCoder.encode(completeText, forKey: PropertyKey.completeTextKey)
        aCoder.encode(difficulty, forKey: PropertyKey.difficultyKey)
        aCoder.encode(duration, forKey: PropertyKey.durationKey)
        aCoder.encode(isFavourite, forKey: PropertyKey.isFavouriteKey)
        aCoder.encode(type.rawValue, forKey: PropertyKey.typeKey)
    }

    required convenience init?(coder aDecoder: NSCoder) {
        let title = aDecoder.decodeObject(forKey: PropertyKey.titleKey) as! String

        self.init(title: title)

        self.imagesData = aDecoder.decodeObject(forKey: PropertyKey.imagesKey) as! [Data]
        self.thumbnailImageData = aDecoder.decodeObject(forKey: PropertyKey.thumbnailImageKey) as? Data
        self.shortText = aDecoder.decodeObject(forKey: PropertyKey.shortTextKey) as! String
        self.completeText = aDecoder.decodeObject(forKey: PropertyKey.completeTextKey) as! String
        self.difficulty = aDecoder.decodeObject(forKey: PropertyKey.difficultyKey) as! String
        self.duration = aDecoder.decodeObject(forKey: PropertyKey.durationKey) as! String
        self.isFavourite = aDecoder.decodeObject(forKey: PropertyKey.isFavouriteKey) as! Bool
        self.type = excursionType(rawValue: aDecoder.decodeObject(forKey: PropertyKey.typeKey) as! String)!
    }
}

最佳答案

来自 NSCoder documentation :

NSCoder operates on objects, scalars, C arrays, structures, and strings, and on pointers to these types. It does not handle types whose implementation varies across platforms, such as union, void *, function pointers, and long chains of pointers. A coder object stores object type information along with the data, so an object decoded from a stream of bytes is normally of the same class as the object that was originally encoded into the stream.

您在 MacOS 应用程序中编码的类是 ExcursionCreator.ExcursionDataModel。但是,您的 iOS 应用程序可能具有不同的模块名称,因此即使您在 iOS 应用程序中包含完全相同的 swift 类,实际的命名空间类名称也会不同,例如 ExcursionIOSApp.ExcursionDataModel

NSCoder 不知道这些是相同的类,当它尝试解码 ExcursionDataModel 时找不到它,所以你会得到错误。

您的选择是:

选项#1

为您的序列化使用开放的可移植格式。 JSON 是一个流行的选择,但还有其他选择。如果您以后决定将您的应用程序移植到 Android,这有利于在 Apple 生态系统之外的平台中重用。

MacOS/iOS 中有一个基础的 JSON 序列化支持,许多开源项目提供更好/额外的 JSON 支持。

选项 #2

使用跨平台共享相同实现和名称的不同类。例如,您可以使用 NSDictionary 作为您的数据结构。只要确保你放在那里的所有东西都符合 NSCoder 要求的限制

选项 #3

将您的数据类放入框架中。这将强制该类在两个项目中使用时具有相同的名称。

要创建框架,请在创建新项目时选择一个 MacOS 框架项目。将您的 ExcursionDataModel 添加到此框架。

请注意,您需要为每个平台单独编译此框架。因此,一旦您拥有适用于 MacOS 的它,您将需要在 xcode 中为其添加额外的目标,以便为 iOS 编译它。

然后将您的框架项目包含在您的每个应用程序中。确保从您的应用程序源中删除该类。

查看 Apple 的 Framework Programming Guide ,

就我个人而言,我会选择选项 #1,使用开放和流行的格式,并远离 Apple 的专有序列化。

关于ios - 适用于 iOS 和 macOS 的 Swift 框架,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/40746834/

相关文章:

ios - UITableView 顶部和底部单元格在加载和每次更新时扩展

iphone - ios asihttprequest 发布字符串

xcode - 在自动调整大小的标签中的单独行上显示 Swift 字符串数组

postgresql - 我不知道 Postgresql 如何在我的 mac 上创建用户

iOS - bodyWithPolygonFromPath : Body is the same as the path but collisions are not working properly

objective-c - 标准化顶点和法线坐标 Open GL ES 2.0

objective-c - 以编程方式检查是否安装了应用程序

Swift 闭包语法

c - 在 mac OS X 上,gcc 在 C 中默认链接数学库?

macos - 如何设置NSPopUpButton的菜单位置?