【问题标题】:Wrapping Core Data into Codable structs将核心数据包装到可编码结构中
【发布时间】:2018-11-17 10:30:41
【问题描述】:

我将核心数据对象包装到结构中以使它们可编码。

[注意:在您指导我为每个 Core Data 类编写 swift 文件之前,我想说包装 NSManagedObject 子代是有意识地选择有利于代码可维护性的结果,因为数据模型可能会在未来。]

我有几个这样的类,这里是一个例子:

struct CodableNeed : Codable {
    enum CodingKeys: String, CodingKey {
        ...
    }

    var need:Need

    init (_ need:Need) {
        self.need = need
    }

    init(from decoder: Decoder) throws {
        ....
    }

    func encode(to encoder: Encoder) throws {
        ....
    }
}

这实际上工作得很好,因为结构的 init(from:decoder) 中的任何更新实际上都存储在 ManagedObjectContext 中。

为了让每个 NSManagedObject 类实例返回它们自己的结构体,我定义了一个协议,每个类实例都可以返回它们自己的 Codable 结构体:

protocol CodableWhenWrapped {
    func wrapToCodable() -> Codable
}

extension Need : CodableWhenWrapped {

    func wrapToCodable() -> Codable {
        return CodableNeed(self)
    }
}

然后我在编码函数中使用它:

func jsonDataOfCodable<T:Encodable>(_ object:T) throws -> Data {
    let encoder = JSONEncoder()
    let data = try encoder.encode(object)
    return data
}

我调用这个函数来生成一个URLSessionUploadTask

func updateTaskFor<T: NSManagedObject> (_ object:T, withSession session:URLSession) throws -> URLSessionUploadTask
    where T: CodableWhenWrapped
{
    let encoder = JSONEncoder() 

    // Here is the compile error:  
    // " Cannot invoke 'jsonDataOfCodable' with an argument list of type '(Codable)' "
    let jsonData = try jsonDataOfCodable(object.wrapToCodable())

    // then continue with generating the uploadTask        
    let url = "https://myurl.com/"
    let request = URLRequest(url: url)
    let updateTask = session.uploadTask(with: request, from: jsonData) { (data, response, error) in
        ....
    }
}

问题是:调用jsonDataOfCodable 时代码无法编译:Cannot invoke 'jsonDataOfCodable' with an argument list of type '(Codable)'

知道为什么编译器不喜欢这样吗?

请注意,当我在 jsonDataOfCodable 原型中指定 &lt;T:Codable&gt; 而不是 &lt;T:Encodable&gt; 时,我遇到了同样的问题。

【问题讨论】:

  • JSONEncoder().encode 需要符合(En)codable 的具体类型,而不是协议Codable 本身。您可以使用associatedtype。在每个 NSManagedObject 子类中实现 init(from decoder:encode(to encoder: 可能更容易。特别是如果有关系,这是唯一的选择。

标签: swift core-data wrapper swift-protocols codable


【解决方案1】:

在反思vadian 的评论后,我正在回答我自己的问题。

我通过更新协议解决了这个问题:

protocol CodableWhenWrapped : Encodable {
    func wrapToCodable<T>() -> CodableWrapper<T>
    func update(from decoder:Decoder) throws
}

然后我为我的对象添加了一个通用包装器(全部继承自 Synchronizable 类):

struct CodableWrapper<T> : Codable
where T:CodableWhenWrapped, T: Synchronizable
{
    enum SynchronizableCodingKeys: String, CodingKey {
        case pk = "idOnServer"
        case lastModificationDate = "modified_on"
    }

    var object:T

    init(_ obj:T){
        self.object = obj
    }

    init(from decoder: Decoder) throws {
        // This is how the NSManagedObjectContext is passed through the decoder
        guard let codingUserInfoKeyManagedObjectContext = CodingUserInfoKey.managedObjectContext,
            let managedObjectContext = decoder.userInfo[codingUserInfoKeyManagedObjectContext] as? NSManagedObjectContext
            else {
                fatalError("Failed to decode Need: could not retriever the NSManagedObjectContext. Was it included in Decoder.userInfo as CodingUserInfoKey.managedObjectContext ?")
            }

        // check if there is a an existing object with the same idOnServer. If not, create a new one        
        let container = try decoder.container(keyedBy: SynchronizableCodingKeys.self)
        let pk = try container.decode(UUID.self, forKey: .pk)

        object = try Synchronizable.withIDOnServer(pk.uuidString, inMOC: managedObjectContext) ?? T(context: managedObjectContext)
        try object.update(from: decoder)
    }

    func encode(to encoder: Encoder) throws {
        try object.encode(to: encoder)
    }
}

然后我可以有一个通用函数来序列化为 JSON:

func jsonDataFor<T>(_ object:T) throws -> Data
    where T:CodableWhenWrapped, T:Synchronizable {
    let encoder = JSONEncoder()
    let wrappedObject:CodableWrapper<T> = object.wrapToCodable()
    let jsonData = try encoder.encode(wrappedObject)
    return jsonData
}

解码只是:

let _ = try decoder.decode(CodableWrapper<T>.self, from: data!)

感谢@vadian 的帮助,我希望这对其他人有用! 顺便说一句,CodableWhenWrapped 协议可以很容易地用于其他对象类型。

【讨论】:

    猜你喜欢
    • 2020-01-31
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-03-02
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-07-28
    相关资源
    最近更新 更多