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
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
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
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"
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With