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.
Related
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
Swift 4.2, Xcode 10.1
In the order processing app I'm working on, the user may do a search for orders already processed or submitted. When that happens, it will check to see if it has a cache of orders, and if it does not, it will refill that cache using an asynchronous API request, then check the cache again.
The function that refills the cache is a private static one that accepts an escaping completion handler. Whenever I have used that completion handler in the past, all I had to do was add a closure at the end of the function call. This was before I was instructed to make a cache of all data wherever possible, and only use the API to refill that cache. Since then, the function has become private, because there will never be a need to call the API directly from anywhere but within this class.
Now, when I put the closure directly after the function call, it's giving me an error that basically says I'm passing a #nonescaping closure instead of an #escaping closure:
"Cannot invoke 'getAndCacheAPIData' with an argument list of type '(type: Codable.Type, (String?) -> Void)', Expected an argument list of type '(type: CodableClass.Type, #escaping (String?) -> Void)'"
I've never had to explicitly declare a closure to be #escaping before, nether does it seem to be possible. I suspect that because the function is both private AND static, there's some kind of issue happening with the way closures are inferred to be #escaping. I'm out of my depth. I could try converting the static class to a singleton, but I'm hesitant to refactor a bunch of working code because of one error until I'm absolutely sure that change will resolve the issue, and that what I'm trying to do isn't possible unless I change my approach.
Here's the code:
public static func fillSearchResultArray<ManagedClass: NSManagedObject>(query:String, parameters:[String], with type: ManagedClass.Type, completionHandler: #escaping (String?)->Void)
{
let codableType:Codable.Type
switch type
{
case is ClientTable.Type:
codableType = ClientData.self
case is OrderTable.Type:
codableType = OrderData.self
case is ProductTable.Type:
codableType = ProductData.self
default:
completionHandler("Unrecognized type.")
return
}
let fetchedData:[ManagedClass]
do
{
fetchedData = try PersistenceManager.shared.fetch(ManagedClass.self)
}
catch
{
completionHandler(error.localizedDescription)
return
}
if fetchedData.isEmpty
{
AppNetwork.getAndCacheAPIData(type: codableType)//error here
{(firstErrorString) in
//move search array data to the cache
if firstErrorString.exists
{
completionHandler(error)
}
else
{
AppNetwork.fillSearchResultArray(query: query, parameters: parameters, type: type)
{ errorString in
completionHandler(errorString)
}
}
}
return
}
else
{ ...
The signature of the function being called:
private static func getAndCacheAPIData <CodableClass: Any & Codable>(type:CodableClass.Type, completionHandler: #escaping (String?)->Void)
Why is swift inferring this closure to be the default #nonescaping when before it always inferred it to be #escaping?
The problem has nothing to do with the closure, or static, or private. It has to do with the type parameter. You cannot call this method:
private static func getAndCacheAPIData <CodableClass: Any & Codable>(type:CodableClass.Type, completionHandler: #escaping (String?)->Void)
with a variable of type Codable.Type. The type value you pass must be a concrete type, known at compile-time. If you want to pass a variable, you can't use a generic. It would have to be:
private static func getAndCacheAPIData(type: Codable.Type, completionHandler: #escaping (String?)->Void)
Alternately, you can call this as:
AppNetwork.getAndCacheAPIData(type: Int.self) {(firstErrorString) in ... }
or some other known-at-compile-time type.
Probably what you really want here is something like:
let completion: (String?) -> Void = {(firstErrorString) in ... }
switch ... {
case ...:
AppNetwork.getAndCacheAPIData(type: Int.self, completion: completion)
case ...:
AppNetwork.getAndCacheAPIData(type: String.self, completion: completion)
...
The basic problem is that protocols do not conform to themselves, so a variable of type Codable.Type does not satisfy the : Codable requirement. This comes down to the same reason you can't just call:
AppNetwork.getAndCacheAPIData(type: Codable.self) {...}
Alternately, you could refactor it this way:
private static func handleAPI<CodableClass: Codable>(type: CodableClass.Type) {
getAndCacheAPIData(type: type.self) { _ in ... the completion handler ..}
}
switch ... {
case ...:
AppNetwork.handleAPI(type: Int.self)
case ...:
AppNetwork.handleAPI(type: String.self)
...
Side note: Any & is meaningless here. You just meant <CodableClass: Codable>.
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
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)")
}
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.