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

SwiftUI中SearchBar与REST API结合实现搜索数据更新的问题

看起来你已经搭好了UI框架和SearchBar的基础,现在就差把搜索输入和API请求的逻辑串起来了对吧?我来给你梳理一下具体的实现步骤:

第一步:重构GameListViewModel,让它支持动态搜索

首先你的ViewModel需要能响应搜索字符串的变化,我们把搜索字段改成@Published属性,再添加一个专门的API请求方法,这样就能在搜索内容改变时触发数据更新:

class GameListViewModel: ObservableObject {
    @Published var games: [Game] = []
    @Published var isLoading = false
    @Published var searchString: String = "" // 改为可观察的属性
    
    init() {
        // 可选:初始化时加载默认数据,或者留空等待用户搜索
        fetchGames()
    }
    
    // 封装API请求逻辑,使用当前的searchString作为参数
    func fetchGames() {
        isLoading = true
        
        // 替换为你的实际API地址,注意对搜索字符串做URL编码
        guard let encodedSearch = searchString.addingPercentEncoding(withAllowedCharacters: .urlQueryAllowed),
              let url = URL(string: "https://your-api-domain.com/games?search=\(encodedSearch)") else {
            isLoading = false
            return
        }
        
        URLSession.shared.dataTask(with: url) { [weak self] data, _, error in
            DispatchQueue.main.async {
                self?.isLoading = false
                guard let data = data, error == nil else {
                    // 这里可以添加错误提示逻辑
                    print("请求失败:\(error?.localizedDescription ?? "未知错误")")
                    return
                }
                
                // 解析数据到games数组(根据你的API返回结构调整解析逻辑)
                do {
                    let gameResponse = try JSONDecoder().decode([Game].self, from: data)
                    self?.games = gameResponse
                } catch {
                    print("数据解析错误:\(error)")
                }
            }
        }.resume()
    }
}

第二步:在ContentView中关联搜索输入与ViewModel

现在我们需要监听搜索框的输入变化,触发ViewModel的API请求。可以用SwiftUI的onChange修饰符,或者结合Combine做延迟优化:

struct ContentView: View {
    @State private var searchTerm: String = ""
    @StateObject var gameData = GameListViewModel() // SwiftUI 2+推荐用@StateObject保持ViewModel实例稳定
    
    var body: some View {
        NavigationView{
            Group{
                if gameData.isLoading {
                    LoadingView()
                } else{
                    VStack{
                        // 注意Swift命名规范:类型名首字母大写,改为SearchBarView
                        SearchBarView(text: $searchTerm)
                            // 监听搜索文本变化,触发API请求
                            .onChange(of: searchTerm) { newSearchText in
                                gameData.searchString = newSearchText
                                gameData.fetchGames()
                            }
                        
                        List(gameData.games){ game in
                            NavigationLink(destination: GameDetailView(gameName: game.name ?? "0", gameId: 0)){
                                HStack {
                                    VStack(alignment: .leading, spacing: 2) {
                                        Text(game.name ?? "Kein Name gefunden")
                                            .font(.headline)
                                        Text("Cover: \(game.cover?.toString() ?? "0")")
                                            .font(.subheadline)
                                            .foregroundColor(.gray)
                                    }
                                }
                            }
                        }
                    }
                }
            }
            .navigationBarTitle(Text("Games"))
        }
    }
}

第三步:优化SearchBar的细节与命名

Swift的类型命名规范要求首字母大写,同时可以添加一些友好的细节:

import Foundation
import SwiftUI

struct SearchBarView: UIViewRepresentable {
    @Binding var text: String
    
    class Coordinator: NSObject, UISearchBarDelegate {
        @Binding var text: String
        
        init(text: Binding<String>){
            _text = text
        }
        
        func searchBar(_ searchBar: UISearchBar, textDidChange searchText: String) {
            text = searchText
        }
        
        // 可选:添加搜索按钮点击回调,用户点击搜索时收起键盘
        func searchBarSearchButtonClicked(_ searchBar: UISearchBar) {
            searchBar.resignFirstResponder()
        }
    }
    
    func makeCoordinator() -> Coordinator {
        Coordinator(text: $text)
    }
    
    func makeUIView(context: Context) -> UISearchBar {
        let searchBar = UISearchBar(frame: .zero)
        searchBar.delegate = context.coordinator
        searchBar.placeholder = "Suche Spiele..." // 添加占位符提升用户体验
        return searchBar
    }
    
    func updateUIView(_ uiView: UISearchBar, context: Context) {
        uiView.text = text
    }
}

额外优化:添加搜索延迟(避免频繁请求)

如果用户输入很快,每输入一个字符就发起API请求会浪费资源,可以用Combine实现0.5秒的延迟,等用户停止输入后再请求:

import Combine

struct ContentView: View {
    @State private var searchTerm: String = ""
    @StateObject var gameData = GameListViewModel()
    private var cancellables = Set<AnyCancellable>()
    
    var body: some View {
        NavigationView{
            // 保持原有UI代码不变
        }
        .onAppear {
            $searchTerm
                .debounce(for: .seconds(0.5), scheduler: DispatchQueue.main)
                .sink { newSearchText in
                    gameData.searchString = newSearchText
                    gameData.fetchGames()
                }
                .store(in: &cancellables)
        }
    }
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.14 08:04:34