Files
shaguabijia-app-ios/App/Features/Login/LoginViewModel.swift
T
marco 98b7600797 初始化傻瓜比价正式版 iOS 客户端
对标安卓正式版、对接 app-server(8770)的 SwiftUI 客户端。iOS 无障碍受限、无比价核心,做自成一体:美团 CPS 导购 + 福利(签到/任务/金币)+ 查看(比价记录/省钱战绩)。
功能完整 M0-M5:脚手架、短信登录(JWT+Keychain+401 自动刷新)、首页券 feed 导购、比价记录+省钱战绩、福利、我的(资料/反馈/设置/注销)。
单 target、0 三方依赖、XcodeGen 生成工程;Debug/Release 双 plist 分流 ATS。

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-05 00:54:11 +08:00

68 lines
2.3 KiB
Swift

import SwiftUI
/// ( SmsLoginViewModel)
@MainActor
@Observable
final class LoginViewModel {
var phone: String = ""
var code: String = ""
var countdown: Int = 0
var isSending = false
var isLoggingIn = false
var isMock = false
var errorMessage: String?
private let auth = AuthService()
private var timerTask: Task<Void, Never>?
var isValidPhone: Bool { phone.count == 11 && phone.hasPrefix("1") }
var canSendCode: Bool { countdown == 0 && !isSending && isValidPhone }
var canLogin: Bool { isValidPhone && code.count >= 4 && !isLoggingIn }
func sendCode() async {
guard canSendCode else { return }
errorMessage = nil
isSending = true
defer { isSending = false }
do {
let resp = try await auth.smsSend(phone: phone)
isMock = resp.mock
startCountdown(resp.cooldownSec > 0 ? resp.cooldownSec : 60)
} catch {
errorMessage = (error as? LocalizedError)?.errorDescription ?? "验证码发送失败,请重试"
}
}
/// session, true( View )
func login(into session: SessionStore) async -> Bool {
guard canLogin else { return false }
errorMessage = nil
isLoggingIn = true
defer { isLoggingIn = false }
do {
let resp = try await auth.smsLogin(phone: phone, code: code)
await session.loginSucceeded(resp)
return true
} catch APIError.http(let status, _) where status == 400 {
errorMessage = "验证码错误或已失效,请重新获取"
return false
} catch {
errorMessage = (error as? LocalizedError)?.errorDescription ?? "登录失败,请重试"
return false
}
}
private func startCountdown(_ seconds: Int) {
countdown = seconds
timerTask?.cancel()
timerTask = Task { @MainActor [weak self] in
while true {
do { try await Task.sleep(for: .seconds(1)) }
catch { return } // cancel() 退,
guard let self, self.countdown > 0 else { return }
self.countdown -= 1
}
}
}
}