Essentially I would like to convert an array of the following custom struct into data for data for easier saving in CoreData as Binary Data. How can the following be converted into data to then be ready to decode back:
Custom Struct
struct Place: Codable, Identifiable {
var id = UUID()
var coordinate: Coordinate
struct Coordinate: Codable {
let latitude: Double
let longitude: Double
func locationCoordinate() -> CLLocationCoordinate2D {
return CLLocationCoordinate2D(latitude: self.latitude,
longitude: self.longitude)
}
}
}
Adding to Custom Struct
var mapAddresses = [Place]()
Task {
mapAddresses.append(Place(coordinate: try await getCoordinate(from:
post.location)))
}
The issue I am having is converting the array mapAddresses with the custom structure into Binary Data, that can then be decoded back into the custom array.
CodePudding user response:
In comments, your phrasing indicates that you want a binary encoding. I'll get to that, but first let's just encode/decode JSON.
Given
struct Place: Codable {...}
let mapAddresses: [Place] = ...
The following code encodes mapAddresses
, then immediately decodes the resulting Data
:
guard let data = try? JSONEncoder().encode(mapAddresses) else {
fatalError("Failed to encode")
}
guard let decodedMapAddresses =
try? JSONDecoder().decode([Place].self, from: data)
else { fatalError("Failed to decode") }
Whether data
is binary is a matter of interpretation. data
will just contain the text of the JSON representing mapAddresses
. Technically text is binary, but then again so is anything else in a computer. What we normally mean is non-text, that is some more compact or more directly machine-friendly encoding that is not so human-friendly. JSONEncoder
doesn't provide such a facility, but PropertyListEncoder
does, via it's outputFormat
property. To use it:
let encoder = PropertlyListEncoder()
encoder.outputFormat = .binary
guard let data = encoder.encode(mapAddresses) else {
fatalError("Failed to encode")
}
guard let decodedMapAddresses =
try? PropertyListDecoder().decode([Place].self, from: data)
else { fatalError("Failed to decode") }
Note there is no need specify that it's binary in the decoder, because PropertyListDecoder
knows how to detect that.
CodePudding user response:
Like others have said in the comments, you don't have a fully-formed question yet. But first, you don't need Coordinate
. Just make CLLocationCoordinate2D
Codable, like Apple should have.
import struct CoreLocation.CLLocationCoordinate2D
public extension CLLocationCoordinate2D {
enum CodingKey: Swift.CodingKey {
case latitude
case longitude
}
}
extension CLLocationCoordinate2D: Decodable {
public init(from decoder: Decoder) throws {
try self.init(
Self.init, (CodingKey.latitude, .longitude),
decoder: decoder
)
}
}
extension CLLocationCoordinate2D: Encodable {
public func encode(to encoder: Encoder) throws {
var container = encoder.container(keyedBy: CodingKey.self)
try container.encode(latitude, forKey: .latitude)
try container.encode(longitude, forKey: .longitude)
}
}
public extension Decodable {
/// Initialize using 2 keyed, decoded arguments.
/// - Parameters:
/// - init: An initializer (or factory function) whose arguments are the decoded values.
/// - keys: `CodingKey` instances, matching the arguments.
init<
Parameter0: Decodable, Parameter1: Decodable, Key: CodingKey
>(
_ init: (Parameter0, Parameter1) -> Self,
_ keys: (Key, Key),
decoder: Decoder
) throws {
let container = try decoder.container(keyedBy: Key.self)
self = try `init`(
container.decode(forKey: keys.0),
container.decode(forKey: keys.1)
)
}
}
public extension KeyedDecodingContainerProtocol {
/// Decode, relying on the return type, to avoid having to explicitly use a metatype argument.
func decode<Decodable: Swift.Decodable>(forKey key: Key) throws -> Decodable {
try decode(Decodable.self, forKey: key)
}
}