Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Closures return value (previously completionBlock)

Tags:

swift

ios8

I'd like to return some values after the long term operation is completed. But furthermore I'd like to split the logic and the gui.

For example; I have two classes

  1. SomeServices.swift which has a method named "getDataFromService..."
  2. MyTableViewController.swift which will display the result from "getDataFromService"

So, previously in Objective-C I've just add a method in SomeServices like this:

(void)getDataFromService:(void (^)(NSArray *, NSError *))completionBlock{ ...... }

In this method I've just called completionBlock(myData, myError) to return my values to the tableviewcontroller.

What would be the equivalent closure which I have to define in SomeServices.swift and how will it be called in MyTableViewController?

I know how to call a simple closures like this one:

   ....({
            responseData, error  in
            if(!error){
                //Do something
            }
        })

But I don't have any ideas how to define a closure with a completionBlock equivalent.

Any help would be appreciated

like image 891
user1011394 Avatar asked Jun 04 '14 07:06

user1011394


3 Answers

The plus of closures are, that you can pass everything you want. Methods or functions - it doesn't matter.

You can pass a function within the parameters and just call it.

func someFunctionThatTakesAClosure(completionClosure: () -> ()) {
    // function body goes here
    if(error = false) {
       completionClosure()
    }

}

//Call it
someFunctionThatTakesAClosure({
    //Completions Stuff
    println("someFunctionThatTakesAClosure")
});

Excerpt From: Apple Inc. “The Swift Programming Language.” iBooks. https://itun.es/ch/jEUH0.l

like image 88
Sam Avatar answered Sep 25 '22 03:09

Sam


The answer is in the language guide:

Assume you want to return a String. This is the syntax

({(responseData: DataClass, error: ErrorClass) -> String in
//do stuff - calculations etc..
return calculatedString
})

Here is an example that takes two strings and concatenates them, and returns the result:

let sumStrings = ({(first: String, second: String) -> String in
    return first + " " + second
})

then you can do the following:

sumStrings("Hello","Swift")             // "Hello Swift"
like image 40
Kaan Dedeoglu Avatar answered Sep 23 '22 03:09

Kaan Dedeoglu


Here is how I use a singleton ServiceManager to achieve this.

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 the singleton class.

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.

like image 25
caffieneToCode Avatar answered Sep 22 '22 03:09

caffieneToCode