Assigning value to inout parameters within closure in Swift 3 - swift

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

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

is this a proper completion handler?

I had a very slow bottom sheet, showing up blank then after a while loading data. I tried to apply a completionHandler isLoadedCompletionHandler, solution worked, but my colleague told me this is not a "completion handler". Could you explain me why this is working. And how? Is this a proper completion handler?
func buttonDetailTapped(with travelSolutionId: String) {
guard let currentPurchaseSolution = purchaseSolutions.value.first(where: { $0.xmlId == purchaselSolutionId }) else {return}
getAllPurchaseDetail(searchId: searchId.value, solutionId: purchaseSolutionId)
.subscribe(onNext: { [weak self] purchaseDetails in
let isLoadedCompletionHandler: ([PurchaseDetail]) -> Void = { theArray in
self?.result.onNext(.showPurhcaseSolutionDetails(purchaseDetails, currentTravelSolution))
}
isLoadedCompletionHandler(purchaseDetails)
})
.disposed(by: disposeBag)
}
A completion handler is a function that you pass into your function, which usually gets called upon the completion of some asynchronous task.
Your function of buttonDetailTapped contains no parameters that are functions (i.e. (Thing, Error) -> Void) which are called upon completion, and thus you cannot know from calling this function when it completes.
Your function may go ahead and do other things when it's done, but there is no completion handler.
isLoadedCompletionHandler is not a completion handler because it is called immediately after it is assigned.
A completion handler is a closure that you pass into a function, that will be called when whatever that function is doing asynchronously completes. You are not passing isLoadedCompletionHandler anywhere.
You could have just written
getAllPurchaseDetail(searchId: searchId.value, solutionId: purchaseSolutionId)
.subscribe(onNext: { [weak self] purchaseDetails in
self?.result.onNext(.showPurhcaseSolutionDetails(purchaseDetails, currentTravelSolution))
})
.disposed(by: disposeBag)
and achieved the same result.

Swift #escaping and Completion Handler

I am trying to understand 'Closure' of Swift more precisely.
But #escaping and Completion Handler are too difficult to understand
I searched many Swift postings and official documents, but I felt it was still not enough.
This is the code example of official documents
var completionHandlers: [()->Void] = []
func someFunctionWithEscapingClosure(completionHandler: #escaping ()->Void){
completionHandlers.append(completionHandler)
}
func someFunctionWithNoneescapingClosure(closure: ()->Void){
closure()
}
class SomeClass{
var x:Int = 10
func doSomething(){
someFunctionWithEscapingClosure {
self.x = 100
//not excute yet
}
someFunctionWithNoneescapingClosure {
x = 200
}
}
}
let instance = SomeClass()
instance.doSomething()
print(instance.x)
completionHandlers.first?()
print(instance.x)
I heard that there are two ways and reasons using #escaping
First is for storing a closure, second is for Async operating purposes.
The following are my questions:
First, if doSomething executes then someFunctionWithEscapingClosure will executing with closure parameter and that closure will be saved in global variable array.
I think that closure is {self.x = 100}
How self in {self.x = 100} that saved in global variable completionHandlers can connect to instance that object of SomeClass ?
Second, I understanding someFunctionWithEscapingClosure like this.
To store local variable closure completionHandler to global variable 'completionHandlerswe using#escaping` keyword!
without #escaping keyword someFunctionWithEscapingClosure returns, local variable completionHandler will remove from memory
#escaping is keep that closure in the memory
Is this right?
Lastly, I just wonder about the existence of this grammar.
Maybe this is a very rudimentary question.
If we want some function to execute after some specific function. Why don't we just call some function after a specific function call?
What are the differences between using the above pattern and using an escaping callback function?
Swift Completion Handler Escaping & Non-Escaping:
Assume the user is updating an app while using it. You definitely want
to notify the user when it is done. You possibly want to pop up a box
that says, “Congratulations, now, you may fully enjoy!”
So, how do you run a block of code only after the download has been
completed? Further, how do you animate certain objects only after a
view controller has been moved to the next? Well, we are going to find
out how to design one like a boss.
Based on my expansive vocabulary list, completion handlers stand for
Do stuff when things have been done
Bob’s post provides clarity about completion handlers (from a developer point of view it exactly defines what we need to understand).
#escaping closures:
When one passes a closure in function arguments, using it after the function’s body gets executed and returns the compiler back. When the function ends, the scope of the passed closure exist and have existence in memory, till the closure gets executed.
There are several ways to escaping the closure in containing function:
Storage: When you need to store the closure in the global variable, property or any other storage that exist in the memory past of the calling function get executed and return the compiler back.
Asynchronous execution: When you are executing the closure asynchronously on despatch queue, the queue will hold the closure in memory for you, can be used in future. In this case you have no idea when the closure will get executed.
When you try to use the closure in these scenarios the Swift compiler will show the error:
For more clarity about this topic you can check out this post on Medium.
Adding one more points , which every ios developer needs to understand :
Escaping Closure : An escaping closure is a closure that’s called after the function it was passed to returns. In other words,
it outlives the function it was passed to.
Non-escaping closure : A closure that’s called within the function it was passed into, i.e. before it returns.
Here's a small class of examples I use to remind myself how #escaping works.
class EscapingExamples: NSObject {
var closure: (() -> Void)?
func storageExample(with completion: (() -> Void)) {
//This will produce a compile-time error because `closure` is outside the scope of this
//function - it's a class-instance level variable - and so it could be called by any other method at
//any time, even after this function has completed. We need to tell `completion` that it may remain in memory, i.e. `escape` the scope of this
//function.
closure = completion
//Run some function that may call `closure` at some point, but not necessary for the error to show up.
//runOperation()
}
func asyncExample(with completion: (() -> Void)) {
//This will produce a compile-time error because the completion closure may be called at any time
//due to the async nature of the call which precedes/encloses it. We need to tell `completion` that it should
//stay in memory, i.e.`escape` the scope of this function.
DispatchQueue.global().async {
completion()
}
}
func asyncExample2(with completion: (() -> Void)) {
//The same as the above method - the compiler sees the `#escaping` nature of the
//closure required by `runAsyncTask()` and tells us we need to allow our own completion
//closure to be #escaping too. `runAsyncTask`'s completion block will be retained in memory until
//it is executed, so our completion closure must explicitly do the same.
runAsyncTask {
completion()
}
}
func runAsyncTask(completion: #escaping (() -> Void)) {
DispatchQueue.global().async {
completion()
}
}
}
/*the long story short is that #escaping means that don't terminate the function life time until the #escaping closure has finished execution in the opposite of nonEscaping closure the function can be terminated before the closure finishes execution Ex:
*/
func fillData(completion: #escaping: () -> Void){
/// toDo
completion()
}
//___________________________
//The call for this function can be in either way's #escaping or nonEscaping :
fillData{
/// toDo
}
/* again the deference between the two is that the function can be terminated before finish of execution nonEscaping closure in the other hand the #escaping closure guarantees that the function execution will not be terminated before the end of #escaping closure execution. Hope that helps ***#(NOTE THAT THE CLOSURE CAN BE OF ANY SWIFT DATA TYPE EVEN IT CAN BE TYPEALIAS)*/

Is there a way to throw errors from asynchronous closures in Swift 3?

I’m executing some functions in a test asynchronously using a DispatchQueue like this:
let queue: DispatchQueue = DispatchQueue.global(qos: DispatchQoS.QoSClass.userInitiated)
let group: DispatchGroup = DispatchGroup()
func execute(argument: someArg) throws {
group.enter()
queue.async {
do {
// Do stuff here
group.leave()
} catch {
Log.info(“Something went wrong")
}
}
group.wait()
}
Sometimes the code inside the do block can throw errors, that I have to catch later on. Since I’m developing a test, I want it to fail, if the code inside the do block throws an error.
Is there a way to throw an error, without catching it inside the queue.async call?
You cannot throw an error, but you can return an error:
First, you need to make your calling function asynchronous as well:
func execute(argument: someArg, completion: #escaping (Value?, Error?)->()) {
queue.async {
do {
// compute value here:
...
completion(value, nil)
} catch {
completion(nil, error)
}
}
}
The completion handler takes a parameter which we could say is a "Result" containing either the value or an error. Here, what we have is a tuple (Value?, Error?), where Value is the type which is calculated by the task. But instead, you could leverage a more handy Swift Enum for this, e.g. Result<T> or Try<T> (you might want to the search the web).
Then, you use it as follows:
execute(argument: "Some string") { value, error in
guard error == nil else {
// handle error case
}
guard let value = value else {
fatalError("value is nil") // should never happen!
}
// do something with the value
...
}
Some rules that might help:
If a function calls an asynchronous function internally, it inevitable becomes an asynchronous function as well. *)
An asynchronous function should have a completion handler (otherwise, it's some sort of "fire and forget").
The completion handler must be called, no matter what.
The completion handler must be called asynchronously (with respect the the caller)
The completion handler should be called on a private execution context (aka dispatch queue) unless the function has a parameter specifying where to execute the completion handler. Never use the main thread or main dispatch queue - unless you explicitly state that fact in the docs or you intentionally want to risk dead-locks.
*) You can force it to make it synchronous using semaphores which block the calling thread. But this is inefficient and really rarely needed.
Well, you might conclude, that this looks somewhat cumbersome. Fortunately, there's help - you might look for Future or Promise which can nicely wrap this and make the code more concise and more comprehensible.
Note: In Unit Test, you would use expectations to handle asynchronous calls (see XCTest framework).
Refactor your code to use queue.sync and then throw your error from there. (Since your execute function is actually synchronous, given the group.wait() call at the last line, it shouldn't really matter.)
For instance, use this method from DispatchQueue:
func sync<T>(execute work: () throws -> T) rethrows -> T
By the way, a good idiom for leaving a DispatchGroup is:
defer { group.leave() }
as the first line of your sync/async block, which guarantees you won't accidentally deadlock when an error happens.