// // SettingViewModel.swift // suixinkan // import Foundation /// 设置中心 ViewModel,管理版本展示、协议入口和更新标记。 final class SettingViewModel { private let infoDictionary: [String: Any]? private let environment: APIEnvironment private(set) var latestVersion: VersionResponse? private(set) var hasNewVersion = false var onStateChange: (() -> Void)? init( infoDictionary: [String: Any]? = Bundle.main.infoDictionary, environment: APIEnvironment = .current ) { self.infoDictionary = infoDictionary self.environment = environment } var appVersion: String { AppClientInfo.appVersion(infoDictionary: infoDictionary) } var appDownloadURL: URL { environment.baseURL.appending(path: "h5/app/download") } /// 静默检查最新版本,失败时保持当前页面状态。 func checkLatestVersion(api: SettingAPI) async { do { let response = try await api.checkLatestVersion() latestVersion = response hasNewVersion = Self.isNewerVersionCode(response.versionCode, than: currentVersionCode(infoDictionary: infoDictionary)) notifyStateChange() } catch is CancellationError { return } catch { latestVersion = nil hasNewVersion = false notifyStateChange() } } /// 返回指定协议入口的标题和 URL。 func agreementDestination(for kind: SettingAgreementKind) -> SettingAgreementDestination { switch kind { case .aboutUs: SettingAgreementDestination( title: "关于我们", url: environment.baseURL.appending(path: "h5/app/about-us") ) case .userAgreement: SettingAgreementDestination( title: "用户协议", url: environment.baseURL.appending(path: "h5/app/user-agreement") ) case .privacyPolicy: SettingAgreementDestination( title: "隐私政策", url: environment.baseURL.appending(path: "h5/app/privacy-policy") ) } } /// 判断后端版本号是否高于当前构建号。 nonisolated static func isNewerVersionCode(_ latestVersionCode: Int, than currentVersionCode: Int?) -> Bool { guard let currentVersionCode else { return false } return latestVersionCode > currentVersionCode } private func currentVersionCode(infoDictionary: [String: Any]?) -> Int? { let build = (infoDictionary?["CFBundleVersion"] as? String)? .trimmingCharacters(in: .whitespacesAndNewlines) guard let build, !build.isEmpty else { return nil } return Int(build) } private func notifyStateChange() { onStateChange?() } }