diff --git a/Sources/XyoClient/Api/Archivist/ArchivistApiClient.swift b/Sources/XyoClient/Api/Archivist/ArchivistApiClient.swift index 20d6bc7..3957457 100644 --- a/Sources/XyoClient/Api/Archivist/ArchivistApiClient.swift +++ b/Sources/XyoClient/Api/Archivist/ArchivistApiClient.swift @@ -30,6 +30,60 @@ public class XyoArchivistApiClient { self.queryAccount = account ?? Account() } + public func insert( + payloads: [Payload], + completion: @escaping ([Payload]?, Error?) -> Void + ) { + do { + // Build QueryBoundWitness + let (bw, signed) = try BoundWitnessBuilder() + .payloads(payloads) + .signer(self.queryAccount) + .query(XyoArchivistApiClient.ArchivistInsertQuery) + .build() + + // Perform the request + AF.request( + self.url, + method: .post, + parameters: ModuleQueryResult(bw: bw, payloads: signed), + encoder: JSONParameterEncoder.default + ) + .validate() + .responseData { response in + switch response.result { + case .success(let responseData): + do { + // Decode the response data + let decodedResponse = try JSONDecoder().decode( + ApiResponseEnvelope.self, from: responseData + ) + + // Check if the response data matches the expected result + if decodedResponse.data?.bw.payload_hashes.count == payloads.count { + // Return the payloads array in case of success + completion(payloads, nil) + } else { + // Return an empty array if the counts don't match + completion([], nil) + } + } catch { + // Pass any decoding errors to the completion handler + completion(nil, error) + } + + case .failure(let error): + // Pass any request errors to the completion handler + completion(nil, error) + } + } + + } catch { + // Handle synchronous errors (like errors from the BoundWitnessBuilder) + completion(nil, error) + } + } + @available(iOS 15, *) public func insert(payloads: [Payload]) async throws -> [Payload] { // Build QueryBoundWitness diff --git a/Sources/XyoClient/Panel/Panel.swift b/Sources/XyoClient/Panel/Panel.swift index 7412ceb..f0341cd 100644 --- a/Sources/XyoClient/Panel/Panel.swift +++ b/Sources/XyoClient/Panel/Panel.swift @@ -6,13 +6,13 @@ public enum XyoPanelError: Error { public class XyoPanel { - public init(archivists: [XyoArchivistApiClient], witnesses: [AbstractWitness]) { + public init(archivists: [XyoArchivistApiClient], witnesses: [WitnessModuleSync]) { self._archivists = archivists self._witnesses = witnesses } public convenience init( - archive: String? = nil, apiDomain: String? = nil, witnesses: [AbstractWitness]? = nil, + archive: String? = nil, apiDomain: String? = nil, witnesses: [WitnessModuleSync]? = nil, token: String? = nil ) { let apiConfig = XyoArchivistApiConfig( @@ -23,7 +23,7 @@ public class XyoPanel { public convenience init(observe: (() -> XyoEventPayload?)?) { if observe != nil { - var witnesses = [AbstractWitness]() + var witnesses = [WitnessModuleSync]() if let observe = observe { witnesses.append(XyoEventWitness(observe)) @@ -38,21 +38,40 @@ public class XyoPanel { public typealias XyoPanelReportCallback = (([String]) -> Void) private var _archivists: [XyoArchivistApiClient] - private var _witnesses: [AbstractWitness] + private var _witnesses: [WitnessModule] private var _previous_hash: String? - + @available(iOS 15, *) public func report() async throws -> [Payload] { - let payloads = self._witnesses.map { witness in - witness.observe() - }.flatMap({ $0 }) + var payloads: [Payload] = [] + + // Collect payloads from both synchronous and asynchronous witnesses + for witness in _witnesses { + if let syncWitness = witness as? WitnessSync { + // For synchronous witnesses, call the sync `observe` method directly + payloads.append(contentsOf: syncWitness.observe()) + } else if let asyncWitness = witness as? WitnessAsync { + // For asynchronous witnesses, call the async `observe` method using `await` + do { + let asyncPayloads = try await asyncWitness.observe() + payloads.append(contentsOf: asyncPayloads) + } catch { + print("Error observing async witness: \(error)") + // Handle error as needed, possibly continue or throw + } + } + } + + // Build the BoundWitness let (bw, _) = try BoundWitnessBuilder() .payloads(payloads) - .signers(self._witnesses.map({ $0.account })) + .signers(self._witnesses.map { $0.account }) .build(_previous_hash) self._previous_hash = bw._hash + + // Collect results from archivists using async tasks var allResults: [[Payload]] = [] await withTaskGroup(of: [Payload]?.self) { group in for instance in _archivists { diff --git a/Sources/XyoClient/Witness/Basic/BasicWitness.swift b/Sources/XyoClient/Witness/Basic/BasicWitness.swift index 2fd06c7..1d733da 100644 --- a/Sources/XyoClient/Witness/Basic/BasicWitness.swift +++ b/Sources/XyoClient/Witness/Basic/BasicWitness.swift @@ -1,6 +1,6 @@ import Foundation -open class BasicWitness: AbstractWitness { +open class BasicWitness: WitnessModuleSync { public typealias TPayloadOut = Payload diff --git a/Sources/XyoClient/Witness/Event/EventWitness.swift b/Sources/XyoClient/Witness/Event/EventWitness.swift index dd59e42..6cb8c54 100644 --- a/Sources/XyoClient/Witness/Event/EventWitness.swift +++ b/Sources/XyoClient/Witness/Event/EventWitness.swift @@ -1,6 +1,6 @@ import Foundation -open class XyoEventWitness: AbstractWitness { +open class XyoEventWitness: WitnessModuleSync { public init(_ observer: @escaping ObserverClosure) { _observer = observer diff --git a/Sources/XyoClient/Witness/Location/LocationPayload.swift b/Sources/XyoClient/Witness/Location/LocationPayload.swift index 030a4fa..2345d9b 100644 --- a/Sources/XyoClient/Witness/Location/LocationPayload.swift +++ b/Sources/XyoClient/Witness/Location/LocationPayload.swift @@ -46,7 +46,7 @@ open class LocationPayload: Payload { } try container.encode(self.location.speed, forKey: .speed) try container.encode(self.location.speedAccuracy, forKey: .speedAccuracy) - try container.encode(self.location.timestamp, forKey: .timestamp) + try container.encode(Int(self.location.timestamp.timeIntervalSince1970 * 1000), forKey: .timestamp) try container.encode(self.location.verticalAccuracy, forKey: .verticalAccuracy) } diff --git a/Sources/XyoClient/Witness/Location/LocationWitness.swift b/Sources/XyoClient/Witness/Location/LocationWitness.swift new file mode 100644 index 0000000..ee32847 --- /dev/null +++ b/Sources/XyoClient/Witness/Location/LocationWitness.swift @@ -0,0 +1,21 @@ +import CoreLocation +import Foundation + +open class LocationWitness: WitnessModuleAsync { + private let locationService = LocationService() + + override open func observe(completion: @escaping ([Payload]?, Error?) -> Void) { + locationService.requestAuthorization() + locationService.requestLocation { result in + DispatchQueue.main.async { + switch result { + case .success(let location): + let payload = LocationPayload(location) + completion([payload], nil) + case .failure(let error): + completion(nil, error) + } + } + } + } +} diff --git a/Sources/XyoClient/Witness/SystemInfo/SystemInfoWitness.swift b/Sources/XyoClient/Witness/SystemInfo/SystemInfoWitness.swift index 5814dd0..381715e 100644 --- a/Sources/XyoClient/Witness/SystemInfo/SystemInfoWitness.swift +++ b/Sources/XyoClient/Witness/SystemInfo/SystemInfoWitness.swift @@ -1,6 +1,6 @@ import Foundation -open class SystemInfoWitness: AbstractWitness { +open class SystemInfoWitness: WitnessModuleSync { var allowPathMonitor: Bool diff --git a/Sources/XyoClient/Witness/Witness.swift b/Sources/XyoClient/Witness/Witness.swift index 36720a2..abc0600 100644 --- a/Sources/XyoClient/Witness/Witness.swift +++ b/Sources/XyoClient/Witness/Witness.swift @@ -1,4 +1,4 @@ -public protocol Witness { +public protocol WitnessSync { func observe() -> [Payload] } diff --git a/Sources/XyoClient/Witness/AbstractWitness.swift b/Sources/XyoClient/Witness/WitnessModule.swift similarity index 80% rename from Sources/XyoClient/Witness/AbstractWitness.swift rename to Sources/XyoClient/Witness/WitnessModule.swift index f00a659..81cca61 100644 --- a/Sources/XyoClient/Witness/AbstractWitness.swift +++ b/Sources/XyoClient/Witness/WitnessModule.swift @@ -1,12 +1,14 @@ import Foundation -open class AbstractWitness: AbstractModule, Witness { +public protocol WitnessModule: Module {} + +open class WitnessModuleSync: AbstractModule, WitnessSync, WitnessModule { open func observe() -> [Payload] { preconditionFailure("This method must be overridden") } } -open class AbstractAsyncWitness: AbstractModule, WitnessAsync { +open class WitnessModuleAsync: AbstractModule, WitnessAsync, WitnessModule { open func observe(completion: @escaping ([Payload]?, Error?) -> Void) { preconditionFailure("This method must be overridden") } diff --git a/Tests/XyoClientTests/Panel.swift b/Tests/XyoClientTests/Panel.swift index fd2b018..a1530f7 100644 --- a/Tests/XyoClientTests/Panel.swift +++ b/Tests/XyoClientTests/Panel.swift @@ -17,7 +17,7 @@ final class PanelTests: XCTestCase { let apiDomain = XyoPanel.Defaults.apiDomain let archive = XyoPanel.Defaults.apiModule let account = Account() - let witness = AbstractWitness(account: account) + let witness = WitnessModuleSync(account: account) let panel = XyoPanel(archive: archive, apiDomain: apiDomain, witnesses: [witness]) XCTAssertNotNil(account) XCTAssertNotNil(panel)