Fix Swift 6 actor isolation in MarksAuth — use UserDefaults UUID
All checks were successful
CI / build-and-deploy (push) Successful in 22s
All checks were successful
CI / build-and-deploy (push) Successful in 22s
UIDevice.current is MainActor-isolated; replaced with a stable UUID persisted in UserDefaults to avoid the async access. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -1,10 +1,9 @@
|
|||||||
import Foundation
|
import Foundation
|
||||||
import UIKit
|
|
||||||
|
|
||||||
private let backendBaseURL = "https://chatai-realtime-proxy-production.up.railway.app"
|
private let backendBaseURL = "https://chatai-realtime-proxy-production.up.railway.app"
|
||||||
|
private let deviceIdKey = "marksDeviceId"
|
||||||
private let tokenKey = "marksJWT"
|
private let tokenKey = "marksJWT"
|
||||||
private let tokenExpiryKey = "marksJWTExpiry"
|
private let tokenExpiryKey = "marksJWTExpiry"
|
||||||
|
|
||||||
enum MarksAuth {
|
enum MarksAuth {
|
||||||
static var baseURL: String { backendBaseURL }
|
static var baseURL: String { backendBaseURL }
|
||||||
@@ -15,6 +14,13 @@ enum MarksAuth {
|
|||||||
return try await register()
|
return try await register()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static func deviceId() -> String {
|
||||||
|
if let existing = UserDefaults.standard.string(forKey: deviceIdKey) { return existing }
|
||||||
|
let new = UUID().uuidString
|
||||||
|
UserDefaults.standard.set(new, forKey: deviceIdKey)
|
||||||
|
return new
|
||||||
|
}
|
||||||
|
|
||||||
private static func cached() -> String? {
|
private static func cached() -> String? {
|
||||||
guard
|
guard
|
||||||
let token = UserDefaults.standard.string(forKey: tokenKey),
|
let token = UserDefaults.standard.string(forKey: tokenKey),
|
||||||
@@ -28,22 +34,19 @@ enum MarksAuth {
|
|||||||
guard let url = URL(string: "\(backendBaseURL)/api/auth/register") else {
|
guard let url = URL(string: "\(backendBaseURL)/api/auth/register") else {
|
||||||
throw URLError(.badURL)
|
throw URLError(.badURL)
|
||||||
}
|
}
|
||||||
let deviceId = UIDevice.current.identifierForVendor?.uuidString ?? UUID().uuidString
|
let id = deviceId()
|
||||||
var req = URLRequest(url: url)
|
var req = URLRequest(url: url)
|
||||||
req.httpMethod = "POST"
|
req.httpMethod = "POST"
|
||||||
req.setValue("application/json", forHTTPHeaderField: "Content-Type")
|
req.setValue("application/json", forHTTPHeaderField: "Content-Type")
|
||||||
req.httpBody = try JSONSerialization.data(withJSONObject: ["device_id": deviceId])
|
req.httpBody = try JSONSerialization.data(withJSONObject: ["device_id": id])
|
||||||
let (data, response) = try await URLSession.shared.data(for: req)
|
let (data, response) = try await URLSession.shared.data(for: req)
|
||||||
let status = (response as? HTTPURLResponse)?.statusCode ?? 0
|
let status = (response as? HTTPURLResponse)?.statusCode ?? 0
|
||||||
guard (200...299).contains(status) else {
|
guard (200...299).contains(status) else { throw APIError.badStatus(status) }
|
||||||
throw APIError.badStatus(status)
|
|
||||||
}
|
|
||||||
struct Resp: Decodable { let access_token: String; let expires_in: Int }
|
struct Resp: Decodable { let access_token: String; let expires_in: Int }
|
||||||
let resp = try JSONDecoder().decode(Resp.self, from: data)
|
let resp = try JSONDecoder().decode(Resp.self, from: data)
|
||||||
UserDefaults.standard.set(resp.access_token, forKey: tokenKey)
|
UserDefaults.standard.set(resp.access_token, forKey: tokenKey)
|
||||||
let expiry = Date().addingTimeInterval(TimeInterval(resp.expires_in))
|
UserDefaults.standard.set(Date().addingTimeInterval(TimeInterval(resp.expires_in)), forKey: tokenExpiryKey)
|
||||||
UserDefaults.standard.set(expiry, forKey: tokenExpiryKey)
|
print("[Auth] Registered \(id.prefix(8))…")
|
||||||
print("[Auth] Registered device \(deviceId.prefix(8))…")
|
|
||||||
return resp.access_token
|
return resp.access_token
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user