Swift中闭包外无法访问JSON数据的解决方法咨询
Hey there! Let's break down what's going on here and fix your problem step by step:
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).
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.
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 } }
- 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.asyncfor all UI changes. - [weak self]: Using
weak selfin the closure prevents memory leaks, which is a crucial habit in Swift.
内容的提问来源于stack exchange,提问作者Coding while Loading

