Расшифровка недействительных URL как ноль - PullRequest
2 голосов
/ 17 октября 2019

Перед тем, как ответить:

Я знаю:

  • То, что пустая строка недопустима URL
  • ТоЯ мог бы написать собственный декодер для Employee
  • , который я мог бы объявить url как String

То, что я ищу, является лучшим решением для декодированиянеобязательно URL сам. Я надеюсь, что какая-то магия Codable мне не хватает!


Итак, у меня есть JSON, такой как

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

и соответствующий объект, который содержит необязательный URL…

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

Поскольку url в JSON недопустимо, я бы хотел, чтобы оно декодировалось как 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 Ответ

5 голосов
/ 17 октября 2019

Если вы используете 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
...