Migrate from iOS 17 Observation to iOS 16-compatible Combine architecture.
Lower deployment target to iOS 16, replace @Observable with ObservableObject, add navigation and UI compatibility shims, and include login smoke UI tests. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@ -6,7 +6,7 @@
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import Observation
|
||||
import Combine
|
||||
|
||||
/// 飞手认证服务协议,定义认证详情、短信、提交和编辑接口。
|
||||
@MainActor
|
||||
@ -18,10 +18,9 @@ protocol PilotCertificationServing {
|
||||
}
|
||||
|
||||
@MainActor
|
||||
@Observable
|
||||
/// 飞手认证 API,封装 `/api/app/flyer` 认证相关接口。
|
||||
final class PilotCertificationAPI {
|
||||
@ObservationIgnored private let client: APIClient
|
||||
private let client: APIClient
|
||||
|
||||
/// 初始化飞手认证 API,并注入共享网络客户端。
|
||||
init(client: APIClient) {
|
||||
|
||||
@ -6,7 +6,7 @@
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import Observation
|
||||
import Combine
|
||||
|
||||
@MainActor
|
||||
/// 飞手认证实名认证读取协议,便于 ViewModel 单测替换。
|
||||
@ -17,29 +17,28 @@ protocol PilotRealNameServing {
|
||||
extension ProfileAPI: PilotRealNameServing {}
|
||||
|
||||
@MainActor
|
||||
@Observable
|
||||
/// 飞手认证 ViewModel,管理审核状态、表单、验证码、证件图上传和提交。
|
||||
final class PilotCertificationViewModel {
|
||||
private(set) var flyer: FlyerDetailResponse?
|
||||
private(set) var realNameInfo: RealNameInfo?
|
||||
var name = ""
|
||||
var certType: PilotCertType = .caac
|
||||
var certNo = ""
|
||||
var certImageUrl = ""
|
||||
var startDate = Date()
|
||||
var endDate = Calendar.current.date(byAdding: .year, value: 1, to: Date()) ?? Date()
|
||||
var droneModel = ""
|
||||
var droneSerialNo = ""
|
||||
var phone = ""
|
||||
var verifyCode = ""
|
||||
private(set) var pendingCertificateImageData: Data?
|
||||
private(set) var pendingCertificateFileName: String?
|
||||
private(set) var uploadProgress: Int?
|
||||
private(set) var loading = false
|
||||
private(set) var sendingCode = false
|
||||
private(set) var submitting = false
|
||||
private(set) var countdown = 0
|
||||
var statusMessage: String?
|
||||
final class PilotCertificationViewModel: ObservableObject {
|
||||
@Published private(set) var flyer: FlyerDetailResponse?
|
||||
@Published private(set) var realNameInfo: RealNameInfo?
|
||||
@Published var name = ""
|
||||
@Published var certType: PilotCertType = .caac
|
||||
@Published var certNo = ""
|
||||
@Published var certImageUrl = ""
|
||||
@Published var startDate = Date()
|
||||
@Published var endDate = Calendar.current.date(byAdding: .year, value: 1, to: Date()) ?? Date()
|
||||
@Published var droneModel = ""
|
||||
@Published var droneSerialNo = ""
|
||||
@Published var phone = ""
|
||||
@Published var verifyCode = ""
|
||||
@Published private(set) var pendingCertificateImageData: Data?
|
||||
@Published private(set) var pendingCertificateFileName: String?
|
||||
@Published private(set) var uploadProgress: Int?
|
||||
@Published private(set) var loading = false
|
||||
@Published private(set) var sendingCode = false
|
||||
@Published private(set) var submitting = false
|
||||
@Published private(set) var countdown = 0
|
||||
@Published var statusMessage: String?
|
||||
|
||||
/// 加载实名状态和飞手认证详情,单通道失败不清空另一通道数据。
|
||||
func load(api: any PilotCertificationServing, realNameAPI: any PilotRealNameServing) async {
|
||||
|
||||
@ -12,14 +12,14 @@ import UIKit
|
||||
|
||||
/// 飞手认证页面,展示认证审核状态并支持提交/驳回后编辑。
|
||||
struct PilotCertificationView: View {
|
||||
@Environment(AccountContext.self) private var accountContext
|
||||
@Environment(ProfileAPI.self) private var profileAPI
|
||||
@Environment(PilotCertificationAPI.self) private var pilotAPI
|
||||
@Environment(OSSUploadService.self) private var ossUploadService
|
||||
@Environment(ToastCenter.self) private var toastCenter
|
||||
@EnvironmentObject private var accountContext: AccountContext
|
||||
@Environment(\.profileAPI) private var profileAPI
|
||||
@Environment(\.pilotCertificationAPI) private var pilotAPI
|
||||
@Environment(\.ossUploadService) private var ossUploadService
|
||||
@EnvironmentObject private var toastCenter: ToastCenter
|
||||
@Environment(\.globalLoading) private var globalLoading
|
||||
|
||||
@State private var viewModel = PilotCertificationViewModel()
|
||||
@StateObject private var viewModel = PilotCertificationViewModel()
|
||||
@State private var selectedImageItem: PhotosPickerItem?
|
||||
private let countdownTimer = Timer.publish(every: 1, on: .main, in: .common).autoconnect()
|
||||
|
||||
@ -55,7 +55,7 @@ struct PilotCertificationView: View {
|
||||
.onReceive(countdownTimer) { _ in
|
||||
viewModel.tickCountdown()
|
||||
}
|
||||
.onChange(of: selectedImageItem) { _, item in
|
||||
.onChange(of: selectedImageItem) { item in
|
||||
guard let item else { return }
|
||||
Task { await loadImage(item) }
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user