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

SwiftUI中如何将TextField内容传入函数拼接URL并请求数据

解决SwiftUI中View输入值传递给Combine ViewModel的问题

想要将ResultView中TextField的inputText传入DownloadWithCombineViewModel的getPosts函数,拼接成请求URL获取PostModel数据,但ViewModel无法访问到inputText,以下是具体解决方法:

原问题代码

import Combine
import SwiftUI

struct PostModel: Identifiable, Codable {
    let id: String
    let url: String
}

class DownloadWithCombineViewModel: ObservableObject {
    
    @Published var posts: [PostModel] = []
    var cancellables = Set<AnyCancellable>()
    
    init() {
        getPosts()
    }
    
    func getPosts() {
        guard let url = URL(string: "https://example\(inputText)")
        else { return }
        URLSession.shared.dataTaskPublisher(for: url)
        
            .receive(on: DispatchQueue.main)
            .tryMap(handleOutput)
            .decode(type: PostModel.self, decoder: JSONDecoder())
            .replaceError(with: PostModel(id: "", url: ""))
            .sink(receiveValue: { [weak self] returnedPosts in
                self?.posts = [returnedPosts]
            })
            .store(in: &cancellables)
    }
    
    func handleOutput(output: URLSession.DataTaskPublisher.Output) throws -> Data {
        guard
            let response = output.response as? HTTPURLResponse,
            response.statusCode >= 200 && response.statusCode < 300 else {
            throw URLError(.badServerResponse)
        }
        return output.data
    }
}
struct ResultView: View {
    
    @State var inputText = ""
    @State var showResult = false
    @StateObject var vm = DownloadWithCombineViewModel()
    
    var body: some View {
        NavigationView {
            VStack {
                TextField("Enter", text: $inputText)
                
                Button("Show Result") {
                    
                    showResult.toggle()
                }
                .padding()
                .background(Color.blue)
                .foregroundColor(.white)
                .cornerRadius(10)
                
                if showResult {
                   
                    List {
                        ForEach(vm.posts) { post in
                            VStack(alignment: .leading) {
                                Text(post.url)
                                    .font(.headline)
                            }
                            .frame(maxWidth: .infinity, alignment: .leading)
                            .padding()
                        }
                    }
                }
            }
        }
    }
}

核心修改方案

修改点说明

  • 让getPosts函数接收外部传入的inputText参数,消除对ViewModel内部未定义变量的依赖
  • 移除ViewModel初始化时自动调用getPosts的逻辑,改为由View手动触发请求
  • 在按钮点击事件中,将View的inputText传入ViewModel的请求方法,同时控制结果展示

修改后完整代码

import Combine
import SwiftUI

struct PostModel: Identifiable, Codable {
    let id: String
    let url: String
}

class DownloadWithCombineViewModel: ObservableObject {
    
    @Published var posts: [PostModel] = []
    var cancellables = Set<AnyCancellable>()
    
    // 移除初始化时自动调用getPosts的逻辑
    init() {}
    
    // 修改函数,接收外部传入的inputText参数
    func getPosts(inputText: String) {
        // 拼接URL并做有效性校验,无效时清空数据
        guard let url = URL(string: "https://example\(inputText)") else {
            posts = []
            return
        }
        
        URLSession.shared.dataTaskPublisher(for: url)
            .receive(on: DispatchQueue.main)
            .tryMap(handleOutput)
            .decode(type: PostModel.self, decoder: JSONDecoder())
            .replaceError(with: PostModel(id: "", url: "请求出错或数据无效"))
            .sink(receiveValue: { [weak self] returnedPost in
                self?.posts = [returnedPost]
            })
            .store(in: &cancellables)
    }
    
    func handleOutput(output: URLSession.DataTaskPublisher.Output) throws -> Data {
        guard
            let response = output.response as? HTTPURLResponse,
            response.statusCode >= 200 && response.statusCode < 300 else {
            throw URLError(.badServerResponse)
        }
        return output.data
    }
}

struct ResultView: View {
    
    @State var inputText = ""
    @State var showResult = false
    @StateObject var vm = DownloadWithCombineViewModel()
    
    var body: some View {
        NavigationView {
            VStack {
                TextField("Enter", text: $inputText)
                    .textFieldStyle(RoundedBorderTextFieldStyle())
                    .padding()
                
                Button("Show Result") {
                    // 点击按钮时传入inputText并发起请求
                    vm.getPosts(inputText: inputText)
                    showResult.toggle()
                }
                .padding()
                .background(Color.blue)
                .foregroundColor(.white)
                .cornerRadius(10)
                
                if showResult {
                    List {
                        ForEach(vm.posts) { post in
                            VStack(alignment: .leading) {
                                Text(post.url)
                                    .font(.headline)
                            }
                            .frame(maxWidth: .infinity, alignment: .leading)
                            .padding()
                        }
                    }
                }
            }
        }
    }
}

额外优化建议

  • 给TextField添加输入验证逻辑,确保内容符合URL格式要求后再发起请求
  • 添加加载状态提示,避免用户在请求过程中重复点击按钮
  • 细化错误处理,区分无效URL、网络错误、数据解析错误等不同场景

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.29 14:17:57