import Foundation import CryptoKit struct Client { let server: URL private let delegate = NeverCacheDelegate() init(server: URL) { self.server = server } private enum RequestReponse: Error { case requestFailed case unknownResponse case success(UInt8) } func deviceStatus() async throws -> ClientState { let url = server.appendingPathComponent("status") let request = URLRequest(url: url, cachePolicy: .reloadIgnoringLocalCacheData) let response = await integerReponse(to: request) switch response { case .requestFailed: return .statusRequestFailed case .unknownResponse: return .unknownDeviceStatus case .success(let int): switch int { case 0: return .deviceDisconnected case 1: return .deviceConnected default: print("Unexpected device status '\(int)'") return .unknownDeviceStatus } } } func keyResponse(key: SymmetricKey, id: Int) async throws -> ClientState { let url = server.appendingPathComponent("key/\(id)") var request = URLRequest(url: url) request.httpBody = key.data request.httpMethod = "POST" let response = await integerReponse(to: request) switch response { case .requestFailed: return .statusRequestFailed case .unknownResponse: return .unknownDeviceStatus case .success(let int): guard let status = KeyResult(rawValue: int) else { print("Invalid key response: \(int)") return .unknownDeviceStatus } return ClientState(keyResult: status) } } private func fulfill(_ request: URLRequest) async -> Result { 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 .failure(.requestFailed) } guard code == 200 else { print("Invalid server response \(code)") return .failure(.requestFailed) } return .success(data) } catch { print("Request failed: \(error)") return .failure(.requestFailed) } } private func integerReponse(to request: URLRequest) async -> RequestReponse { let response = await fulfill(request) switch response { case .failure(let cause): return cause case .success(let data): guard let string = String(data: data, encoding: .utf8) else { print("Unexpected device status data: \([UInt8](data))") return .unknownResponse } guard let int = UInt8(string) else { print("Unexpected device status '\(string)'") return .unknownResponse } return .success(int) } } } class NeverCacheDelegate: NSObject, NSURLConnectionDataDelegate { func connection(_ connection: NSURLConnection, willCacheResponse cachedResponse: CachedURLResponse) -> CachedURLResponse? { return nil } }