Кодируемые пользовательские типы перечислений - PullRequest
1 голос
/ 27 марта 2020

У меня есть модель, которая является перечислением структур. Мне нужно согласовать это перечисление с 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
}

. Я использую Firestore из Firebase для хранения и извлечения данных.

Ранее при использовании простых struct s я использовал следующее расширение для декодирования;

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

Однако, это не работает на enum s

1 Ответ

1 голос
/ 28 марта 2020

Вам необходимо:

  1. добавить CodingKeys перечисление
  2. агрегат init(from:)
  3. агрегат encode(to:)
  4. добавить Error enum

Вот код:

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

Test

Я буду использовать эту упрощенную версию вашей TextPost struct.

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"))
...