如何使用Alamofire搜索JSON API并解码数组(按name字段筛选)
解决Alamofire调用API解码数组及按name前缀搜索的问题
看起来你现在的代码有几个关键问题需要调整:一是API返回的是数组结构,但你的解码模型是单个对象;二是每次搜索文本变化都重新发起网络请求,效率很低;三是还没实现搜索过滤的核心逻辑。我帮你一步步修正:
1. 修正JSON解码模型
首先,你提供的API返回是对象数组([{...}, {...}]),但当前的SearchResults结构体只对应单个对象,而且字段名也和API返回不匹配(API里是resultId和name,你用的是id)。我们需要先定义单个结果的模型,再解码成数组:
// 单个结果的模型,字段名完全匹配API返回的键名 struct SearchResult: Decodable { let resultId: String let name: String }
2. 优化网络请求逻辑
不要在searchBar(_:textDidChange:)里每次输入都发起请求——频繁输入会导致大量重复请求,既浪费资源又影响体验。正确的做法是:
- 先在页面初始化时发起一次网络请求,缓存所有返回结果
- 搜索时直接在缓存的结果里做本地过滤
我们可以添加全局变量来缓存数据:
// 缓存所有API返回的完整结果 var allResults: [SearchResult] = [] // 存储过滤后的结果,供UI展示使用 var displayedResults: [SearchResult] = []
然后把网络请求移到页面初始化逻辑里(比如viewDidLoad):
override func viewDidLoad() { super.viewDidLoad() // 页面加载时先获取全部数据 fetchAllResults() } func fetchAllResults() { let url = "http://dummy.com/url" Alamofire.request(url).responseData { [weak self] dataResponse in guard let self = self else { return } if let err = dataResponse.error { print("请求服务器失败:", err) return } guard let data = dataResponse.data else { print("未获取到有效数据") return } do { // 解码成数组类型[SearchResult] self.allResults = try JSONDecoder().decode([SearchResult].self, from: data) print("成功获取\(self.allResults.count)条结果") // 初始加载后显示全部结果 self.updateDisplayedResults() } catch let decodeError { print("数据解码失败:", decodeError) } } }
3. 实现按name前缀搜索的逻辑
现在在searchBar(_:textDidChange:)里,我们只需要根据输入的文本过滤缓存的allResults即可:
func searchBar(_ searchBar: UISearchBar, textDidChange searchText: String) { if searchText.isEmpty { // 输入为空时显示全部结果 displayedResults = allResults } else { // 过滤出name以搜索文本开头的结果(不区分大小写,提升用户体验) displayedResults = allResults.filter { result in result.name.lowercased().hasPrefix(searchText.lowercased()) } } // 更新UI展示过滤后的结果 updateDisplayedResults() } // 用于刷新UI的方法(比如给tableView调用reloadData) func updateDisplayedResults() { // 示例:如果用UITableView展示,取消注释下面的代码 // tableView.reloadData() print("当前展示\(displayedResults.count)条过滤结果") }
额外注意点
- 如果API返回的是外层带包裹的JSON(比如
{ "total": 2, "results": [...] }),那你需要再定义一个外层模型:
解码时改为struct SearchResponse: Decodable { let total: Int let results: [SearchResult] }let response = try JSONDecoder().decode(SearchResponse.self, from: data),再把self.allResults = response.results - 可以给搜索添加防抖逻辑(比如延迟0.3秒再执行过滤),避免频繁触发过滤操作
- 网络请求里的
[weak self]是为了避免循环引用,记得保留
内容的提问来源于stack exchange,提问作者Box House
相关产品推荐
相关产品推荐

