Home > Software design >  Swift - Can't use the object that initialized in closure
Swift - Can't use the object that initialized in closure

Time:07-12

as a newbie I got a problem. This is my custom model for API that I use which is called Poke API.

import UIKit

struct Pokemon: Codable {
    var results: [PokemonEntry]
}

struct PokemonEntry: Codable {
    var name: String
    var url: String
}

And this is the service that I use to get data from API:

import Foundation

class Webservice {
    
    func getData(completion: @escaping (Pokemon?, Error?) -> () ) {
          guard let url = URL(string: "https://pokeapi.co/api/v2/pokemon?limit151") else {return}
          URLSession.shared.dataTask(with: url) { data, res, err in
          if let err = err {
          completion(nil, err)
          return
          }
          do {
          let pokemons = try JSONDecoder().decode(Pokemon.self, from: data!)
          completion(pokemons, nil)
      //  pokemons.results.forEach({print($0.name)})
          } catch {
          completion(nil, error)
          print(error.localizedDescription)
          }
          }.resume()
  }
    
}

So in my viewController, I wanna get the pokemon object that returned from Webservice().getData function so I can use wherever I want but it comes as a nil, I can use it only inside of Webservice function's closure.

import UIKit

class ViewController: UIViewController {
        var pokeList: Pokemon?
        override func viewDidLoad() {
        super.viewDidLoad()
        
        
            Webservice().getData { pokemonResponse, error in
                if let error = error {
                    print(error.localizedDescription)
                }
                self.pokeList = pokemonResponse
                print("I can use pokeList here: \(self.pokeList?.results)")
            }
            
            print("I cant use pokeList out of Webservice closure, its nil: \(print(pokeList?.results))")
    }
}

CodePudding user response:

Try this instead:

class ViewController: UIViewController {
    var pokeList: Pokemon? {
        didSet {
            print("I can use pokeList out of the Webservice closure, it is: \(pokeList?.results ?? "<nil>")")
        }
    }
    override func viewDidLoad() {
        super.viewDidLoad()        
    
        Webservice().getData { pokemonResponse, error in
            if let error = error {
                print(error.localizedDescription)
            }
            self.pokeList = pokemonResponse
            print("I can use pokeList here: \(self.pokeList?.results)")
        }
    }
} 

The problem was that you were using pokeList before it got filled by the web service. It worked as follows:

  1. Web service task gets started (waits for a response asynchronously)
  2. Either response is received or your print outside of the block is called which already wants to use what gets filled in the block. The print is usually faster which results in the bug (also known as a race condition)
  • Related