You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

SwiftUI中延迟.task执行及LoveViewModel数据初始化优化问题

问题1:提前发起API请求、跳转后先显示默认数据的修复

你的代码中LoveCalculator视图的.task修饰符会在视图渲染完成后立即执行loveCal方法,此时firstName和secondName为空字符串,导致提前发起无效请求;同时loveData初始化了默认值,跳转后会先显示默认内容,直到API请求完成才更新。

修复要点:

  • 移除.task修饰符,将API请求触发时机改为点击"CHECK IT!"按钮时
  • 禁用按钮直到两个输入框都有内容,避免空参数请求
  • 配合可选类型的loveData,确保数据加载完成后再跳转

问题2:将loveData改为可选类型的实现

完全可以把loveData设为可选类型,无需默认值。同时建议添加加载状态和错误状态,提升用户体验。


修改后的完整代码

1. LoveData(无需修改)

struct LoveData: Codable {
    let percentage: String
    let result: String
}

2. LoveViewModel

class LoveViewModel: ObservableObject {
    // 改为可选类型,取消默认值
    @Published var loveData: LoveData?
    @Published var isLoading = false
    @Published var errorMessage: String?
    
    let baseURL = "https://love-calculator.p.rapidapi.com/getPercentage"
    let apiKey = "c6c134a7f0msh980729b528fe273p1f337fjsnd17137cb2f24"
    
    func calculateLove(first: String, second: String) async {
        guard !first.isEmpty, !second.isEmpty else {
            errorMessage = "请输入完整姓名"
            return
        }
        
        isLoading = true
        errorMessage = nil
        
        // 使用URLComponents构建URL,避免字符串拼接的编码问题
        guard var components = URLComponents(string: baseURL) else {
            errorMessage = "URL格式错误"
            isLoading = false
            return
        }
        
        components.queryItems = [
            URLQueryItem(name: "sname", value: first),
            URLQueryItem(name: "fname", value: second),
            URLQueryItem(name: "rapidapi-key", value: apiKey)
        ]
        
        guard let url = components.url else {
            errorMessage = "无法构建请求URL"
            isLoading = false
            return
        }
        
        do {
            var request = URLRequest(url: url)
            // 符合rapidapi接口要求的Host头
            request.setValue("love-calculator.p.rapidapi.com", forHTTPHeaderField: "X-RapidAPI-Host")
            
            let (data, response) = try await URLSession.shared.data(for: request)
            
            // 检查HTTP响应状态
            guard let httpResponse = response as? HTTPURLResponse, httpResponse.statusCode == 200 else {
                errorMessage = "请求失败,请重试"
                isLoading = false
                return
            }
            
            let decodedData = try JSONDecoder().decode(LoveData.self, from: data)
            DispatchQueue.main.async {
                self.loveData = decodedData
            }
        } catch {
            errorMessage = "请求出错:\(error.localizedDescription)"
            print(error)
        }
        
        DispatchQueue.main.async {
            self.isLoading = false
        }
    }
}

3. LoveCalculator View

struct LoveCalculator: View {
    @State var firstName = ""
    @State var secondName = ""
    @State private var isResultPresented = false
    @StateObject var loveViewModel = LoveViewModel() // 用@StateObject确保生命周期一致
    
    var body: some View {
        NavigationView {
            ZStack{
                Color(.systemTeal).ignoresSafeArea()
                VStack(alignment: .center) {
                    Text("Love Calculator")
                        .padding(.top, 100)
                    
                    Text("输入第一个姓名:")
                        .padding()
                    TextField("姓名", text: $firstName)
                        .textFieldStyle(.roundedBorder)
                        .frame(maxWidth: 300)
                        .padding(.bottom)
                    
                    Text("输入第二个姓名:")
                        .padding()
                    TextField("姓名", text: $secondName)
                        .textFieldStyle(.roundedBorder)
                        .frame(maxWidth: 300)
                        .padding(.bottom)
                    
                    // 显示错误提示
                    if let error = loveViewModel.errorMessage {
                        Text(error)
                            .foregroundColor(.red)
                            .padding(.bottom)
                    }
                    
                    // 点击按钮触发请求,成功后跳转
                    Button(action: {
                        Task {
                            await loveViewModel.calculateLove(first: firstName, second: secondName)
                            // 仅当数据加载成功时跳转
                            if loveViewModel.loveData != nil {
                                isResultPresented = true
                            }
                        }
                    }) {
                        if loveViewModel.isLoading {
                            ProgressView()
                                .foregroundColor(.white)
                        } else {
                            Text("CHECK IT!")
                                .foregroundColor(.white)
                                .padding()
                                .background(Color.blue)
                                .cornerRadius(8)
                        }
                    }
                    // 输入为空时禁用按钮
                    .disabled(firstName.isEmpty || secondName.isEmpty)
                    
                    Spacer()
                }
                
                // 通过isPresented控制导航跳转
                NavigationLink(isActive: $isResultPresented) {
                    if let data = loveViewModel.loveData {
                        LoveResults(percentage: data.percentage, description: data.result)
                    } else {
                        Text("暂无数据")
                    }
                } label: {
                    EmptyView()
                }
            }
        }
    }
}

4. LoveResults View(无需修改)

struct LoveResults: View {
    var percentage: String
    var description: String
    var body: some View {
        ZStack{
            Color(.green).ignoresSafeArea()
            VStack{
                Text("RESULTS :")
                    .padding()
                Text(percentage)
                    .font(.system(size: 80, weight: .heavy))
                    .foregroundColor(.red)
                    .padding()
                Text(description)
            }
        }
    }
}

关键修改说明

  • ViewModel:
    • 将loveData改为可选类型,取消默认值
    • 添加isLoading和errorMessage状态,用于显示加载提示和错误信息
    • 使用URLComponents构建请求URL,避免姓名含特殊字符时的编码问题
    • 完善错误处理逻辑,覆盖空输入、URL构建失败、HTTP响应异常等场景
  • 视图:
    • 用@StateObject替代@ObservedObject,确保ViewModel与视图生命周期同步
    • 移除自动触发的.task,将请求放在按钮点击事件中
    • 按钮根据输入状态禁用,避免无效请求
    • 实现"先请求再跳转"的逻辑,确保跳转时已有有效数据

内容的提问来源于stack exchange,提问作者michalbarnat

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.25 17:36:21