learning_ai_clock/ios/ChronoMind/Shared/Cloud/AuthService.swift
saravanakumardb1 d1b4534b22 refactor(ios): migrate Cloud/ files to ByteLystPlatformSDK — eliminate duplicated platform code
Replaced standalone implementations with thin wrappers over shared SDK:
- KeychainHelper → delegates to BLKeychain (53→27 lines)
- TelemetryService → delegates to BLTelemetryClient (139→55 lines)
- FeatureFlagService → delegates to BLFeatureFlagClient (72→39 lines)
- AuthService → delegates to BLAuthClient (359→171 lines)
- project.yml → added ByteLystPlatformSDK local package dependency

Total: 623 lines of duplicated code → 292 lines of thin wrappers.
PlatformSyncManager kept as-is (product-specific timer DTOs).
All existing call-site APIs preserved — zero breaking changes.
2026-02-28 22:12:37 -08:00

171 lines
4.9 KiB
Swift

// Auth Service
// Thin wrapper over ByteLystPlatformSDK's BLAuthClient.
// Keeps existing call-site API + ChronoMind-specific sync wiring.
// AuthUser = BLAuthUser (re-exported from SDK).
import Foundation
import SwiftUI
import ByteLystPlatformSDK
typealias AuthUser = BLAuthUser
enum CMAuthState {
case loading
case loggedOut
case loggedIn(AuthUser)
case error(String)
}
@MainActor
final class CMAuthService: ObservableObject {
static let shared = CMAuthService()
@Published var state: CMAuthState = .loading
@AppStorage("cm_user_email") private var userEmail = ""
@AppStorage("cm_user_name") private var userName = ""
@AppStorage("cm_user_plan") private var userPlan = "free"
private let authClient: BLAuthClient
private let platformClient: BLPlatformClient
private init() {
let config = BLPlatformConfig.fromInfoPlist(
productId: "chronomind",
defaultBaseURL: "https://api.chronomind.app",
bundleId: "com.saravana.chronomind",
appGroupId: "group.com.chronomind.shared"
)
platformClient = BLPlatformClient(config: config)
authClient = BLAuthClient(config: config, client: platformClient)
// Wire token updates sync manager
authClient.onTokensUpdated = { [weak self] token in
Task { @MainActor in
self?.wireSyncToken(token)
}
}
checkExistingSession()
}
private func checkExistingSession() {
if authClient.isAuthenticated, !userEmail.isEmpty {
state = .loggedIn(AuthUser(id: "", email: userEmail, displayName: userName, plan: userPlan))
wireSyncToken(authClient.accessToken)
Task { await fetchCurrentUser() }
} else if authClient.isAuthenticated {
Task { await fetchCurrentUser() }
} else {
state = .loggedOut
}
}
// MARK: - Public API
func login(email: String, password: String) async {
state = .loading
do {
let user = try await authClient.login(email: email, password: password)
saveUserInfo(user)
state = .loggedIn(user)
} catch {
state = .error("Invalid email or password")
}
}
func register(name: String, email: String, password: String) async {
state = .loading
do {
let user = try await authClient.register(displayName: name, email: email, password: password)
saveUserInfo(user)
state = .loggedIn(user)
} catch {
state = .error("Registration failed")
}
}
func logout() {
authClient.logout()
userEmail = ""
userName = ""
state = .loggedOut
}
func forgotPassword(email: String) async -> String? {
do {
try await authClient.forgotPassword(email: email)
return nil
} catch {
return error.localizedDescription
}
}
func changePassword(currentPassword: String, newPassword: String) async -> String? {
do {
try await authClient.changePassword(currentPassword: currentPassword, newPassword: newPassword)
return nil
} catch {
return error.localizedDescription
}
}
func deleteAccount(password: String) async -> String? {
do {
try await authClient.deleteAccount(password: password)
userEmail = ""
userName = ""
state = .loggedOut
return nil
} catch {
return error.localizedDescription
}
}
var isLoggedIn: Bool {
if case .loggedIn = state { return true }
return false
}
var currentUser: AuthUser? {
if case .loggedIn(let user) = state { return user }
return nil
}
func getAccessToken() -> String? {
authClient.accessToken
}
// MARK: - Token Refresh
func refreshAccessToken() async -> Bool {
let ok = await authClient.refreshAccessToken()
if !ok { logout() }
return ok
}
func fetchCurrentUser() async {
do {
let user = try await authClient.getMe()
saveUserInfo(user)
state = .loggedIn(user)
} catch {
if let netErr = error as? BLNetworkError, netErr.statusCode == 401 {
let ok = await refreshAccessToken()
if ok { await fetchCurrentUser() } else { state = .loggedOut }
}
}
}
// MARK: - Helpers
private func wireSyncToken(_ token: String?) {
PlatformSyncManager.shared.setAuthToken(token)
}
private func saveUserInfo(_ user: AuthUser) {
userEmail = user.email
userName = user.displayName
userPlan = user.plan
}
}