Empty call to a callback in swift. How it can be useful? - swift

I had a glance at the AlamoFire lib code to learn something more about it and I've found this definition:
var dataTaskDidReceiveData: ((NSURLSession!, NSURLSessionDataTask!, NSData!) -> Void)?
I suppose it defines that dataTaskDidReceiveData is a callback with some params and without a return.
Then I see that this callback is used in a "strange" way that I can't understand:
func URLSession(session: NSURLSession!, dataTask: NSURLSessionDataTask!, didReceiveData data: NSData!) {
if let delegate = self[dataTask] as? Request.DataTaskDelegate {
delegate.URLSession(session, dataTask: dataTask, didReceiveData: data)
}
dataTaskDidReceiveData?(session, dataTask, data)//??????
}
How this call works exactly?
dataTaskDidReceiveData?(session, dataTask, data)//??????

dataTaskDidReceiveData is defined as optional closure. ? is optional chaining operator. So dataTaskDidReceiveData?(session, dataTask, data) reads like: If dataTaskDidReceiveData is not nil call dataTaskDidReceiveData closure, else do nothing.

Related

Swift - Call completion handler from a second function

thanks for reading this, i hope you can understand my problem. Basically what i would like to do is this:
private func doGet(path:String, body:Dictionary, completion: #escaping (JSON?, Bool) -> Void) {
completion(data, bool)
}
func getData(body){
return doGet("/api/data", body: body)
}
// The function gets called in another class
getData(data){ (data, bool)
// do something with data
}
I know this code doesnt work but thats what i would like to do. I dont want to call doGet from the other class i want to have a function in between. Maybe this is not possible. Please let me know if i didnt make myself clear and thanks in advance. :)
getData also needs to have a completion handler parameter, since it too returns the results asynchronously.
So, you'd have something like this:
getData(body: SomeType, completion: #escaping (Data, Bool) -> Void) {
doGet(path: "/api/data", body: body) { (param1, param2) in
// turns param1 and param2 into parameters to invoke
// the completion handler with
completion(data, true)
}
}

Issue with Alamofire challenge delegate and escaping closure

I am using AF and using it's delegate to catch the authentication challenge returned by my server.
func connectGetRequest(_ url : URL){
let sessionManager = Alamofire.SessionManager.default
sessionManager.request(url).responseString { response in
print("Response String: \(response.result.value)")
}
let delegate: Alamofire.SessionDelegate = sessionManager.delegate
delegate.taskDidReceiveChallengeWithCompletion = { session, task, challenge, completionHander in
print("session is \(session), task is \(task) challenge is \(challenge.protectionSpace.authenticationMethod) and handler is \(completionHander)")
if(challenge.protectionSpace.authenticationMethod == "NSURLAuthenticationMethodServerTrust"){
completionHander(.performDefaultHandling,nil)
}else{
print("challenge type is \(challenge.protectionSpace.authenticationMethod)")
// Following line give me the error: "passing non-escaping parameter 'completionHander' to function expecting an #escaping closure"
self.handleAuthenticationforSession(challenge,completionHandler: completionHander)
}
}
delegate.dataTaskDidReceiveData = {session , task, data in
print("received data \(data)")
}
}
func handleAuthenticationforSession(_ challenge: URLAuthenticationChallenge,completionHandler: #escaping (Foundation.URLSession.AuthChallengeDisposition, URLCredential?) -> Void) {
Authhandler.handleChallenge(forURLSessionChallenge: challenge, completionHandler: completionHandler)
}
issue I have:
If I use the code above as it is, I get
error: "passing non-escaping parameter 'completionHander' to function expecting an #escaping closure"
If I make the parameter of the function handleAuthenticationSession non escaping, I get :
func handleAuthenticationforSession(_ challenge: URLAuthenticationChallenge,
completionHandler: (Foundation.URLSession.AuthChallengeDisposition, URLCredential?) -> Void) {
}
error: "Closure use of non-escaping parameter 'completion' may allow it to escape"
Also, handleChallenge method from AuthHandler class (which is a part of obj-c framework) looks like following.
-(BOOL)handleChallenge:(NSURLAuthenticationChallenge *)challenge
completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition disposition,
NSURLCredential *credential))completionHandler;
So basically I am stuck in a deadlock while I use Alamofire's closure syntax to delegate the auth challenge.
It seems to me the missing piece of your question is whether the completion handler in Authhandler.handleChallenge is escaping. It is, right?
But the taskDidReceiveChallengeWithCompletion completionHandler is non-escaping. So you're trying to figure out how to let it escape when it's not allowed to escape.
Looking at the Alamofire source code, about 3 months ago, they changed that completionHandler to be #escaping! See here: https://github.com/Alamofire/Alamofire/commit/b03b43cc381ec02eb9855085427186ef89055eef
You need to update to a version of Alamofire after that PR got merged or you need to figure out how to handle the completionHandler in a fully non-escaping way. Meaning, your Authhandler.handleChallenge can't have an escaped completionHandler.

IOs Swift : How does completion closure work

Can anybody explain me, how does this code work
private func viewWillTransition(completion:(() -> Void)?)
{
if completion != nil
{
completion!()
}
}
This is a basic scheme of implementing callbacks in Swift.
The function takes parameter completion of type () -> Void)?, meaning "an optional closure taking no parameters and not returning a value."
The code inside tests the optional value of closure for nil. If it is not nil, the code unwraps it with !, and makes a call.
A somewhat more idiomatic way of implementing this in Swift is with if let construct:
private func viewWillTransition(completion:(() -> Void)?) {
if let nonEmptyCompletion = completion {
nonEmptyCompletion()
}
}

Confusing closures and completion handles

Im a new programmer and am very lost.
I am taking this online iOS dev course and I was configuring collection view cell.
However, closures and completion handles were used and it was never mentioned before.
import UIKit
class PersonCell: UICollectionViewCell {
#IBOutlet weak var img: UIImageView!
func configureCell(imgUrl: String) {
if let url = NSURL(string: imgUrl) {
downloadImg(url)
}
}
func downloadImg(url: NSURL) {
getDataFromURL(url) { (data, response, error) in
dispatch_async(dispatch_get_main_queue()) { () -> Void in
guard let data = data where error == nil else {return}
self.img.image = UIImage(data: data)
}
}
}
func getDataFromURL(url: NSURL, completion: ((data: NSData?, response: NSURLResponse?, error: NSError?) -> Void)) {
NSURLSession.sharedSession().dataTaskWithURL(url) { (data, response, error) in
completion(data: data, response: response, error: error)
} .resume()
}
}
Can someone explain to me what the completion handler is doing after the "getDataFromURL" function. Also what are the closures doing? is "(data, response, error)" getting passed around? How does swift know that "data" is suppose to be NSData and etc in the "(data, response, error)"?
What does the closure after the "dataTaskWithURL" do (is it setting up the completion handler"?
Thank you!
These are good questions!
A closure is simply a collection (aka block) of lines of code that you can treat like a variable and execute like a function. You can refer to a closure with a variable name and you can pass a closure around as a parameter in function calls just like any other variable, eventually executing the code when appropriate. A closure can accept certain parameters to use in its code and it can include a return value.
Example:
This is a closure that accepts two strings as parameters and returns a string.
let closure: (String, String) -> String = { (a: String, b: String) -> String in
return a + b
}
Thus, the following will print "Hello Jack!":
print(closure("Hello ", "Jack!"))
A closure also has a variable type (just like "hello" is a String and 1 is an Int). The variable type is based on the parameters that the closure accepts and the value that the closure returns. Thus, since the closure above accepts two strings as parameters and returns a string, its variable type is (String, String) -> String. Note: when nothing is returned (i.e. the return type is Void), you can omit the return type (so (Int, String) -> Void is the same thing as (Int, String)).
A completion handler is a closure that you can pass to certain functions. When the function completes, it executes the closure (e.g. when a view finished animating onto the screen, when a file finished downloading, etc.).
Example:
"Done!" will be printed when the view controller is finished presenting.
let newClosure: () -> Void = { () -> Void in
print("Done!")
}
let someViewController = UIViewController(nibName: nil, bundle: nil)
self.presentViewController(someViewController, animated: true, completion: newClosure)
Let's focus on the getDataFromURL function you wrote first. It takes two parameters: a variable of type NSData and a closure of type (NSData?, NSURLResponse?, NSError?) -> Void. Thus, the closure (which is named completion) takes three parameters of types NSData?, NSURLResponse?, and NSError?, and returns nothing, because this is how you defined the closure in the function declaration.
You then call getDataFromURL. If you read the documentation, you'll see that the closure you pass to this function as the second parameter is executed when the load task is complete. The function declaration for dataTaskWithURL is what defines the variable types that the closure accepts and returns. Within this closure, you are then calling the closure you passed to the getDataFromURL function.
Within this latter closure (the one you define in downloadImg when you are calling getDataFromURL), you are checking to see if the data that you downloaded is not nil, and if not, you are then setting the data as an image in a UIImageView. The dispatch_async(dispatch_get_main_queue(), ...) call simply ensures that you are setting the new image on the main thread, as per Apple's specifications (you can read more about threads elsewhere).
make an typealias to understand this is easy :
typealias Handle = (data: NSData?, response: NSURLResponse?, error: NSError?) -> Void
//the func should be
func getDataFromURL(url: NSURL, completion: Handle)
//when you call it. it needs an url and an Handle
getDataFromURL(url:NSURL, completion: Handle)
// so we pass the url and handle to it
getDataFromURL(url) { (data, response, error) in
dispatch_async(dispatch_get_main_queue()) { () -> Void in
guard let data = data where error == nil else {return}
self.img.image = UIImage(data: data)
}
}
//setp into the func
func getDataFromURL(url: NSURL, completion: Handle){
// call async net work by pass url
NSURLSession.sharedSession().dataTaskWithURL(url) { (data, response, error) in
// now data / response / error we have and we invoke the handle
completion(data: data, response: response, error: error)
} .resume()
}
hope it be helpful :D

The meaning of urlSession.dataTaskWithRequest(request)

When I read the book about swift in the Network Development chapter, I met some code which I cannot understand. The code is as follows:
let sessionTask = urlSession.dataTaskWithRequest(request) {
(data, response, error) in
handler(response, data)
}
the prototype of this function in swift is:
public func dataTaskWithRequest(request: NSURLRequest, completionHandler: (NSData?, NSURLResponse?, NSError?) -> Void) -> NSURLSessionDataTask
As you can see, the prototype has 2 parameters, one is request, another is completionHandler. But in the above code, it also has one parameter. And also I cannot understand the code in the curly braces, where do the 3 variable data, response, error come from? I cannot find any definition of the 3 variables. Who can help me understand the code, thanks in advance.
It is called a trailing closure, it's a cleaner way of passing a function to another function if that function is the last argument. The same code can be written as:
let sessionTask = NSURLSession.sharedSession()
let request = NSURLRequest()
sessionTask.dataTaskWithRequest(request, completionHandler: {(data: NSData?, response: NSURLResponse?, error: NSError?) -> Void in
})
If you need to pass a closure expression to a function as the function’s final argument and the closure expression is long, it can be useful to write it as a trailing closure instead. A trailing closure is a closure expression that is written outside of (and after) the parentheses of the function call it supports
https://developer.apple.com/library/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Closures.html#//apple_ref/doc/uid/TP40014097-CH11-ID102
func aFunction(callback: (done: Bool) -> Void) {
let finished = true
callback(done: finished)
}
aFunction { (done) -> Void in
print("we are done \(done)")
}