从 iOS 17 Observation 迁移至 iOS 16 兼容的 Combine 架构
将最低部署版本降至 iOS 16,以 ObservableObject 替换 @Observable,新增导航与 UI 兼容层,并补充登录冒烟 UI 测试。 Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@ -5,7 +5,7 @@
|
||||
// Created by Codex on 2026/6/18.
|
||||
//
|
||||
|
||||
import Observation
|
||||
import Combine
|
||||
|
||||
/// 当前账号的基础资料实体,用于跨页面展示昵称、手机号和头像。
|
||||
struct AccountProfile: Codable, Equatable {
|
||||
@ -58,14 +58,13 @@ struct BusinessScope: Codable, Equatable, Identifiable {
|
||||
}
|
||||
|
||||
@MainActor
|
||||
@Observable
|
||||
/// 账号上下文状态中心,保存当前账号资料和景区/门店作用域。
|
||||
final class AccountContext {
|
||||
private(set) var profile: AccountProfile?
|
||||
private(set) var scenicScopes: [BusinessScope] = []
|
||||
private(set) var storeScopes: [BusinessScope] = []
|
||||
var currentScenic: BusinessScope?
|
||||
var currentStore: BusinessScope?
|
||||
final class AccountContext: ObservableObject {
|
||||
@Published private(set) var profile: AccountProfile?
|
||||
@Published private(set) var scenicScopes: [BusinessScope] = []
|
||||
@Published private(set) var storeScopes: [BusinessScope] = []
|
||||
@Published var currentScenic: BusinessScope?
|
||||
@Published var currentStore: BusinessScope?
|
||||
|
||||
/// 应用登录成功后写入账号资料。
|
||||
func applyLogin(profile: AccountProfile? = nil) {
|
||||
|
||||
@ -6,7 +6,7 @@
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import Observation
|
||||
import Combine
|
||||
|
||||
/// 登录阶段实体,表示根视图当前应该展示的认证状态。
|
||||
enum AuthPhase: Equatable {
|
||||
@ -16,11 +16,10 @@ enum AuthPhase: Equatable {
|
||||
}
|
||||
|
||||
@MainActor
|
||||
@Observable
|
||||
/// 登录会话状态中心,保存 token 和当前认证阶段。
|
||||
final class AppSession {
|
||||
private(set) var phase: AuthPhase = .loggedOut
|
||||
private(set) var token: String?
|
||||
final class AppSession: ObservableObject {
|
||||
@Published private(set) var phase: AuthPhase = .loggedOut
|
||||
@Published private(set) var token: String?
|
||||
|
||||
var isLoggedIn: Bool {
|
||||
phase == .loggedIn
|
||||
|
||||
@ -6,15 +6,14 @@
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import Observation
|
||||
import Combine
|
||||
|
||||
@MainActor
|
||||
@Observable
|
||||
/// 登录会话协调器,统一处理登录完成、退出登录和缓存同步。
|
||||
final class AuthSessionCoordinator {
|
||||
@ObservationIgnored private let tokenStore: SessionTokenStore
|
||||
@ObservationIgnored private let snapshotStore: AccountSnapshotStore
|
||||
@ObservationIgnored private let preferencesStore: AppPreferencesStore
|
||||
private let tokenStore: SessionTokenStore
|
||||
private let snapshotStore: AccountSnapshotStore
|
||||
private let preferencesStore: AppPreferencesStore
|
||||
|
||||
/// 初始化登录会话协调器,并注入 token、账号快照和偏好存储。
|
||||
init(
|
||||
|
||||
@ -6,15 +6,14 @@
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import Observation
|
||||
import Combine
|
||||
|
||||
@MainActor
|
||||
@Observable
|
||||
/// 权限上下文状态中心,保存角色权限、当前角色和扁平化权限 URI。
|
||||
final class PermissionContext {
|
||||
private(set) var rolePermissions: [RolePermissionResponse] = []
|
||||
private(set) var permissionURIs: Set<String> = []
|
||||
var currentRole: RoleInfo?
|
||||
final class PermissionContext: ObservableObject {
|
||||
@Published private(set) var rolePermissions: [RolePermissionResponse] = []
|
||||
@Published private(set) var permissionURIs: Set<String> = []
|
||||
@Published var currentRole: RoleInfo?
|
||||
|
||||
/// 替换角色权限列表,并尽量按缓存角色 ID 保持当前角色。
|
||||
func replaceRolePermissions(_ rolePermissions: [RolePermissionResponse], currentRoleId: Int? = nil) {
|
||||
|
||||
@ -6,7 +6,7 @@
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import Observation
|
||||
import Combine
|
||||
|
||||
/// 景点加载阶段实体,表示当前景区景点列表的加载状态。
|
||||
enum ScenicSpotLoadState: Equatable {
|
||||
@ -17,12 +17,11 @@ enum ScenicSpotLoadState: Equatable {
|
||||
}
|
||||
|
||||
@MainActor
|
||||
@Observable
|
||||
/// 景点上下文状态中心,保存当前景区下的景点或打卡点列表。
|
||||
final class ScenicSpotContext {
|
||||
private(set) var scenicId: Int?
|
||||
private(set) var spots: [ScenicSpotItem] = []
|
||||
private(set) var loadState: ScenicSpotLoadState = .idle
|
||||
final class ScenicSpotContext: ObservableObject {
|
||||
@Published private(set) var scenicId: Int?
|
||||
@Published private(set) var spots: [ScenicSpotItem] = []
|
||||
@Published private(set) var loadState: ScenicSpotLoadState = .idle
|
||||
|
||||
/// 按景区 ID 重新加载景点列表,失败只影响景点模块本身。
|
||||
func reload(scenicId: Int?, api: AccountContextServing) async {
|
||||
|
||||
@ -6,14 +6,13 @@
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import Observation
|
||||
import Combine
|
||||
|
||||
@MainActor
|
||||
@Observable
|
||||
/// 登录态启动恢复器,负责冷启动时从缓存恢复并校验 token。
|
||||
final class SessionBootstrapper {
|
||||
@ObservationIgnored private let tokenStore: SessionTokenStore
|
||||
@ObservationIgnored private let snapshotStore: AccountSnapshotStore
|
||||
private let tokenStore: SessionTokenStore
|
||||
private let snapshotStore: AccountSnapshotStore
|
||||
private var didAttemptRestore = false
|
||||
|
||||
/// 初始化启动恢复器,并注入 token 与账号快照存储。
|
||||
|
||||
@ -5,17 +5,16 @@
|
||||
// Created by Codex on 2026/6/18.
|
||||
//
|
||||
|
||||
import Observation
|
||||
import Combine
|
||||
import SwiftUI
|
||||
|
||||
@MainActor
|
||||
@Observable
|
||||
/// 全局 Toast 状态中心,负责保存和清除当前提示文案。
|
||||
final class ToastCenter {
|
||||
fileprivate var message: String?
|
||||
@ObservationIgnored private let autoDismissNanoseconds: UInt64
|
||||
@ObservationIgnored private var dismissTask: Task<Void, Never>?
|
||||
@ObservationIgnored private var displayToken: UInt64 = 0
|
||||
final class ToastCenter: ObservableObject {
|
||||
@Published fileprivate var message: String?
|
||||
private let autoDismissNanoseconds: UInt64
|
||||
@Published private var dismissTask: Task<Void, Never>?
|
||||
@Published private var displayToken: UInt64 = 0
|
||||
|
||||
/// 创建 Toast 状态中心,默认 2.2 秒后自动隐藏。
|
||||
init(autoDismissNanoseconds: UInt64 = 2_200_000_000) {
|
||||
@ -65,7 +64,7 @@ final class ToastCenter {
|
||||
|
||||
/// 全局 Toast 叠加层修饰器,负责把 Toast 展示到页面顶部。
|
||||
private struct GlobalToastOverlay: ViewModifier {
|
||||
@Environment(ToastCenter.self) private var toastCenter
|
||||
@EnvironmentObject private var toastCenter: ToastCenter
|
||||
|
||||
func body(content: Content) -> some View {
|
||||
content
|
||||
|
||||
Reference in New Issue
Block a user