Swift中如何将Task获取的JSON共享至其他UIViewController
解决异步获取JSON并跨VC使用的问题
嘿,我来帮你搞定这个问题!你之前把网络请求放在UIViewController里其实不太合适,而且异步操作的核心是得等请求完成才能拿到数据,直接拿变量肯定不行。咱们一步步来,用更清晰的方式实现:
第一步:把网络请求抽成独立的服务类
首先,咱们把网络请求逻辑从视图控制器里剥离出来,做一个专门的网络服务类——这样不仅代码更整洁,还能在任意UIViewController里复用这个逻辑。
// ShuttleService.swift import Foundation class ShuttleService { // 用单例模式,方便全局调用 static let shared = ShuttleService() // 自定义网络错误类型,方便处理各种失败情况 enum NetworkError: Error { case invalidURL case invalidResponse case decodingFailed } // 用Swift的async/await实现异步获取数据(现在更推荐这种方式,比completionHandler简洁) func getGenres() async throws -> [Genre] { // 替换成你的API地址 guard let url = URL(string: "https://your-api-url.com/genres") else { throw NetworkError.invalidURL } // 发起异步网络请求 let (data, response) = try await URLSession.shared.data(from: url) // 检查响应状态码是否正常 guard let httpResponse = response as? HTTPURLResponse, httpResponse.statusCode == 200 else { throw NetworkError.invalidResponse } // 解析JSON到你的模型对象 let decoder = JSONDecoder() // 这里的Genre是你自己定义的Codable模型,替换成你实际的模型 let genres = try decoder.decode([Genre].self, from: data) return genres } } // 示例:你的JSON解析模型(根据实际API返回结构调整) struct Genre: Codable { let id: Int let name: String // 其他字段根据你的JSON结构添加 }
第二步:在任意UIViewController里调用获取数据
现在你可以在任何需要数据的UIViewController里,通过这个服务类异步获取JSON数据,拿到后就能直接操作了:
// 比如你的某个ViewController.swift import UIKit class GenreListViewController: UIViewController { // 用来存储拿到的JSON解析后的数据 private var genres: [Genre] = [] override func viewDidLoad() { super.viewDidLoad() // 页面加载后开始获取数据 fetchGenres() } private func fetchGenres() { // 用Task包装异步操作 Task { do { // 等待异步请求完成,拿到数据 self.genres = try await ShuttleService.shared.getGenres() // 注意:更新UI必须在主线程! DispatchQueue.main.async { // 这里可以做UI操作,比如刷新表格、更新标签文字等 self.tableView.reloadData() // 假设你有一个展示列表的tableView } } catch { // 处理请求失败的情况,比如打印错误、给用户提示 print("获取数据失败:\(error.localizedDescription)") // 示例:显示错误弹窗 let alert = UIAlertController(title: "出错了", message: "无法获取数据", preferredStyle: .alert) alert.addAction(UIAlertAction(title: "确定", style: .default)) self.present(alert, animated: true) } } } }
如果你还是想用CompletionHandler的方式
要是你习惯用完成处理程序,也可以在服务类里加个兼容方法:
// 在ShuttleService类里添加这个方法 func getGenresWithCompletion(completion: @escaping (Result<[Genre], NetworkError>) -> Void) { guard let url = URL(string: "https://your-api-url.com/genres") else { completion(.failure(.invalidURL)) return } URLSession.shared.dataTask(with: url) { data, response, error in // 处理请求错误 if error != nil { completion(.failure(.invalidResponse)) return } // 检查响应状态 guard let httpResponse = response as? HTTPURLResponse, httpResponse.statusCode == 200 else { completion(.failure(.invalidResponse)) return } // 解析JSON guard let data = data else { completion(.failure(.decodingFailed)) return } do { let genres = try JSONDecoder().decode([Genre].self, from: data) completion(.success(genres)) } catch { completion(.failure(.decodingFailed)) } }.resume() }
然后在ViewController里这样调用:
private func fetchGenresWithCompletion() { ShuttleService.shared.getGenresWithCompletion { [weak self] result in guard let self = self else { return } // 切回主线程更新UI DispatchQueue.main.async { switch result { case .success(let genres): self.genres = genres self.tableView.reloadData() case .failure(let error): print("失败:\(error)") // 显示错误提示 } } } }
关键要点提醒
- 不要把网络请求放在UIViewController里:解耦后代码更易维护,多个页面可以复用同一个请求逻辑
- 异步操作的本质:网络请求是耗时操作,不能直接同步返回数据,必须用
async/await或completionHandler等待请求完成 - UI操作必须在主线程:所有更新UI的代码一定要放在
DispatchQueue.main.async里,否则会崩溃
内容的提问来源于stack exchange,提问作者Andreas P.
相关产品推荐
相关产品推荐

