可解码符合枚举类型的属性

时间:2018-02-14 09:27:11

标签: swift struct enums decodable

我有这个枚举:

enum DealStatus:String {
    case PENDING = "Pending"
    case ACTIVE = "Active"
    case STOP = "Stop"
    case DECLINED = "Declined"
    case PAUSED = "Paused"
}

和struct:

struct ActiveDeals: Decodable {
    let keyword:            String
    let bookingType:        String
    let expiryDate:         Int
    let createdAt:          Int?
    let shopLocation:       String?
    let dealImages:         [DealImages]?
    let dealStatus:         String?
    let startingDate:       Int?
}

在struct中我试图将枚举指定为dealStatus的类型,如下所示:

struct ActiveDeals: Decodable {
        let keyword:            String
        let bookingType:        String
        let expiryDate:         Int
        let createdAt:          Int?
        let shopLocation:       String?
        let dealImages:         [DealImages]?
        let dealStatus:         DealStatus
        let startingDate:       Int?
    }

但是我收到了一些编译错误:

  

类型'ActiveDeals'不符合协议'可解码'

     

协议需要初始化程序'init(from :)',类​​型为'Decodable'   (Swift.Decodable)

     

无法自动合成'可解码'   因为'DealStatus'不符合'Decodable'

3 个答案:

答案 0 :(得分:6)

问题是只有当结构的所有属性都是Decodable并且你的枚举不是Decodable时,Swift才能自动合成Decodable所需的方法。

刚刚在操场上玩,似乎你可以通过声明它是来使你的枚举Decodable ,并且Swift将自动为你合成方法。即

enum DealStatus:String, Decodable  
//                      ^^^^^^^^^ This is all you need
{
    case PENDING = "Pending"
    case ACTIVE = "Active"
    case STOP = "Stop"
    case DECLINED = "Declined"
    case PAUSED = "Paused"
}

答案 1 :(得分:4)

错误表示类的某些属性不符合可解码协议。

为你的枚举添加可解码的一致性,它应该没问题。

extension DealStatus: Decodable { }

答案 2 :(得分:1)

根据Federico Zanetello的帖子Swift 4 Decodable: Beyond The Basics,如果您需要解析基元的子集(字符串,数字,布尔等),Codable和Decobable协议将正常工作。

在你的情况下,只需使DealStatus符合Decodable(如建议的那样) JeremyP)应该解决你的问题。您可以检查Playgrounds创建自己的JSON数据并尝试解析它:

import UIKit

enum DealStatus: String, Decodable {
    case PENDING = "Pending"
    case ACTIVE = "Active"
    case STOP = "Stop"
    case DECLINED = "Declined"
    case PAUSED = "Paused"
}

struct ActiveDeals: Decodable {
    let keyword:            String
    let bookingType:        String
    let expiryDate:         Int
    let createdAt:          Int?
    let shopLocation:       String?
    let dealStatus:         DealStatus
    let startingDate:       Int?
}

let json = """
{
    "keyword": "Some keyword",
    "bookingType": "A type",
    "expiryDate": 123456,
    "createdAt": null,
    "shopLocation": null,
    "dealStatus": "Declined",
    "startingDate": 789456
}
""".data(using: .utf8)!

do {
    let deal = try JSONDecoder().decode(ActiveDeals.self, from: json)
    print(deal)
    print(deal.dealStatus)
} catch {
    print("error info: \(error)")
}

输出将是:

ActiveDeals(keyword: "Some keyword", bookingType: "A type", expiryDate: 123456, createdAt: nil, shopLocation: nil, dealStatus: __lldb_expr_61.DealStatus.DECLINED, startingDate: Optional(789456))
DECLINED

然而,要成为一名更好的程序员,你应该总是好奇并尝试学习如何做事,所以如果你对如何符合可解码协议感兴趣(假设你需要自定义键,自定义错误或一些更复杂的数据)结构),你可以这样做:

import UIKit

enum DealStatus: String {
    case PENDING = "Pending"
    case ACTIVE = "Active"
    case STOP = "Stop"
    case DECLINED = "Declined"
    case PAUSED = "Paused"
}

struct ActiveDeals {
    let keyword:            String
    let bookingType:        String
    let expiryDate:         Int
    let createdAt:          Int?
    let shopLocation:       String?
    let dealStatus:         DealStatus
    let startingDate:       Int?
}

extension ActiveDeals: Decodable {
    enum StructKeys: String, CodingKey {
        case keyword = "keyword"
        case bookingType = "booking_type"
        case expiryDate = "expiry_date"
        case createdAt = "created_at"
        case shopLocation = "shop_location"
        case dealStatus = "deal_status"
        case startingDate = "starting_date"
    }

    enum DecodingError: Error {
        case dealStatus
    }

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

        let keyword = try container.decode(String.self, forKey: .keyword)
        let bookingType = try container.decode(String.self, forKey: .bookingType)
        let expiryDate = try container.decode(Int.self, forKey: .expiryDate)
        let createdAt = try container.decode(Int?.self, forKey: .createdAt)
        let shopLocation = try container.decode(String?.self, forKey: .shopLocation)

        //Get deal status as a raw string and then convert to your custom enum
        let dealStatusRaw = try container.decode(String.self, forKey: .dealStatus)
        guard let dealStatus = DealStatus(rawValue: dealStatusRaw) else {
            throw DecodingError.dealStatus
        }

        let startingDate = try container.decode(Int?.self, forKey: .startingDate)

        self.init(keyword: keyword, bookingType: bookingType, expiryDate: expiryDate, createdAt: createdAt, shopLocation: shopLocation, dealStatus: dealStatus, startingDate: startingDate)
    }
}

let json = """
{
    "keyword": "Some keyword",
    "booking_type": "A type",
    "expiry_date": 123456,
    "created_at": null,
    "shop_location": null,
    "deal_status": "Declined",
    "starting_date": 789456
}
""".data(using: .utf8)!

do {
    let deal = try JSONDecoder().decode(ActiveDeals.self, from: json)
    print(deal)
    print(deal.dealStatus)
} catch {
    print("error info: \(error)")
}

在这种情况下,输出仍然相同:

ActiveDeals(keyword: "Some keyword", bookingType: "A type", expiryDate: 123456, createdAt: nil, shopLocation: nil, dealStatus: __lldb_expr_67.DealStatus.DECLINED, startingDate: Optional(789456))
DECLINED