Как можно настроить IPv4Address и IPv6Address в Codable с помощью сетевой инфраструктуры Swift? - PullRequest
1 голос
/ 01 февраля 2020

Сетевая платформа Swift включает в себя структуры IPv4Address и IPv6Address. Они используются с NWEndpoints для сетевых подключений. Структура IPv6Address также полезна для проверки синтаксиса адреса IPv6 и реализации правил сокращения.

Как я могу заставить IPv4Address и IPv6Address соответствовать Codable?

Ответы [ 2 ]

1 голос
/ 01 февраля 2020

Нет необходимости создавать свой собственный IPv4AddressDecodingError. Вы можете бросить DecodingError, используя dataCorruptedError метод. Кстати, нет необходимости создавать перечисление CodingKeys для одного значения:

Вы также можете создать протокол, который соответствует RawRepresentable & Codable и ограничивает RawValue до Codable. Таким образом, вы можете создавать обобщенные c методы кодировщика и декодера для обоих IP-адресов:

import Network
public protocol RawRepresentableCodableProtocol: RawRepresentable & Codable
                                                 where Self.RawValue: Codable { }

public extension RawRepresentableCodableProtocol {
    init(from decoder: Decoder) throws {
        let container = try decoder.singleValueContainer()
        let rawValue = try container.decode(RawValue.self)
        guard let object = Self(rawValue: rawValue) else {
            throw DecodingError
                .dataCorruptedError(in: container, debugDescription: "Invalid rawValue data: \(rawValue)")
        }
        self = object
    }
    func encode(to encoder: Encoder) throws {
        var container = encoder.singleValueContainer()
        try container.encode(rawValue)
    }
}

Теперь мы можем расширить RawRepresentableCodableProtocol, ограничив Self до IPAddress протокол и обеспечение ошибочного инициализатора данных rawValue:

public extension RawRepresentableCodableProtocol where Self: IPAddress {
     init?(rawValue: Data) {
        guard let object = Self(rawValue, nil) else { return nil }
        self = object
    }
}

extension IPv4Address: RawRepresentableCodableProtocol { }
extension IPv6Address: RawRepresentableCodableProtocol { }

Тестирование игровой площадки:

let ipv4 = IPv4Address("1.2.33.44")!                                         // 1.2.33.44
let dataIPv4 = try JSONEncoder().encode(ipv4)                                // 10 bytes
let loadedIPv4 = try JSONDecoder().decode(IPv4Address.self, from: dataIPv4)  // 1.2.33.44

let ipv6 = IPv6Address("2001:db8::35:44")!                                   // 2001:db8::35:44
let dataIPv6 = try JSONEncoder().encode(ipv6)                                // 26 bytes
let loadedIPv6 = try JSONDecoder().decode(IPv6Address.self, from: dataIPv6)  // 2001:db8::35:44
0 голосов
/ 01 февраля 2020

Хитрость заключается в том, чтобы использовать поле IPv4Address.rawValue или IPv6Address.rawValue для получения адреса IPv4 или IPv6 в структуре Data (). Затем закодируйте данные.

При декодировании вы можете использовать данные для инициализации адреса, обрабатывая случай неудачного инициализатора.

Также можно использовать IPv6Address.description или IPv6Address.debugDescription. кодировать, но это не рекомендуется, потому что эти описания могут изменить формат в будущем (thx Martin R).

import Foundation
import Network

extension IPv6Address: Codable {
    enum CodingKeys: String, CodingKey {
        case ipv6Data
    }
    enum IPv6AddressDecodingError: Error {
        case decoding(String)
    }

    public func encode(to encoder: Encoder) throws {
        var container = encoder.container(keyedBy: CodingKeys.self)
        let addressData = self.rawValue
        try container.encode(addressData, forKey: .ipv6Data)
    }

    public init(from decoder: Decoder) throws {
        let values = try decoder.container(keyedBy: CodingKeys.self)
        let addressData = try values.decode(Data.self, forKey: .ipv6Data)
        guard let ipv6Address = IPv6Address(addressData) else {
            throw IPv6AddressDecodingError.decoding("unable to decode IPv6 address from \(values)")
        }
        self = ipv6Address
    }
}

IPv4Address по существу идентичен:

import Foundation
import Network

extension IPv4Address: Codable {
    enum CodingKeys: String, CodingKey {
        case ipv4Data
    }
    enum IPv4AddressDecodingError: Error {
        case decoding(String)
    }

    public func encode(to encoder: Encoder) throws {
        var container = encoder.container(keyedBy: CodingKeys.self)
        let addressData = self.rawValue
        try container.encode(addressData, forKey: .ipv4Data)
    }

    public init(from decoder: Decoder) throws {
        let values = try decoder.container(keyedBy: CodingKeys.self)
        let addressData = try values.decode(Data.self, forKey: .ipv4Data)
        guard let ipv4Address = IPv4Address(addressData) else {
            throw IPv4AddressDecodingError.decoding("unable to decode IPv4 address from \(values)")
        }
        self = ipv4Address
    }
}
...