Files
MatchLiveTv/native/ios/MatchLiveTv/Core/TokenStore.swift
T
eminuxandCursor 5b723bf844 Aggiunge annunci in-app/sito, contatti e migliora UI copertina sul portale.
Include admin/API/banner nativi, form contatti e reset copertina standard con layout edit più ampio.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-08-20 15:30:49 +02:00

126 lines
4.2 KiB
Swift

import Foundation
import Security
struct StoredSession: Equatable, Sendable {
let user: User
let accessToken: String
let refreshToken: String
}
@MainActor
final class TokenStore: ObservableObject {
@Published private(set) var session: StoredSession?
@Published private(set) var activeTeamId: String?
private let defaults = UserDefaults.standard
private let service = "com.matchlivetv.match-live-tv.auth"
init() {
session = loadSession()
activeTeamId = defaults.string(forKey: Keys.activeTeamId)
}
func saveSession(_ session: StoredSession) {
self.session = session
saveKeychain(key: Keys.accessToken, value: session.accessToken)
saveKeychain(key: Keys.refreshToken, value: session.refreshToken)
defaults.set(session.user.id, forKey: Keys.userId)
defaults.set(session.user.email, forKey: Keys.userEmail)
defaults.set(session.user.name, forKey: Keys.userName)
defaults.set(session.user.role, forKey: Keys.userRole)
}
func saveActiveTeamId(_ teamId: String) {
activeTeamId = teamId
defaults.set(teamId, forKey: Keys.activeTeamId)
}
func clear() {
session = nil
activeTeamId = nil
deleteKeychain(key: Keys.accessToken)
deleteKeychain(key: Keys.refreshToken)
defaults.removeObject(forKey: Keys.userId)
defaults.removeObject(forKey: Keys.userEmail)
defaults.removeObject(forKey: Keys.userName)
defaults.removeObject(forKey: Keys.userRole)
defaults.removeObject(forKey: Keys.activeTeamId)
}
private func loadSession() -> StoredSession? {
guard let access = loadKeychain(key: Keys.accessToken),
let refresh = loadKeychain(key: Keys.refreshToken),
let id = defaults.string(forKey: Keys.userId) else { return nil }
return StoredSession(
user: User(
id: id,
email: defaults.string(forKey: Keys.userEmail) ?? "",
name: defaults.string(forKey: Keys.userName) ?? "",
role: defaults.string(forKey: Keys.userRole) ?? "coach"
),
accessToken: access,
refreshToken: refresh
)
}
private enum Keys {
static let accessToken = "access_token"
static let refreshToken = "refresh_token"
static let userId = "user_id"
static let userEmail = "user_email"
static let userName = "user_name"
static let userRole = "user_role"
static let activeTeamId = "active_team_id"
}
private func saveKeychain(key: String, value: String) {
let data = Data(value.utf8)
let query: [String: Any] = [
kSecClass as String: kSecClassGenericPassword,
kSecAttrService as String: service,
kSecAttrAccount as String: key,
]
SecItemDelete(query as CFDictionary)
var add = query
add[kSecValueData as String] = data
SecItemAdd(add as CFDictionary, nil)
}
private func loadKeychain(key: String) -> String? {
let query: [String: Any] = [
kSecClass as String: kSecClassGenericPassword,
kSecAttrService as String: service,
kSecAttrAccount as String: key,
kSecReturnData as String: true,
kSecMatchLimit as String: kSecMatchLimitOne,
]
var result: AnyObject?
guard SecItemCopyMatching(query as CFDictionary, &result) == errSecSuccess,
let data = result as? Data else { return nil }
return String(data: data, encoding: .utf8)
}
private func deleteKeychain(key: String) {
let query: [String: Any] = [
kSecClass as String: kSecClassGenericPassword,
kSecAttrService as String: service,
kSecAttrAccount as String: key,
]
SecItemDelete(query as CFDictionary)
}
}
final class AnnouncementStore {
private let key = "mltv.dismissedAnnouncements"
func dismissedIds() -> Set<String> {
Set(UserDefaults.standard.stringArray(forKey: key) ?? [])
}
func dismiss(_ id: String) {
var ids = dismissedIds()
ids.insert(id)
UserDefaults.standard.set(Array(ids), forKey: key)
}
}