可编码的自定义枚举类型

Codable Custom Enum Types

我有一个模型,它是结构的枚举。我需要使这个枚举符合 Codable.

import Foundation

enum Post: Codable {

    case textPost(TextPost)
    case imagePost(ImagePost)
    case videoPost(VideoPost)

    init(from decoder: Decoder) throws {

    }

    func encode(to encoder: Encoder) throws {

    }
}

struct TextPost: Codable {
    let documentID: String
    let createdAt: Int
    let organization: Organization
    let title: String
    let description: String
    let commentTotal: Int
}

struct ImagePost: Codable {
    let documentID: String
    let createdAt: Int
    let organization: Organization
    let title: String
    let description: String
    let commentTotal: Int
    let imageURL: String
}

struct VideoPost: Codable {
    let documentID: String
    let createdAt: Int
    let organization: Organization
    let title: String
    let description: String
    let commentTotal: Int
    let videoURL: String
}

我正在使用 Firebase 的 Firestore 来存储和检索数据。

以前使用简单的 structs 时,我使用以下扩展进行解码;

extension QueryDocumentSnapshot {

    func decode() -> [String: Any] {
        var data = self.data()
        data["documentID"] = documentID
        return data
    }
}

extension JSONDecoder {
    func decodeQuery<T>(_ type: T.Type, fromJSONObject object: Any) throws -> T where T: Decodable {
        return try decode(T.self, from: try JSONSerialization.data(withJSONObject: object, options: []))
    }
}

我这样称呼它:

func retrievePosts(success: @escaping([Post]) -> ()) {
    var posts = [Post]()
    reference(to: .posts).getDocuments { (snapshot, error) in
        if error != nil {
            print(error as Any)
            return
        } else {
            guard let snapshotDocuments = snapshot?.documents else { return }
            for snapshot in snapshotDocuments {
                if let post = try? JSONDecoder().decodeQuery(Post.self, fromJSONObject: snapshot.decode()) {
                    posts.append(post)
                }
            }
        }
    }
}

但是,这不适用于 enums

您将需要:

  1. 添加 CodingKeys 枚举
  2. 实施init(from:)
  3. 实施encode(to:)
  4. 添加一个 Error 枚举

代码如下:

enum Post: Codable {

    case textPost(TextPost)
    case imagePost(ImagePost)
    case videoPost(VideoPost)

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

        if let value = try? values.decode(TextPost.self, forKey: .textPost) {
            self = .textPost(value)
            return
        }
        if let value = try? values.decode(ImagePost.self, forKey: .imagePost) {
            self = .imagePost(value)
            return
        }
        if let value = try? values.decode(VideoPost.self, forKey: .videoPost) {
            self = .videoPost(value)
            return
        }

        throw Error.invalidData
    }

    func encode(to encoder: Encoder) throws {
        var container = encoder.container(keyedBy: CodingKeys.self)
        switch self {
        case .textPost(let value):
            try container.encode(value, forKey: .textPost)
        case .imagePost(let value):
            try container.encode(value, forKey: .imagePost)
        case .videoPost(let value):
            try container.encode(value, forKey: .videoPost)
        }
    }

    private enum CodingKeys: String, CodingKey {
        case textPost
        case imagePost
        case videoPost
    }

    enum Error: Swift.Error {
        case invalidData
    }
}

测试

我将使用您的 TextPost 结构的这个简化版本。

do {
    let post = Post.imagePost(ImagePost(documentID: "123"))
    let data = try JSONEncoder().encode(post)
    let decodedPost = try JSONDecoder().decode(Post.self, from: data)
    print(decodedPost)
} catch {
    print(error)
}

结果:

imagePost(ImagePost(documentID: "123"))