How to return value from a Closure in Swift?

后端 未结 2 514
逝去的感伤
逝去的感伤 2021-02-07 12:09

So I am using fabric plugin/Twitter-kit to use twitter api in my application. I want to get the image URL of profile picture of a celebrity. here is my code below.



        
相关标签:
2条回答
  • 2021-02-07 12:43

    You need to return from an @escaping closure. Change the function

    func getImageURL(celebrity: String) -> String {
    
    }
    

    to

    func getImageURL(celebrity: String, completion: @escaping(String)->()) {
    
          // your code
          completion(imgURL)
    }
    

    You can use it as given below

    getImageURL(celebrity: String) { (imgURL) in
    
          self.imgURL = imgURL // Using self as the closure is running in background
    }
    

    Here is an example how I write multiple methods with closures for completion.

    class ServiceManager: NSObject {
    
    //  Static Instance variable for Singleton
    static var sharedSessionManager = ServiceManager()
    
    //  Function to execute GET request and pass data from escaping closure
    func executeGetRequest(with urlString: String, completion: @escaping (Data?) -> ()) {
    
        let url = URL.init(string: urlString)
        let urlRequest = URLRequest(url: url!)
    
        URLSession.shared.dataTask(with: urlRequest) { (data, response, error) in
            //  Log errors (if any)
            if error != nil {
                print(error.debugDescription)
            } else {
                //  Passing the data from closure to the calling method
                completion(data)
            }
        }.resume()  // Starting the dataTask
    }
    
    //  Function to perform a task - Calls executeGetRequest(with urlString:) and receives data from the closure.
    func downloadMovies(from urlString: String, completion: @escaping ([Movie]) -> ()) {
        //  Calling executeGetRequest(with:)
        executeGetRequest(with: urlString) { (data) in  // Data received from closure
            do {
                //  JSON parsing
                let responseDict = try JSONSerialization.jsonObject(with: data!, options: .allowFragments) as? [String: Any]
                if let results = responseDict!["results"] as? [[String:Any]] {
                    var movies = [Movie]()
                    for obj in results {
                        let movie = Movie(movieDict: obj)
                        movies.append(movie)
                    }
                    //  Passing parsed JSON data from closure to the calling method.
                    completion(movies)
                }
            } catch {
                print("ERROR: could not retrieve response")
            }
        }
      }
    }
    

    Below is the example how I use it to pass values.

    ServiceManager.sharedSessionManager.downloadMovies(from: urlBase) { (movies : [Movie]) in   // Object received from closure
          self.movies = movies
          DispatchQueue.main.async {
                //  Updating UI on main queue
                self.movieCollectionView.reloadData()
          }
    }
    

    I hope this helps anybody looking for the same solution.

    0 讨论(0)
  • 2021-02-07 12:53

    You are correct, sendTwitterRequest will return after your function has already returned, so there is no way for the outer function to return an imageURL.

    Instead, in the closure, take the return value that you want in the cell and store it somewhere (in a member variable) and have the tableView update it itself (e.g. with tableView.reloadData()).

    This will cause it to get the cell again (cellForRow ...). Change the implementation to use the member variable where you stored the value from the call.

    0 讨论(0)
提交回复
热议问题