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

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.

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.22 10:11:00