import Foundation import CryptoKit final class Client { // TODO: Use or delete private let delegate = NeverCacheDelegate() init() {} func deviceStatus(authToken: Data, server: String) async -> ClientState { await send(path: .getDeviceStatus, server: server, data: authToken).state } func sendMessageOverLocalNetwork(_ message: Message, server: String) async -> (state: ClientState, response: Message?) { let data = message.encoded.hexEncoded guard let url = URL(string: server + "message?m=\(data)") else { return (.internalError("Invalid server url"), nil) } var request = URLRequest(url: url) request.httpMethod = "POST" return await requestAndDecode(request) } func send(_ message: Message, server: String, authToken: Data) async -> (state: ClientState, response: Message?) { let serverMessage = ServerMessage(authToken: authToken, message: message) return await send(path: .postMessage, server: server, data: serverMessage.encoded) } private func send(path: RouteAPI, server: String, data: Data) async -> (state: ClientState, response: Message?) { guard let url = URL(string: server) else { return (.internalError("Invalid server url"), nil) } let fullUrl = url.appendingPathComponent(path.rawValue) return await send(to: fullUrl, data: data) } private func send(to url: URL, data: Data) async -> (state: ClientState, response: Message?) { var request = URLRequest(url: url) request.httpBody = data request.httpMethod = "POST" return await requestAndDecode(request) } private func requestAndDecode(_ request: URLRequest) async -> (state: ClientState, response: Message?) { guard let data = await fulfill(request) else { return (.deviceNotAvailable(.serverNotReached), nil) } guard let byte = data.first else { return (.internalError("Empty response"), nil) } guard let status = MessageResult(rawValue: byte) else { return (.internalError("Invalid message response: \(byte)"), nil) } let result = ClientState(keyResult: status) guard data.count == Message.length + 1 else { if data.count != 1 { print("Device response with only \(data.count) bytes") } return (result, nil) } let messageData = Array(data.advanced(by: 1)) let message = Message(decodeFrom: messageData) return (result, message) } private func fulfill(_ request: URLRequest) async -> Data? { do { let (data, response) = try await URLSession.shared.data(for: request) guard let code = (response as? HTTPURLResponse)?.statusCode else { print("No response from server") return nil } guard code == 200 else { print("Invalid server response \(code)") return nil } return data } catch { print("Request failed: \(error)") return nil } } } class NeverCacheDelegate: NSObject, NSURLConnectionDataDelegate { func connection(_ connection: NSURLConnection, willCacheResponse cachedResponse: CachedURLResponse) -> CachedURLResponse? { return nil } }