What does let NAME1 = NAME2( params ) { var1, var2 in mean in Swift? - swift

I am new to Swift and some constructs I can't even read. For example:
let task = URLSession.shared.dataTask(with: request) { data, response, error in
What is it? Function call because of ()? Or inline anonymous class declaration because of {}? Or enumeration because of in?

That is trailing closure syntax. URLSession.dataTask(with:) returns a URLSessionDataTask instance and its last input argument is a closure of type (Data?, URLResponse?, Error?) -> Void, where the async network request response or an error is returned.
You can use the URLSessionDataTask object to call start or cancel the network request.
If you don't use trailing closure syntax, it might be more clear what you're seeing:
let task = URLSession.shared.dataTask(with: request, completionHandler: { data, response, error in
// you can use data, response and error inside the closure here
})
Which is equivalent to:
let task = URLSession.shared.dataTask(with: request) { data, response, error in
The trailing closure syntax simply allows use to omit the input argument label for the last input argument when it is a closure and put the closure with the curly brackets after the closing parentheses of the function call.

In Swift this is called a completion handler. It's basically an anonymous function that you pass in which the function you are calling will call when it's done. In Swift this can either be written as a normal parameter or as a closure after the function call, which is what is happening in your example.
You can see in the function definition from the documentation
func dataTask(with request: URLRequest, completionHandler: #escaping (Data?, URLResponse?, Error?) -> Void) -> URLSessionDataTask
That there are two parameters to the function request and completionHandler
In Swift, when the final parameter is a function it can be, and often is, written as a closure after the function call.
NOTE:
As was suggested in another comment, this is NOT the return value. You'll see this function has a return value of URLSessionDataTask which is being assigned to task in your example

Related

Assigning value to inout parameters within closure in Swift 3

I have an error when I try to assign a value to the function parameter while inside a completion block, I get an error that reads 'escaping closures can only capture inout parameters explicitly by value' .
How could I fix this? Any tip is much appreciated!
func fetchCurrentUser(user: inout User? ) {
self.fetchUser(withId: AuthProvider.sharedInstance.currentUserId(), completionHandler: {
fetchedUser in
guard let newUser = fetchedUser else { return }
user = newUser // error Here
})
}
This will not work, because you use a completion handler. The self.fetchUser will (almost) immediately return and the completion handler will be executed whenever the background work (most likely a network request) is finished.
Your function fetchCurrentUser calls self.fetchUser and than returns so it will return before the completion block is even executed.
You cannot use inout parameter on escaping closures (this is what the error message also tells you). A escaping closure is a closure which will be executed after the function which you pass it in returns.
You can either rewrite your function to also use a completion hander or change you function to wait for the completion handler to run before ending the fetchCurrentUser function. But for the second approach please be aware that this also will block the caller of the function from executing anything else.
I suggest this refactor:
func fetchCurrentUser(callback: #escaping (User) -> ()) {
self.fetchUser(withId: AuthProvider.sharedInstance.currentUserId(), completionHandler: {
fetchedUser in
guard let newUser = fetchedUser else { return }
callback(newUser)
})
}
or if you want fetchCurrentUser to be synchronous you could use semaphores

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.

Swift closures supplied to an async call dont take any arguments nor return any arguments

In CS193P Stanford lectures on iTunesU lecturer mentions in lecture 8 at 30:14 re multithreading that the closure you supply to an async call "takes no arguments and returns no arguments". That surprises me because a little while later he gives an example of an iOS method (37:01) where the closure is able to return arguments i.e.:
let task = session.dataTask(with: url) { (data: Data?, response, error ) in ...
Also there are many other iOS methods that seem to return arguments to their completion closures. Am I missing something here? Why can't the closures supplied with an async call return arguments in a similar way to the dataTask call above.
dataTask(with:) is not the same thing as DispatchQueue's async.
First of all, remember that you're using a trailing closure. When the last argument to a function is a closure (i.e. a "block" of code), the closure may be placed outside of the parentheses. The following code, which uses the trailing closure syntax:
someQueue.async {
//do something
}
is equivalent to the following code, which does not use the trailing closure syntax.:
someQueue.async(execute: {
//do something
})
Also, keep in mind that closures do not "return" parameters, they accept parameters. The parameters are the input to the closure, while the return value is the output from the closure.
The function signature for DispatchQueue's async is:
func async(
group: DispatchGroup? = default,
qos: DispatchQoS = default,
flags: DispatchWorkItemFlags = default,
execute work: #escaping () -> Void
)
group, qos, and flags have default values, so they can be ignored here. The important part is the execute parameter, whose type is #escaping () -> Void. That means "a closure which takes no arguments and returns Void (i.e. no value)."
The function signature for URLSession's dataTask method is:
func dataTask(
with request: URLRequest,
completionHandler: #escaping (Data?, URLResponse?, Error?) -> Void
) -> URLSessionDataTask
completionHandler is of type #escaping (Data?, URLResponse?, Error?) -> Void, which means it accepts three parameters (an optional Data, an optional URLResponse, and an optional Error) and returns Void.
Both functions accept closures as parameters, but they accept closures with different signatures. async accepts a closure which accepts no parameters, but dataTask accepts a closure which accepts three parameters. There's nothing "magic" about either of these functions; they simply accept closures, and you can write your own functions to accept closures if you like.
Not all asynchronous APIs use multithreading. Memory management has additional considerations when data is updated/transmitted between threads. The lecturer's statements may have been made in the specific context of multithreaded APIs. When your closure is processed in the same thread that issued the function call (or on the main thread), these considerations may not apply.

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)")
}