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

如何使用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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.27 07:24:41