import SwiftUI /// 登录态真相源(UI 订阅)。token 实际存 Keychain,这里持内存镜像 + user 资料。 @MainActor @Observable final class SessionStore { var user: UserDTO? private(set) var isLoggedIn: Bool @ObservationIgnored private var authExpireObserver: NSObjectProtocol? init() { // 启动即判断:Keychain 有 access token 视为已登录(资料由 refreshProfile 拉) isLoggedIn = KeychainStore.get(.accessToken) != nil authExpireObserver = NotificationCenter.default.addObserver( forName: .authDidExpire, object: nil, queue: .main ) { [weak self] _ in MainActor.assumeIsolated { self?.clearLocal() } } } deinit { if let authExpireObserver { NotificationCenter.default.removeObserver(authExpireObserver) } } /// 登录成功:存 token + user。 func loginSucceeded(_ resp: TokenWithUserDTO) async { await AuthTokens.shared.setTokens(access: resp.accessToken, refresh: resp.refreshToken) user = resp.user isLoggedIn = true } /// 资料更新后(改昵称/头像)同步内存镜像。 func updateUser(_ user: UserDTO) { self.user = user } /// 拉最新资料(进入「我的」时刷新)。 func refreshProfile() async { guard isLoggedIn else { return } if let u = try? await AuthService().me() { user = u } } /// 主动退出登录。 func logout() async { _ = try? await AuthService().logout() // 占位接口,失败也无所谓 await AuthTokens.shared.clear() clearLocal() } private func clearLocal() { user = nil isLoggedIn = false } }