Wait for unpredictable data before performing a completion a block inside method - swift

I have a function that does some work and call completion. Something like this
func doStuff(completion: (Bool) -> ()) {
performWork()
completion(true)
}
The problem is that performWork triggers some process that receives result in other method. And depending on this result I need call completion with success or not based on data from previous method.
Is there any possible solution ? Method doStuff can not be modified and I don't have access to performWork() its third party, I can only call it.

You should save completion in a variable in the scope of your class and execute it in the delegate method of your API.
var doStuffCompletion: (Bool) -> ()!
func doStuff(completion: (Bool) -> ()) {
performWork()
doStuffCompletion = completion
}
func apiStuffFinished(success: Bool) {
doStuffCompletion(success)
}

Related

Swift: can I use async/await syntax with a function that already returns synchronously a value?

I know that I can use async/await to replace this :
func test(_ completion: #escaping (Int) -> Void) {
// ...
completion(foundValue)
}
with this:
func test async -> Int {
let result = await calculate()
return result
}
However, can I do something if the initial function is like this?
func test(_ completion: #escaping (Int) -> Void) -> Int {
// ...
}
Thank you for your help
If your function doesn't need to wait for completion handler call then you can create a top level task and call your completion handler from the task after doing asynchronous work:
func test(_ completion: #escaping (Int) -> Void) -> Int {
// synhronous calls
Task {
// async funtion calls
completion(value)
}
return value
}
If you need to wait for all your asynchronous task completion before returning, you can use a semaphore for that:
func test(_ completion: #escaping (Int) -> Void) -> Int {
let semaphore = DispatchSemaphore(value: 0)
// synhronous calls
Task {
// async funtion calls
completion(value)
// signal async task completion
semaphore.signal()
}
// wait for async task completion
semaphore.wait()
return value
}
Or, you can wrap your asynchronous task inside an operation like here. You can signal the operation finish once the underlying async task finishes and wait for operation completion using waitUntilFinished():
let op = TaskOperation {
try await Task.sleep(nanoseconds: 1_000_000_000)
}
op.waitUntilFinished()
Note that using semaphore.wait() or op.waitUntilFinished() blocks the current thread and blocking the thread can cause undefined runtime behaviors in modern concurrency as modern concurrency assumes all threads are always making forward progress. If you are planning to use this method only in contexts where you are not using modern concurrency, with Swift 5.7 you can provide attribute mark method unavailable in asynchronous context:
#available(*, noasync, message: "this method blocks thread use the async version instead")
func test(_ completion: #escaping (Int) -> Void) -> Int {
// do work that can block thread
}
By using this attribute you can only invoke this method from a non-async context. But some caution is needed as you can invoke non-async methods that call this method from an async context if that method doesn't specify noasync availability.

What's the difference between the two closure

I tried comment and uncomment the activity() in the following code. I found when I commented the activity() the result in playground would just show "play tennis" once. However it would show twice if I uncommented activity(). What's the difference between the two statements?
class Baby {
var name = "peter"
var favoriteActivity: (() -> ())!
func outsideActivity(activity: #escaping () -> ()) {
//activity()
favoriteActivity = activity
}
}
var cuteBaby = Baby()
cuteBaby.outsideActivity {
print("play tennis")
}
cuteBaby.favoriteActivity()
This is what’s going on:
Consider this method:
func outsideActivity(activity: #escaping () -> ()) {
//activity()
favoriteActivity = activity
}
All that does is save the closure in the favoriteActivity property
Thus, when you do:
// create `Baby` instance
var cuteBaby = Baby()
// this method saves closure in `favoriteActivity`, but doesn’t call it
cuteBaby.outsideActivity {
print("play tennis")
}
// this now calls the closure
cuteBaby.favoriteActivity()
All the outsideActivity method does is save the closure in a property called favoriteActivity.
Thus you see one print statement.
However, now consider this method:
func outsideActivity(activity: #escaping () -> ()) {
activity()
favoriteActivity = activity
}
This actually calls the closure before saving it in the property.
So, when you do:
// create `Baby` instance
var cuteBaby = Baby()
// this method both calls the closure and then also saves it in `favoriteActivity`
cuteBaby.outsideActivity {
print("play tennis")
}
// this now calls the saved closure a second time
cuteBaby.favoriteActivity()
In this case, you’ll see your print statement being called twice.
That’s why the first rendition calls the closure only once, whereas the second calls the closure twice.
Usually when you pass a closure to a method, you either (a) call the closure from within the method (perhaps in some completion handler or the like); or (b) save the closure in some property so you can call it later.
So, this second example is very unusual, where outsideActivity both calls the closure itself and saves that closure in some property so you can call it again later. You usually do one or the other, but not both.

how to pass a callback function in another function in swift?

so i'm doing my first app,
and i want to do a function that will be a uniform funciton to sevral places in the system, and not belong to only one specific class.
Is there a way for me to pass a callback function as a parameter to another function ?
here is just a demonstration of what i mean.
ClassA {
func classAcallbackFunction (displayString: String) {
print (displayString)
}
ClassB().classBFunction(classAcallbackFunction())
}
ClassB {
func classBfunction (myCallbackfunc: func) {
mycallbackfunc("all is working !!!")
}
}
The parameter you have declared is not correct. Replace it with something like this:
func classBFunction(_ completion: (String) -> Void) {
completion("all working")
}
Like shared by regina_fallangi in the comments, callbacks are usually called completion handlers, which is why I replaced the names accordingly.
Extra Credit:
If you only want to optionally pass a function, you could do this:
func classBFunction(_ completion: ((String) -> Void)? = nil) {
completion?("all working")
}
Now you could also just call classBFunction().

How to cancel a Dispatch Queue

I have a method that has this block inside:
func a(_ foo: () -> ()) { ... }
func b(_ foo: () -> ()) { ... }
func abc() {
a {
// some processing
b {
// some asynchronous work
}
}
}
When a button is tapped:
I call method abc()
It connects to the internet
The point is that it takes time to do so
I am looking for a way to cancel the previous block, and run the current block if tapped twice.
There is no direct option to cancel you're block. And block will be executed when it is called only one way to set condition in the body of the block and do not execute part that is inside.
In you're case try to use Operation and OperationQueue this approach will give you flexible solution to mange operation execution and gives opportunity to cancel operations.
Small example:
let operationQueue = OperationQueue.main
let operation = Operation()
operationQueue.addOperation(operation)
//Cancel operation that is executing
operation.cancel()

Swift Completion Block

I want to achieve the following :
In classB, to reload my database after adding 1 object. reloadDatabase() is called within the completionBlock.
In classB, reloadDatabase() will call getObjects() in classA to get the most updated list of database objects and pass to objectList in classB
Question: How do i ensure that whenever i call getObjectList() in classB, i will always get the most updated list? From my understanding, my objectList might not be update in reloadDatabase() block. I could be calling getObjectList() when reloadDatabase() haven't reach the completion block yet (objectList is still the old objectList).
I am pretty new to closures and blocks. Any guidance is greatly appreciated!
class classA: NSObject {
func addThisObject(object: RLMObject, completionBlock: () -> ())){
...
completionBlock()
}
func getObjects (completionBlock: ((list: [RLMObject]) -> ())){
var recordList = [RLMObject]()
...
completionBlock(list: recordList)
}
}
class classB: NSObject {
var objectList = [RLMObject]()
func addObject (object: RLMObject) {
classA().addThisObject(object, completionBlock: {() -> () in
self.reloadDatabase()
})
}
func reloadDatabase() {
classA().getObjects{(list) -> () in
self.objectList = list
}
}
func getObjectList() -> [RLMObject] {
return objectList
}
}
From your snippets it seems to me there is no asynchronous calls, so you won't be able to call getObjectList() before reloadDatabase() block. Closures are not asynchronous if you don't use them with something that is (e.g. GCD).
If you have asynchronous calls, but they are not in the snippets, then getObjectList() can be called while reloadDatabase() is being executed. Then you have few options:
Remove asynchronous calls
Use serial queue for your methods
Add boolean variable updateInProgress and check it in getObjectList() - how to do it
Ignore the fact that data may be outdated - it is correctness vs speed trade.
Let your database inform its clients that something changed
In your question, you don't say whether you'll be calling any of these functions from different threads. So when you call addObject() in classB, execution wouldn't even continue until the database has been reloaded and objectList was updated.
Using closures and blocks does not automatically imply that code will be executed on a different context.