My goals is to return a value when Alamofire is ready with his tasks. Below script is part of my singleton, that will be called by my viewController. I have looked around and there are examples of where a completion handler is implemented with Alamofire.
Singleton:
func getPeople() -> [Person] {
var persons:[Person] = []
Alamofire.request(requestURL, method: .get, parameters: nil, encoding: URLEncoding.default, headers: nil).responseJSON { (response) in
if let json = response.result.value as? Dictionary<String, Any> {
for field in json {
// Reference to person class
let person = Person()
// Properties will be fill in
...
...
// Add properties to the person object
persons.append(person)
}
}
}
return persons
}
Viewcontroller
I will call above function in my ViewController.swift
and add it to an array of People.
var persons = [Person]()
override func viewDidLoad() {
super.viewDidLoad()
let request = requestHandler()
self.persons = request.getPeople()
}
Because Alamofire will be asynchroon, I don't get returned the array of persons. How do I return the value persons
to a function that is called from my viewcontroller.swift
? I am using Swift 3.
You should use closures for this purpose:
func getPeople(success: (([Person])->Void)?, failure: ((Error)->Void)?) {
//create Alamofire request
//if everything was fine call success block with people array passed into it
//if failure occurred, call failure block with error.
//In any case you must call either `success` of `failure` here
}
Usage:
ServerManager.sharedInstance.getPeople(
success: { people in
//use people here, update UI if needed
},
failure: { error in
//show an error
})
You can read more about closures in swift here, but I will leave some explanation about your particular case:
Basically, (([Person])->Void)?
is a type of the closure. It means that the closure will accepts an array of Person
objects and is returning nothing (Void
). ?
in the end means that it's an optional parameter, so you can pass nil
when you call getPeople
function if you're not interested in the callback at all.
The same about ((Error)->Void)?
failure closure. If you call it, you must pass an Error
object to describe what actually went wrong.
Why you closures should be optional?
For example, you have an endpoint for sending user's location to the server. Sometimes you will want to run some code after request is finished and sometimes you don't care if it fails or not (you're just sending the coordinates in background). If you don't need a callback, you can pass an empty closure or nil. In fact there is nothing wrong in using empty closure like { _ in }
but it's a bit cleaner if you pass nil
. You can meet optional closures in some UIKit methods as well:
present(_:animated:completion:)
animate(withDuration:animations:completion:)
P.S. You can also design your method in such way that it has only one completion
callback with two optionals:
func getPeople(completionHandler: (([Person]?, Error?)->Void)?) {}
Personally I prefer two closures because it's more evident and readable, though it's a matter of taste.