将无效网址解码为nil

时间:2019-10-17 10:49:09

标签: swift url codable

在回答之前:

我知道:

  • 一个空字符串是无效的URL
  • 我可以为Employee编写自定义解码器
  • 我可以将url声明为String

我正在寻找一种更好的解决方案,用于解码可选的URL本身。我希望我缺少一些Codable魔术!


所以,我有JSON,例如

let json = Data("""
                {
                    "name": "Fred",
                    "url": ""
                }
                """.utf8)

以及包含可选URL的相应​​对象...

struct Employee: Decodable {
    let name: String
    let url: URL?
}

由于JSON中的url无效,我希望它解码为nil,而不是抛出错误。

尝试以下操作无效(不会被调用)…

extension Optional where Wrapped == URL {
    init(from decoder: Decoder) throws {
        let container = try decoder.singleValueContainer()
        do {
            self = try container.decode(URL.self)
        } catch {
            self = nil
        }
    }
}

过去我曾经用过……

struct FailableDecodable<T: Decodable>: Deodable {

    let wrapped: T?

    init(from decoder: Decoder) throws {
        let container = try decoder.singleValueContainer()
        do {
            self.wrapped = try container.decode(T.self)
        } catch {
            print("Error decoding failable object: \(error)")
            self.wrapped = nil
        }
    }
}

struct Employee: Decodable {
    let name: String
    let url: FailableDecodable<URL>?
}

但这需要我不断引用url.wrapped

有更好的解决方案吗?

1 个答案:

答案 0 :(得分:5)

如果您使用的是Swift 5.1,则可以使用@propertyWrapper

let json = """
{
    "name": "Fred",
    "url": ""
}
""".data(using: .utf8)!


@propertyWrapper
struct FailableDecodable<Wrapped: Decodable>: Decodable {
    var wrappedValue: Wrapped?

    init(from decoder: Decoder) throws {
        let container = try decoder.singleValueContainer()
        wrappedValue = try? container.decode(Wrapped.self)
    }
}

struct Employee: Decodable {
    let name: String

    @FailableDecodable
    private(set) var url: URL?
}

let employee = try! JSONDecoder().decode(Employee.self, from: json)
employee.url // nil