swift protocol func with #escaping callback - swift

I created a protocol with a function definition that has a callback function parameter.
But when I try to use the callback in a class that implements the protocol, I get the error:
Closure use of non-escaping parameter 'callBack' may allow it to escape
The fix adds the #escaping flag to the function definition, but then it's no longer confirms to the function definition in the protocol.
This is the function I pass as call back:
private func handleSignUpRes(result:Bool, msg:String, uData:UserData){
if(result == false && (currVC != nil)){
Utilities.ShowAlert(title: "Error", msg: msg, vc: currVC!)
}
else if(result == true){
}
}

You need to pass in a closure that does not escape.
Escaping closures are closures that have the possibility of executing after a function returns.
Rewrite your closure to ensure that it cannot return a value after the function returns.
Unfortunately, without seeing the closure, I cannot tell you why the closure is escaping.
When you post the closure, I will edit my answer.

Related

How exactly does this completion handler work?

I've done some research on closures, closures that receive parameters, trailing closures, and completion handlers, but I'm having difficulty understanding when the two arguments to completion, manager and file are passed. In the pick() function definition, there is nowhere in the function scope that calls the completion with completion(manager, file) syntax. There is, however, a present method and I think I am missing something about present() that may include the call for the completion closure with proper arguments. I would appreciate your help.
public func pick(from vc: UIViewController?, withCompletion completion: #escaping (_ manager: HSDriveManager?, _ file: GTLRDrive_File?) -> Void) {
viewer?.completion = completion
viewer?.shouldSignInOnAppear = true
//As of now, present() seems to include the calling of the completion closure.
//self is the HSDriverPicker class
print(type(of: self))
vc?.present(self, animated: true)
}
The code below is the function call for pick. What confuses me is that in order for manager and file to act like arguments of the completion closure somewhere in the function call there has be a part that passes those arguments. I don't see them. I would appreciate your insight on what exactly the present method does and whether it takes care of calling the closure with necessary arguments.
picker.pick(from: self) {
(manager, file) in
print("picked file: \(file?.name ?? "-none-")")
let destinationPath = "/Users/james/Desktop/tests"
manager!.downloadFile(file!, toPath: destinationPath, withCompletionHandler: {
error in
if error != nil {
print("Error downloading : \(error?.localizedDescription ?? "")")
}
else {
print("Success downloading to : \(destinationPath)")
}
})
}
The function present(_:animated:) won't be responsible for calling the completion closure. viewer?.completion = completion means viewer will take care of calling the completion closure in its scope(that's why #escaping is used, i.e the closure will outlive the scope that you've passed it to). The two arguments manager and file aren't supposed to be passed by the client but are exposed to the client so that whoever calls the picker.pick could use these properties and perform the certain action that will then called inside viewer. The closure with parameters simply means the client could use that parameter without worrying about who and when will those parameters be passed.

Swift: How can I call my function inside completion body?

I have a predefined function that has a completion param:
func checkNotificationEnabled(_ resultBlock : ((Bool)->())? = nil){
Bool enabled = false
... a big block of code that gets enabled value
...
... end block
resultBlock?(enabled)
}
I need to get the true/false and pass it to another function:
#objc
func isNotificationEnabled(_
resolve: RCTPromiseResolveBlock, rejecter reject: RCTPromiseRejectBlock
) -> Void {
checkNotificationEnabled { (enabled:Bool) in
resolve(enabled)
}
}
Got the error: Escaping closure captures non-escaping parameter 'resolve'
How can I pass enabled to resolve ?
Presuming RCTPromiseResolveBlock on func isNotificationEnabled to be some block that you're trying to execute with respect to completion of func checkNotificationEnabled, the completion actually 'escapes' (outlives) the scope of the function, and the compiler simply complains that your RCTPromiseResolveBlock may not be alive (in execution) during the completion callback
You'll have to mark #escaping to the 'resolve' parameter to get around this.
You'll definitely get more clarity when you understand what escaping closures are. Have a look at this question: Escaping Closures in Swift
Hope this helps.
resolve argument is passed to a function checkNotificationEnabled but is not marked #escaping
(Bool)->())? = nil optional is nil by default, so no need to assign nil
(Bool)->() this is equal to more readable version Void.
typealias ResultBlock = (Bool) -> Void
func checkNotificationEnabled(_ resultBlock: ResultBlock?) {
var enabled = false
... a big block of code that gets enabled value
...
... end block
resultBlock?(enabled)
}
#objc
func isNotificationEnabled(_
resolve: #escaping RCTPromiseResolveBlock, rejecter reject: RCTPromiseRejectBlock) {
checkNotificationEnabled { enabled in
resolve(enabled)
}
}
Escaping closures
A closure is said to escape a function when the closure is passed as
an argument to the function, but is called after the function returns.
When you declare a function that takes a closure as one of its
parameters, you can write #escaping before the parameter’s type to
indicate that the closure is allowed to escape.
One way that a closure can escape is by being stored in a variable
that is defined outside the function. As an example, many functions
that start an asynchronous operation take a closure argument as a
completion handler. The function returns after it starts the
operation, but the closure isn’t called until the operation is
completed—the closure needs to escape, to be called later.
source

Method with a completion block that can be used from objective-c

I need to call a swift function from objective-c. This needs to have a callback (that will be executed from obj-c)
#objc public func getTokens(forAuthentificationCode auth_code : String, completion: ()->()? )
I get "Method cannot be marked #objc because the parameter..."
How to make a function with a completion block that can be used from objective-c?
You declared the completion parameter as ()->()?, which translates to a closure with no parameters and that returns a Void?. Objective-C supports optionals only for class arguments, thus the completion block is not Objective-C compatible.
I think however that you intended to declare the whole closure as optional, in order to allow callers to pass nil, in that case you need to wrap the closure definition into another set of parentheses: completion: (() -> Void)?. I changed the return type from () to Void for better readability, as Void is just an alias for ().

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

How to understand this GCDWebServer swift unit test code?

I have come across this code:
class WebServerTests: XCTestCase {
let webServer: GCDWebServer = GCDWebServer()
var webServerBase: String!
/// Setup a basic web server that binds to a random port and that has one default handler on /hello
private func setupWebServer() {
webServer.addHandlerForMethod("GET", path: "/hello", requestClass: GCDWebServerRequest.self) { (request) -> GCDWebServerResponse! in
return GCDWebServerDataResponse(HTML: "<html><body><p>Hello World</p></body></html>")
}
I am confused by the webServer.addHandlerForMethod part. It seems to me it is already a complete function call (webServer.addHandlerForMethod("GET", path: "/hello", requestClass: GCDWebServerRequest.self)). Therefore I do not understand why it is followed by a closure ( {(request) -> ... )
EDIT: Clarify what I do not understand
According to the documentation on https://github.com/swisspol/GCDWebServer, the function signature in obj-c is:
[webServer addDefaultHandlerForMethod:#"GET"
requestClass:[GCDWebServerRequest class]
asyncProcessBlock:^(GCDWebServerRequest* request, GCDWebServerCompletionBlock completionBlock) {
Therefore I expect its swift counterpart will be called somewhat like this:
webServer.addHandlerForMethod("GET", path: "/hello", requestClass: GCDWebServerRequest.self, { (request) -> GCDWebServerResponse! in
return GCDWebServerDataResponse(HTML: "<html><body><p>Hello World</p></body></html>")
})
i.e. the handling of the incoming request is passed as the third parameter. But since the closure comes after the closing ')', it does not look like part of the function call at all.
Why the function signature is mapped from obj-c to swift this way?
In Swift, you can use this syntax if the last argument to a function is a closure. Here's the example from the Swift language guide section about closures (scroll down to Trailing Closures):
func someFunctionThatTakesAClosure(closure: () -> ()) {
// function body goes here
}
// here's how you call this function without using a trailing closure:
someFunctionThatTakesAClosure({
// closure's body goes here
})
// here's how you call this function with a trailing closure instead:
someFunctionThatTakesAClosure() {
// trailing closure's body goes here
}
And then there's also this note:
If a closure expression is provided as the function’s only argument and you provide that expression as a trailing closure, you do not need to write a pair of parentheses () after the function’s name when you call the function.
This means it's also legal to write this:
someFunctionThatTakesAClosure {
// closure body
}
… which helps provide a nice meta-programming syntax. For example:
let lock = NSLock()
func locked(closure: () -> ()) {
lock.lock();
closure()
lock.unlock();
}
locked {
NSLog("Hello, world!")
}
The closure is where the handling of the incoming request is done. It tells the server to run the closure's code when a GET method that requests /hello path comes.
In the code you posted code in the closure creates a response that the server returns.