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

Swift中闭包外无法访问JSON数据的解决方法咨询

Hey there! Let's break down what's going on here and fix your problem step by step:

The Core Issues

First off, the main problem you're hitting is asynchronous network requests — your API call runs in the background, so the print statements outside the closure execute before the request finishes and returns data. That's why those prints show nil and an empty array. Additionally, you're not actually saving the fetched businesses to your class-level variable, only printing individual names (and using a single optional string names instead of an array, which won't work for a table view anyway).

Step-by-Step Fix

1. Properly Save the Fetched Data

Inside the completion closure, assign the fetched response.businesses directly to your class's businesses array. If you want to store just the names, use a [String] array instead of a single optional string.

2. Refresh the Table View on the Main Thread

Network request closures run on a background thread, and all UI updates (like reloading a table view) must happen on the main thread. Wrap your table view reload in DispatchQueue.main.async.

3. Don't Forget Table View Setup

Make sure your table view is connected to your view controller (via IBOutlet or code) and that you've set up the data source/delegate and registered a cell reuse identifier.

Modified Working Code
class YelpTableView: UIViewController, UITableViewDataSource, UITableViewDelegate {
    // Store all fetched business data
    var businesses = [CDYelpBusiness]()
    // Optional: Store just business names if needed
    var businessNames = [String]()
    
    // Connect this to your table view in Storyboard/XIB
    @IBOutlet weak var tableView: UITableView!

    override func viewDidLoad() {
        super.viewDidLoad()
        
        // Configure table view (if not set in Storyboard)
        tableView.dataSource = self
        tableView.delegate = self
        // Register a basic cell (use your custom cell if you have one)
        tableView.register(UITableViewCell.self, forCellReuseIdentifier: "BusinessCell")
        
        let yelpAPIClient = CDYelpAPIClient(apiKey: "MY-API-KEY")
        yelpAPIClient.cancelAllPendingAPIRequests()
        CDYelpFusionKitManager.shared.apiClient = yelpAPIClient
        
        CDYelpFusionKitManager.shared.apiClient.searchBusinesses(byTerm: "Bars",
                                                                location: "Hollywood, California",
                                                                latitude: nil,
                                                                longitude: nil,
                                                                radius: 10000,
                                                                categories: [CDYelpBusinessCategoryFilter.bars],
                                                                locale: CDYelpLocale.english_unitedStates,
                                                                limit: 5,
                                                                offset: 0,
                                                                sortBy: CDYelpBusinessSortType.distance,
                                                                priceTiers: [CDYelpPriceTier.twoDollarSigns],
                                                                openNow: true,
                                                                openAt: nil,
                                                                attributes: nil) { [weak self] (response) in
            // Use weak self to avoid memory leaks
            guard let self = self, 
                  let response = response, 
                  let fetchedBusinesses = response.businesses, 
                  !fetchedBusinesses.isEmpty else {
                return
            }
            
            // Save the fetched data to your class variables
            self.businesses = fetchedBusinesses
            self.businessNames = fetchedBusinesses.map { $0.name ?? "Unknown Business" }
            
            // Update the table view on the MAIN THREAD
            DispatchQueue.main.async {
                self.tableView.reloadData()
            }
            
            // These prints will now show valid data (they run after the request finishes)
            print("Fetched businesses: \(self.businesses)")
            print("Business names: \(self.businessNames)")
        }
        
        // This print runs BEFORE the API request finishes — so empty data here is normal!
        print("Note: This runs before the API call completes, so data is empty right now.")
    }
    
    // MARK: - UITableView DataSource Methods
    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        // Return the number of businesses we fetched
        return businesses.count
    }
    
    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let cell = tableView.dequeueReusableCell(withIdentifier: "BusinessCell", for: indexPath)
        let business = businesses[indexPath.row]
        
        // Set cell content
        cell.textLabel?.text = business.name
        cell.detailTextLabel?.text = "Rating: \(business.rating ?? 0.0) ⭐"
        
        return cell
    }
}
Key Things to Remember
  • Asynchronous Execution: Network requests don't block the main thread, so code outside the closure runs first. Always handle fetched data inside the closure (or in a method called from it).
  • Main Thread for UI: Never update UI elements from a background thread — use DispatchQueue.main.async for all UI changes.
  • [weak self]: Using weak self in the closure prevents memory leaks, which is a crucial habit in Swift.

内容的提问来源于stack exchange,提问作者Coding while Loading

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.27 07:08:28