Swift闭包内无法向数组追加对象问题求助
scoresArray Issue in Swift Live Scores App Hey there! Let's walk through why your scoresArray is staying empty even after your Alamofire request runs, and fix this step by step.
First, Let's Identify the Key Problems
Asynchronous Request Timing
Alamofire's network calls run in the background (asynchronously). If you try to checkscoresArrayright after callinggetLiveScoresinviewDidLoad, it will still be empty—because the request hasn't finished yet. The array only gets populated once the response comes back in the closure.Force Unwrapping Risks
You're using!to force-unwrap values everywhere (likeJSON(response.result.value!)andstring!). If any JSON field is missing, malformed, or of the wrong type, this will crash your app silently (or crash it outright), and the code that adds items toscoresArraywill never run.Unpresented Error Alert
When the request fails, you create an alert but never present it. You might think the request succeeded when it actually failed, leaving your array empty without knowing why.Missing UI Refresh
Even if you populate the array, if you don't reload your table/collection view, you won't see the data (though you mentioned the array itself is empty, so this is secondary for now).
Step-by-Step Fixes
1. Fix the Network Request Closure
Update your getLiveScores method to handle success/failure safely, present errors, and avoid memory leaks with a weak self reference:
func getLiveScores(url : String) { Alamofire.request(url, method: .get).responseJSON { [weak self] response in guard let self = self else { return } switch response.result { case .success(let value): let liveScoresJSON = JSON(value) self.updateLiveScore(json: liveScoresJSON) case .failure(let error): let alert = UIAlertController(title: "Error Occurred", message: error.localizedDescription, preferredStyle: .alert) alert.addAction(UIAlertAction(title: "Ok", style: .cancel)) // Actually present the alert so you know when requests fail self.present(alert, animated: true) } } }
2. Safely Parse JSON Without Force Unwrapping
Rewrite updateLiveScore to use optional binding, handle missing fields, and clear old data before adding new results:
func updateLiveScore(json : JSON) { // Clear old data to avoid duplicate entries on repeated requests scoresArray.removeAll() // Check if the "result" field is actually an array guard let results = json["result"].array else { print("No valid results found in the JSON response") return } for result in results { let match = Score() // Use default values for missing fields instead of force-unwrapping match.homeTeamName = result["event_home_team"].string ?? "Unknown Home Team" match.visitorTeamName = result["event_away_team"].string ?? "Unknown Away Team" match.matchScore = result["event_final_result"].string ?? "0-0" match.matchTime = result["event_status"].string ?? "Unknown Status" scoresArray.append(match) } // Refresh your UI on the main thread (network calls run in background) DispatchQueue.main.async { // Replace with your table/collection view reference self.yourTableView.reloadData() } }
3. Understand Asynchronous Flow
Remember: You can't access scoresArray immediately after calling getLiveScores. All code that relies on the array (like UI updates) must run after the updateLiveScore method finishes. That's why we put the reloadData() call inside updateLiveScore.
4. (Optional) Using inout for Array Parameters
If you want to pass the array as a parameter instead of using a class property, use Swift's inout keyword to modify the original array (instead of a copy):
func updateLiveScore(json : JSON, scoresArray: inout [Score]) { scoresArray.removeAll() guard let results = json["result"].array else { print("No valid results found") return } for result in results { let match = Score() match.homeTeamName = result["event_home_team"].string ?? "Unknown Home Team" // ... fill other properties ... scoresArray.append(match) } }
Call it like this:
self.updateLiveScore(json: liveScoresJSON, scoresArray: &self.scoresArray)
Final Notes
- Always avoid force-unwrapping (
!) unless you're 100% certain the value exists—optional binding (if let/guard let) and default values (??) are much safer. - Network operations run on background threads, so all UI updates must happen on the main thread (hence
DispatchQueue.main.async). - If your JSON structure doesn't match what you're expecting, use
print(liveScoresJSON)to log the full response and verify the field names/paths.
内容的提问来源于stack exchange,提问作者HoussamElgu

