banyaro-ios/BanYaroGo/Auth/AuthSession.swift
rene 546386dcbd Hunde-Cache leeren bei Logout/401 — kein Durchschimmern auf neuen User
Bug: ActiveDogStore cached dogs + activeDogId (UserDefaults). Beim
Login mit einem neuen Account waren die Hunde des vorigen Users
weiter zu sehen, weil HeimView nur loadDogs() ruft wenn dogs.isEmpty.

Fix:
- ActiveDogStore hat jetzt reset() (dogs=[], activeDogId=0,
  UserDefaults gelöscht).
- ActiveDogStore hört auf .userDidLogout und auf .apiUnauthorized,
  beides löst reset() aus.
- AuthSession.logout() postet jetzt .userDidLogout.
- Nach Login holt HeimView's .task automatisch die neuen Hunde
  (dogs.isEmpty → loadDogs).
2026-05-30 18:25:23 +02:00

78 lines
2.4 KiB
Swift

import Foundation
import Observation
@Observable
@MainActor
final class AuthSession {
var token: String?
var userName: String?
var isPremium: Bool = false
var profile: UserProfile?
var isLoggingIn: Bool = false
var errorMessage: String?
private let tokenKey = "by_token"
init() {
if let savedToken = KeychainStore.read(tokenKey) {
token = savedToken
APIClient.shared.token = savedToken
}
NotificationCenter.default.addObserver(
forName: .apiUnauthorized,
object: nil,
queue: .main
) { [weak self] _ in
Task { @MainActor in self?.logout() }
}
}
var isLoggedIn: Bool { token != nil }
func login(email: String, password: String) async {
isLoggingIn = true
errorMessage = nil
defer { isLoggingIn = false }
do {
let response: LoginResponse = try await APIClient.shared.post(
"/api/auth/login",
body: LoginRequest(email: email, password: password)
)
KeychainStore.save(response.token, for: tokenKey)
APIClient.shared.token = response.token
self.token = response.token
self.userName = response.name
self.isPremium = response.isPremium
} catch {
self.errorMessage = error.localizedDescription
}
}
func logout() {
KeychainStore.delete(tokenKey)
APIClient.shared.token = nil
token = nil
userName = nil
isPremium = false
profile = nil
// Andere Stores benachrichtigen, damit sie ihren User-Cache leeren.
NotificationCenter.default.post(name: .userDidLogout, object: nil)
}
/// Fetches the full user profile from /api/auth/me. Called after login and
/// when MainTabView appears, so admin/founder/role info shows up.
func loadProfile() async {
guard token != nil else { return }
do {
let me: UserProfile = try await APIClient.shared.get("/api/auth/me")
self.profile = me
if let premium = me.isPremium {
self.isPremium = premium
}
} catch {
// Profil-Refresh ist non-critical; Settings zeigt sonst nur die Basics.
// Loggen ist trotzdem nützlich, damit Decode-Fehler nicht stillschweigend untergehen.
print("loadProfile failed: \(error)")
}
}
}