Как можно кодировать массив CGPoint в Swift

#swift #codable #cgpoint

#swift #codable #cgpoint

Вопрос:

Привет, я нашел хорошее решение без массива отсюда, работает нормально

 @propertyWrapper
struct EncodablePoint: Encodable {
    var wrappedValue: CGPoint
    enum CodingKeys: CodingKey {
        case x
        case y
    }
    func encode(to encoder: Encoder) throws {
        var c = encoder.container(keyedBy: CodingKeys.self)
        try c.encode(wrappedValue.x, forKey: .x)
        try c.encode(wrappedValue.y, forKey: .y)
    }
}
//usage:
struct Foo: Encodable {
    @EncodablePoint var pt: CGPoint = CGPoint(x: 123, y: 456)
    var other = "zxcv"
}

let foo = Foo()
let encoded = try! JSONEncoder().encode(foo)
print(String(bytes: encoded, encoding: .utf8) ?? "nil") // {"pt":{"x":123,"y":456},"other":"zxcv"}
  

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

 @propertyWrapper
struct EncodablePointsArray: Encodable {
    var wrappedValue: [CGPoint]
    enum CodingKeys: CodingKey {
        ?????
    }
    func encode(to encoder: Encoder) throws {
        ??????
    }
}


struct Foo2: Encodable {
    @EncodablePointsArray var pt: [CGPoint] = [CGPoint]
    var other = "zxcv"
}

let foo2 = Foo2()
let encoded = try! JSONEncoder().encode(foo2)
print(String(bytes: encoded, encoding: .utf8) ?? "nil")
// I wold like output like : {"ArrayPoints“:[{„x“:1.1,“y“:1.2},{„x“:2.1,“y“:3.4}]},"other":"zxcv"}
  

Ответ №1:

Вы уходите от устаревшей информации. [CGPoint] теперь Codable без каких-либо расширений.

 struct Foo2: Encodable {
  var pt: [CGPoint] = []
  var other = "zxcv"
}
  

Если вам действительно нужна оболочка, пожалуйста, опубликуйте код, который ее требует. Ваш вопрос предполагает, что вам не нужна оболочка.

Комментарии:

1. Хотя это правда, это не то, что запрашивает OP. Результатом кодирования Foo2 будет следующее {"pt":[[1.1,1.2],[2.1,3.4]],"other":"zxcv"} . Очевидно, что OP запрашивает что-то вроде этого {"pt":[{"x":1.1,"y":1.2},{"x":2.1,"y":3.4}]},"other":"zxcv"}

Ответ №2:

EncodablePoint действует здесь как оболочка для CGPoint , поэтому с точки зрения клиента EncodablePoint это новый тип со CGPoint свойством.

При использовании в качестве оболочки свойства он действует как array of CGPoint . Но после кодирования он превращает его в пользовательский объект с x y координатами и .

В зависимости от результатов, которых вы хотите достичь после декодирования такого объекта, здесь есть два варианта.

Кодирование массива EncodablePoint

Давайте создадим оболочку свойств следующим образом

 @propertyWrapper
struct EncodablePointArray: Encodable {
    var wrappedValue: [EncodablePoint]
}
  

Затем клиент оболочки должен создавать экземпляры EncodablePoint

 struct Points: Encodable {
    @EncodablePointArray var pt: [EncodablePoint] = [
        EncodablePoint(wrappedValue: CGPoint(x: 123, y: 456)),
        EncodablePoint(wrappedValue: CGPoint(x: 789, y: 123)),
        EncodablePoint(wrappedValue: CGPoint(x: 456, y: 789))
    ]
    var other = "zxcv"
}
  

Декодирование, которое даст вам желаемые результаты

 {"pt":{"values":[{"x":123,"y":456},{"x":789,"y":123},{"x":456,"y":789}]},"other":"zxcv"}
  

Но я думаю, вы хотите использовать массив CGPoint , так что…

Кодирование массива CGPoint

Если вы хотите использовать CGPoint s напрямую, как это

 struct Points: Encodable {
    @EncodablePointArray var pt: [CGPoint] = [
        CGPoint(x: 123, y: 456),
        CGPoint(x: 789, y: 123),
        CGPoint(x: 456, y: 789)
    ]
    var other = "zxcv"
}
  

Вы должны создать свою оболочку с CGPoint помощью s

 @propertyWrapper
struct EncodablePointArray: Encodable {
    var wrappedValue: [CGPoint]
}
  

Но декодированная строка будет другой:

 {"pt":{"values":[[123,456],[789,123],[456,789]]},"other":"zxcv"}
  

Ответ №3:

Использование функций unkeyedContainer() и nestedContainer(keyedBy:) сделает свое дело:

 @propertyWrapper
struct EncodablePointsArray: Encodable {
    var wrappedValue: [CGPoint]
    
    enum CodingKeys: CodingKey {
        case x
        case y
    }
    
    func encode(to encoder: Encoder) throws {
        var unkeyedContainer = encoder.unkeyedContainer()
        try wrappedValue.forEach {
            var container = unkeyedContainer.nestedContainer(keyedBy: CodingKeys.self)
            try container.encode($0.x, forKey: .x)
            try container.encode($0.y, forKey: .y)
        }
    }
}
  

Используя ваш пример:

 struct Foo2: Encodable {
    @EncodablePointsArray var pt: [CGPoint] = [CGPoint(x: 1.1, y: 1.2), CGPoint(x: 2.1, y: 3.4)]
    var other = "zxcv"
}

let foo2 = Foo2()
do {
    let encoded = try JSONEncoder().encode(foo2)
    print(String(bytes: encoded, encoding: .utf8) ?? "nil")
} catch {
    print(error)
}
  

В результате это приведет к следующему:

 {
    "pt": [
        {
            "x": 1.1000000000000001,
            "y": 1.2
        },
        {
            "x": 2.1000000000000001,
            "y": 3.3999999999999999
        }
    ],
    "other": "zxcv"
}