Skip to content

Commit

Permalink
Merge pull request #52 from XYOracleNetwork/feature/location-payload-…
Browse files Browse the repository at this point in the history
…serialization

Location payload serialization
  • Loading branch information
JoelBCarter authored Nov 23, 2024
2 parents ec881fb + be2e5d6 commit 32c2bc4
Show file tree
Hide file tree
Showing 6 changed files with 127 additions and 18 deletions.
15 changes: 8 additions & 7 deletions Sources/XyoClient/Witness/Location/Generic/Coordinates.swift
Original file line number Diff line number Diff line change
Expand Up @@ -42,12 +42,13 @@ struct CoordinatesStruct: Encodable {
func encode(to encoder: Encoder) throws {
var container = encoder.container(keyedBy: CodingKeys.self)

try container.encode(self.accuracy, forKey: .accuracy)
try container.encode(self.altitude, forKey: .altitude)
try container.encode(self.altitudeAccuracy, forKey: .altitudeAccuracy)
try container.encode(self.heading, forKey: .heading)
try container.encode(self.latitude, forKey: .latitude)
try container.encode(self.longitude, forKey: .longitude)
try container.encode(self.speed, forKey: .speed)
try container.encodeIfValidNumeric(self.accuracy, forKey: .accuracy)
try container.encodeIfValidNumeric(self.altitude, forKey: .altitude)
try container.encodeIfValidNumeric(self.altitudeAccuracy, forKey: .altitudeAccuracy)
try container.encodeIfValidNumeric(self.heading, forKey: .heading)
try container.encode(self.latitude, forKey: .latitude) // Always encode latitude
try container.encode(self.longitude, forKey: .longitude) // Always encode longitude
try container.encodeIfValidNumeric(self.speed, forKey: .speed)
}
}

5 changes: 2 additions & 3 deletions Sources/XyoClient/Witness/Location/LocationWitness.swift
Original file line number Diff line number Diff line change
Expand Up @@ -30,9 +30,8 @@ open class LocationWitness: WitnessModuleAsync {
switch result {
case .success(let location):
let iosLocationPayload = IosLocationPayload(location)
// let locationPayload = LocationPayload(location)
// completion([iosLocationPayload, locationPayload], nil)
completion([iosLocationPayload], nil)
let locationPayload = LocationPayload(location)
completion([iosLocationPayload, locationPayload], nil)
case .failure(let error):
completion(nil, error)
}
Expand Down
14 changes: 7 additions & 7 deletions Sources/XyoClient/Witness/Location/iOS/IosLocationPayload.swift
Original file line number Diff line number Diff line change
Expand Up @@ -31,33 +31,33 @@ open class IosLocationPayload: Payload {
var container = encoder.container(keyedBy: CodingKeys.self)
try container.encode(self.schema, forKey: .schema)

try container.encode(self.location.altitude, forKey: .altitude)
try container.encodeIfValidNumeric(self.location.altitude, forKey: .altitude)
try container.encode(
IosLocationCoordinatePayloadStruct(self.location.coordinate), forKey: .coordinate)
try container.encode(self.location.course, forKey: .course)
if #available(iOS 13.4, *) {
try container.encode(self.location.courseAccuracy, forKey: .courseAccuracy)
try container.encodeIfValidNumeric(self.location.courseAccuracy, forKey: .courseAccuracy)
}
if #available(iOS 15, *) {
try container.encode(self.location.ellipsoidalAltitude, forKey: .ellipsoidalAltitude)
try container.encodeIfValidNumeric(self.location.ellipsoidalAltitude, forKey: .ellipsoidalAltitude)
}
if let floor = self.location.floor {
try container.encode(
IosLocationFloorPayloadStruct(floor), forKey: .floor)
}
try container.encode(self.location.horizontalAccuracy, forKey: .horizontalAccuracy)
try container.encodeIfValidNumeric(self.location.horizontalAccuracy, forKey: .horizontalAccuracy)
if #available(iOS 15.0, *) {
if let sourceInformation = self.location.sourceInformation {
try container.encode(
IosLocationSourceInformationPayloadStruct(sourceInformation),
forKey: .sourceInformation)
}
}
try container.encode(self.location.speed, forKey: .speed)
try container.encode(self.location.speedAccuracy, forKey: .speedAccuracy)
try container.encodeIfValidNumeric(self.location.speed, forKey: .speed)
try container.encodeIfValidNumeric(self.location.speedAccuracy, forKey: .speedAccuracy)
try container.encode(
Int(self.location.timestamp.timeIntervalSince1970 * 1000), forKey: .timestamp)
try container.encode(self.location.verticalAccuracy, forKey: .verticalAccuracy)
try container.encodeIfValidNumeric(self.location.verticalAccuracy, forKey: .verticalAccuracy)

}
}
13 changes: 13 additions & 0 deletions Sources/XyoClient/extensions/KeyedEncodingContainer.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
extension KeyedEncodingContainer {
mutating func encodeIfValidNumeric<T>(_ value: T?, forKey key: KeyedEncodingContainer<K>.Key) throws
where T: BinaryFloatingPoint & Encodable {
if let value = value, !value.isNaN {
try encode(value, forKey: key)
}
}
mutating func encodeIfNotNil<T>(_ value: T?, forKey key: KeyedEncodingContainer<K>.Key) throws where T: Encodable {
if let value = value {
try encode(value, forKey: key)
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
import XCTest
import CoreLocation

@testable import XyoClient

class LocationPayloadTests: XCTestCase {
struct CoordinatesStruct: Encodable {
let accuracy: CLLocationAccuracy
let altitude: CLLocationDistance
let altitudeAccuracy: CLLocationDistance
let heading: CLLocationDirection
let latitude: CLLocationDegrees
let longitude: CLLocationDegrees
let speed: CLLocationSpeed
}

struct CurrentLocationStruct: Encodable {
let coords: CoordinatesStruct
let timestamp: Date
}

func testLocationPayloadEncoding() throws {
// Arrange: Create a CLLocation instance and the corresponding LocationPayload
let location = CLLocation(
coordinate: CLLocationCoordinate2D(latitude: 37.7749, longitude: -122.4194),
altitude: 15.0,
horizontalAccuracy: 5.0,
verticalAccuracy: 3.0,
course: 90.0,
speed: 2.5,
timestamp: Date(timeIntervalSince1970: 1609459200) // Jan 1, 2021
)
let payload = LocationPayload(location)

// Act: Encode the LocationPayload instance into JSON
let encoder = JSONEncoder()
encoder.outputFormatting = [.sortedKeys, .prettyPrinted] // Consistent output for tests
let jsonData = try encoder.encode(payload)

// Assert: Verify the serialized JSON matches expectations
let jsonString = String(data: jsonData, encoding: .utf8)!
let expectedJSON = """
{
"currentLocation" : {
"coords" : {
"accuracy" : 5,
"altitude" : 15,
"altitudeAccuracy" : 15,
"heading" : 90,
"latitude" : 37.7749,
"longitude" : -122.4194,
"speed" : 2.5
},
"timestamp" : 1609459200000
},
"schema" : "network.xyo.location"
}
"""
XCTAssertEqual(jsonString, expectedJSON)
}

func testLocationPayloadEncodingHandlesNilValues() throws {
// Arrange: Create a CLLocation instance with some properties unset
let location = CLLocation(
coordinate: CLLocationCoordinate2D(latitude: 0.0, longitude: 0.0),
altitude: CLLocationDistance.nan,
horizontalAccuracy: CLLocationAccuracy.nan,
verticalAccuracy: CLLocationAccuracy.nan,
course: CLLocationDirection.nan,
speed: CLLocationSpeed.nan,
timestamp: Date(timeIntervalSince1970: 0)
)
let payload = LocationPayload(location)

// Act: Encode the LocationPayload instance into JSON
let encoder = JSONEncoder()
encoder.outputFormatting = [.sortedKeys, .prettyPrinted]
let jsonData = try encoder.encode(payload)

// Assert: Verify the serialized JSON handles NaN values gracefully (e.g., omitted or replaced)
let jsonString = String(data: jsonData, encoding: .utf8)!
let expectedJSON = """
{
"currentLocation" : {
"coords" : {
"latitude" : 0,
"longitude" : 0
},
"timestamp" : 0
},
"schema" : "network.xyo.location"
}
"""
XCTAssertEqual(jsonString, expectedJSON)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ final class LocationWitnessTests: XCTestCase {
let iosLocationPayload = try XCTUnwrap(
results.compactMap { $0 as? IosLocationPayload }.first, "Missing iOS location payload.")
XCTAssertEqual(iosLocationPayload.schema, IosLocationPayload.schema)
XCTAssertEqual(iosLocationPayload.location.coordinate.latitude, lattitiude)
XCTAssertEqual(iosLocationPayload.location.coordinate.latitude, latitude)
XCTAssertEqual(iosLocationPayload.location.coordinate.longitude, longitude)

}
Expand Down

0 comments on commit 32c2bc4

Please sign in to comment.