Swift-解码/编码具有不同类型的泛型数组 [英] Swift - Decode/encode an array of generics with different types

查看:84
本文介绍了Swift-解码/编码具有不同类型的泛型数组的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

如何解码/编码不同通用类型的数组?

How can I decode/encode an array of different generic types?

我有一个数据结构,该结构具有符合协议<$ c $的属性c> Connection ,因此我使用泛型:

I have a data structure, which has properties, that conform to a protocol Connection, thus I use generics:

// Data structure which saves two objects, which conform to the Connection protocol
struct Configuration<F: Connection, T: Connection>: Codable {
    var from: F
    var to: T
    private var id: String = UUID.init().uuidString

    enum CodingKeys: String, CodingKey {
        case from, to, id
    }

    init(from decoder: Decoder) throws {
        let container = try decoder.container(keyedBy: CodingKeys.self)
        self.from = try container.decode(F.self, forKey: .from)
        self.to = try container.decode(T.self, forKey: .to)
        self.id = try container.decode(String.self, forKey: .id)
    }

    func encode(to encoder: Encoder) throws {
        var container = encoder.container(keyedBy: CodingKeys.self)
        try container.encode(from, forKey: .from)
        try container.encode(to, forKey: .to)
        try container.encode(id, forKey: .id)
    }
}

protocol Connection: Codable {
    var path: String { get set }
}


// Two implementations of the Connection protocol
struct SFTPConnection: Connection, Codable {
    var path: String
    var user: String
    var sshKey: String
}

struct FTPConnection: Connection, Codable {
    var path: String
    var user: String
    var password: String
}

当我知道什么类型的连接时,此方法很好F T 是。但是在某些情况下,我想加载配置,却不知道 F T 是哪种类型。

This works fine when I know of what type the connections F and T are. But I have cases, where I want to load a configuration, not knowing which type F and T are.

public static func load<F: Connection, T: Connection>(for key: String) throws -> Configuration<F, T>? {
    // Load from UserDefaults
    guard let configurationData = defaults.object(forKey: key) as? Data else {
        return nil
    }

    // Decode
    guard let configuration = try? PropertyListDecoder().decode(Configuration<F, T>.self, from: configurationData) else {
        return nil
    }

    return configuration
}

// OR

func loadAll<F:Connection, T: Connection>() -> [String: Configuration<F, T>]? {
    return UserDefaults.standard.dictionaryRepresentation() as? [String: Configuration<F, T>]
}

在上述情况下 F T 可以是任何未知类型,符合 Connection 协议。因此上述功能将无法使用,因为调用时我需要为 F T 指定特定类型函数,我不知道。

In the above cases F and T could be of any unknown type, that conforms to the Connection protocol. So the above functions wouldn't work, since I would need to specify a specific type for F and T when calling the function, which I don't know.

在第二个函数中,单独的 F 实际上可能是不同的类型。那就是困难所在。我想我还需要以某种方式在用户默认值中存储 F T 的类型,然后在 decode encode 函数(从而丢弃泛型)。但是我不知道我该如何优雅地做到这一点。

In the second function, F alone could actually be of different types. That's where it gets difficult. I figured I need to somehow store the types of F and T in the User Defaults as well and then use them in the decode and encode function (thus discarding the generics). But I have no idea how I would elegantly do that.

对于解决这个问题的任何想法,我将不胜感激!

I would appreciate any ideas on how to solve this problem!

推荐答案

以下解决方案解决了我在使用泛型时遇到的所有问题,并且不了解 Connection 的特定类型。解决方案的关键是

The following solutions resolves all the issues, that I had with generics and not knowing the specific type of Connection. The key to the solution was


  1. Connection 实现的类型保存在实现本身和

  2. 使用 superEncoder superDecoder 来编码/解码属性。

  1. saving the type of a Connection implementation in the implementation itself and
  2. Using superEncoder and superDecoder to encode/decode the from and to properties.

这是解决方案:

import Foundation

protocol Connection: Codable {
    var type: ConnectionType { get }
    var path: String { get set }
}


struct LocalConnection: Connection {
    let type: ConnectionType = ConnectionType.local

    var path: String
}


struct SFTPConnection : Connection {
    let type: ConnectionType = ConnectionType.sftp

    var path: String
    var user: String
    var sshKey: String

    init(path: String, user: String, sshKey: String) {
        self.path = path
        self.user = user
        self.sshKey = sshKey
    }
}


struct FTPConnection: Connection {
    let type: ConnectionType = ConnectionType.ftp

    var path: String
    var user: String
    var password: String
}


struct TFTPConnection: Connection {
    let type: ConnectionType = ConnectionType.tftp

    var path: String
}




enum ConnectionType : Int, Codable {
    case local
    case sftp
    case ftp
    case tftp

    func getType() -> Connection.Type {
        switch self {
        case .local: return LocalConnection.self
        case .sftp: return SFTPConnection.self
        case .ftp: return FTPConnection.self
        case .tftp: return TFTPConnection.self
        }
    }
}




struct Configuration {
    var from : Connection
    var to : Connection
    private var id = UUID.init().uuidString

    var fromType : ConnectionType { return from.type }
    var toType : ConnectionType { return to.type }

    init(from: Connection, to: Connection) {
        self.from = from
        self.to = to
    }
}


extension Configuration : Codable {

    enum CodingKeys: String, CodingKey {
        case id, from, to, fromType, toType
    }

    init(from decoder: Decoder) throws {
        let container = try decoder.container(keyedBy: CodingKeys.self)

        self.id = try container.decode(String.self, forKey: .id)

        var type : ConnectionType

        type = try container.decode(ConnectionType.self, forKey: .fromType)
        let fromDecoder = try container.superDecoder(forKey: .from)
        self.from = try type.getType().init(from: fromDecoder)

        type = try container.decode(ConnectionType.self, forKey: .toType)
        let toDecoder = try container.superDecoder(forKey: .to)
        self.to = try type.getType().init(from: toDecoder)
    }

    func encode(to encoder: Encoder) throws {
        var container = encoder.container(keyedBy: CodingKeys.self)

        try container.encode(self.id, forKey: .id)

        try container.encode(self.fromType, forKey: .fromType)
        let fromContainer = container.superEncoder(forKey: .from)
        try from.encode(to: fromContainer)

        try container.encode(self.toType, forKey: .toType)
        let toContainer = container.superEncoder(forKey: .to)
        try to.encode(to: toContainer)
    }
}

这篇关于Swift-解码/编码具有不同类型的泛型数组的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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