Sesame-iOS/Sesame/Client.swift

108 lines
3.6 KiB
Swift
Raw Normal View History

2022-01-29 18:59:42 +01:00
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
2022-04-09 17:43:33 +02:00
case unknownResponseData(Data)
case unknownResponseString(String)
2022-01-29 18:59:42 +01:00
case success(UInt8)
}
2022-04-09 17:43:33 +02:00
func deviceStatus() async -> ClientState {
2022-04-13 14:55:22 +02:00
let url = server.appendingPathComponent(RouteAPI.getDeviceStatus.rawValue)
2022-01-29 18:59:42 +01:00
let request = URLRequest(url: url, cachePolicy: .reloadIgnoringLocalCacheData)
let response = await integerReponse(to: request)
switch response {
case .requestFailed:
2022-04-09 17:43:33 +02:00
return .deviceNotAvailable(.serverNotReached)
case .unknownResponseData(let data):
return .internalError("Unknown status (\(data.count) bytes)")
case .unknownResponseString(let string):
return .internalError("Unknown status (\(string.prefix(15)))")
2022-01-29 18:59:42 +01:00
case .success(let int):
switch int {
case 0:
2022-04-09 17:43:33 +02:00
return .deviceNotAvailable(.deviceDisconnected)
2022-01-29 18:59:42 +01:00
case 1:
2022-04-09 17:43:33 +02:00
return .ready
2022-01-29 18:59:42 +01:00
default:
2022-04-09 17:43:33 +02:00
return .internalError("Invalid status: \(int)")
2022-01-29 18:59:42 +01:00
}
}
}
2022-04-09 17:43:33 +02:00
func send(_ message: Message) async throws -> (state: ClientState, response: Message?) {
2022-04-13 14:55:22 +02:00
let url = server.appendingPathComponent(RouteAPI.postMessage.rawValue)
2022-01-29 18:59:42 +01:00
var request = URLRequest(url: url)
2022-04-09 17:43:33 +02:00
request.httpBody = message.encoded
2022-01-29 18:59:42 +01:00
request.httpMethod = "POST"
2022-04-09 17:43:33 +02:00
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)
2022-01-29 18:59:42 +01:00
}
2022-04-09 17:43:33 +02:00
let result = ClientState(keyResult: status)
guard data.count == Message.length + 1 else {
return (result, nil)
}
let messageData = Array(data.advanced(by: 1))
let message = Message(decodeFrom: messageData)
return (result, message)
2022-01-29 18:59:42 +01:00
}
2022-04-09 17:43:33 +02:00
private func fulfill(_ request: URLRequest) async -> Data? {
2022-01-29 18:59:42 +01:00
do {
let (data, response) = try await URLSession.shared.data(for: request)
guard let code = (response as? HTTPURLResponse)?.statusCode else {
print("No response from server")
2022-04-09 17:43:33 +02:00
return nil
2022-01-29 18:59:42 +01:00
}
guard code == 200 else {
print("Invalid server response \(code)")
2022-04-09 17:43:33 +02:00
return nil
2022-01-29 18:59:42 +01:00
}
2022-04-09 17:43:33 +02:00
return data
2022-01-29 18:59:42 +01:00
} catch {
print("Request failed: \(error)")
2022-04-09 17:43:33 +02:00
return nil
2022-01-29 18:59:42 +01:00
}
}
private func integerReponse(to request: URLRequest) async -> RequestReponse {
2022-04-09 17:43:33 +02:00
guard let data = await fulfill(request) else {
return .requestFailed
}
guard let string = String(data: data, encoding: .utf8) else {
print("Unexpected device status data: \([UInt8](data))")
return .unknownResponseData(data)
}
guard let int = UInt8(string) else {
print("Unexpected device status '\(string)'")
return .unknownResponseString(string)
2022-01-29 18:59:42 +01:00
}
2022-04-09 17:43:33 +02:00
return .success(int)
2022-01-29 18:59:42 +01:00
}
}
class NeverCacheDelegate: NSObject, NSURLConnectionDataDelegate {
func connection(_ connection: NSURLConnection, willCacheResponse cachedResponse: CachedURLResponse) -> CachedURLResponse? {
return nil
}
}