ServerConfig parses https base URL and derives wss livekit URL; TokenClient POSTs /token with clean 503/offline error states; TLSPinning holds optional cert SHA-384 pins (empty = system trust); Settings gains a Connection test screen proving the endpoint works.
70 lines
2.1 KiB
Swift
70 lines
2.1 KiB
Swift
import Foundation
|
|
|
|
struct TokenResponse: Decodable, Equatable {
|
|
let token: String
|
|
let room: String
|
|
let user: User
|
|
|
|
struct User: Decodable, Equatable {
|
|
let id: String
|
|
let displayName: String?
|
|
|
|
enum CodingKeys: String, CodingKey {
|
|
case id
|
|
case displayName = "display_name"
|
|
}
|
|
}
|
|
}
|
|
|
|
enum TokenError: LocalizedError {
|
|
case invalidServerURL(String)
|
|
case httpStatus(Int, String)
|
|
case decodeFailed
|
|
case network(Error)
|
|
|
|
var errorDescription: String? {
|
|
switch self {
|
|
case .invalidServerURL(let raw):
|
|
return "Invalid server URL: \(raw)"
|
|
case .httpStatus(let code, let detail):
|
|
if code == 503 {
|
|
return "Server is starting up or the user file is missing (HTTP 503). Check the deployment on the server. \(detail)"
|
|
}
|
|
return "Token request failed with HTTP \(code). \(detail)"
|
|
case .decodeFailed:
|
|
return "Unexpected response from the token endpoint."
|
|
case .network(let error):
|
|
return "Could not reach the server: \(error.localizedDescription)"
|
|
}
|
|
}
|
|
}
|
|
|
|
final class TokenClient {
|
|
let config: ServerConfig
|
|
private let session: URLSession
|
|
|
|
init(config: ServerConfig, session: URLSession = .shared) {
|
|
self.config = config
|
|
self.session = session
|
|
}
|
|
|
|
func fetchToken() async throws -> TokenResponse {
|
|
var request = URLRequest(url: config.baseURL.appendingPathComponent("token"))
|
|
request.httpMethod = "POST"
|
|
request.timeoutInterval = 10
|
|
let (data, response) = try await session.data(for: request)
|
|
guard let http = response as? HTTPURLResponse else {
|
|
throw TokenError.decodeFailed
|
|
}
|
|
guard (200..<300).contains(http.statusCode) else {
|
|
let detail = String(data: data, encoding: .utf8) ?? ""
|
|
throw TokenError.httpStatus(http.statusCode, detail)
|
|
}
|
|
do {
|
|
return try JSONDecoder().decode(TokenResponse.self, from: data)
|
|
} catch {
|
|
throw TokenError.decodeFailed
|
|
}
|
|
}
|
|
}
|