Several asynchronous processes invoked as a single function - ios

I need to write a function in swift with a workflow process like this:
Call a ViewController to get data
Call a web service to perform a calculation
Call another web service to persist data
The three processes are asynchronous.
The user calls this process, with code like this:
Get_Calc_And_Save_Data( self, closure: {(error:NSError?) -> Void in
if error==nil {
print("OPERATION OK")
} else {
print("OPERATION ERROR: \(error)")
}
})
How should I write the processes?
Should I use GCD?

You can use several completion handlers along with Booleans to check whether each of the functions have finished like so:
func singleMethodToCall(completion: () -> ()) {
var methodOneFinished = false
var methodTwoFinished = false
var methodThreeFinished = false
asyncMethodOne { () -> () in
methodOneFinished = true
if methodOneFinished && methodTwoFinished && methodThreeFinished {
completion()
}
}
asyncMethodTwo { () -> () in
methodTwoFinished = true
if methodOneFinished && methodTwoFinished && methodThreeFinished {
completion()
}
}
asyncMethodThree { () -> () in
methodThreeFinished = true
if methodOneFinished && methodTwoFinished && methodThreeFinished {
completion()
}
}
}
func asyncMethodOne(completion: () -> ()) {
//Do Stuff
completion()
}
func asyncMethodTwo(completion: () -> ()) {
//Do Stuff
completion()
}
func asyncMethodThree(completion: () -> ()) {
//Do Stuff
completion()
}
singleMethodToCall { () -> () in
print("All three methods have finishsed")
}

Related

getAllVoiceShortcuts function returns twice

I'm fetching voiceShortcurts related with my app. Below function goes into completion block twice. It returns true at first, then false which true is the right one. Why It goes into completion block twice?
public static func updateVoiceShortcuts(completion: #escaping ((_ haveShortcutAlready: Bool) -> Void)) {
INVoiceShortcutCenter.shared.getAllVoiceShortcuts { (voiceShortcutsFromCenter, error) in
if let voiceShortcutsFromCenter = voiceShortcutsFromCenter {
self.voiceShortcuts = voiceShortcutsFromCenter
completion(true)
} else {
if let error = error as NSError? {
print(error)
}
completion(false)
}
}
}

How to change protocol to typealias or closure

In my code I do not want to use protocol I want use closures but I couldn't get it done because I am new on Swift.
Here is the example of class
class SplashPresenterImp: SplashPresenter, OnFinishedListener {
private var interactor: SplashInteractor
private var splashNetworkProtocol: SplashNetworkProtocol
init() {
interactor = SplashNetworking()
}
func startDownloadConfigs(splashNetworkProtocol: SplashNetworkProtocol){
if interactor != nil {
interactor.loadConfigs(listener: self)
self.splashNetworkProtocol = splashNetworkProtocol
}
}
func startDownloadDictionary(splashNetworkProtocol: SplashNetworkProtocol) {
if interactor != nil {
interactor.loadDictionary(listener: self)
self.splashNetworkProtocol = splashNetworkProtocol
}
}
func onFinishedGetDictionary(dictionary: Dictionary) {
//save dictionary
if splashNetworkProtocol != nil {
splashNetworkProtocol.onSuccess()
}
}
func onFinishedGetConfigs(config: Config) {
//save configs
if splashNetworkProtocol != nil {
splashNetworkProtocol.onSuccess()
}
}
func onFinishedWithError(error: AMError) {
if splashNetworkProtocol != nil {
splashNetworkProtocol.onError(error: error)()
}
}
}
Here is the protocol
protocol SplashNetworkProtocol: class {
func onSuccess()
func onError(error: AMError)
}
What I want to have on my viewcontroller to have closure when downloadConfig is complete to start downloadDictionary.
I know how it handle on Java here is the code
mPresenter.startDownloadConfigs(new SplashNetworkProtocol() {
#Override
public void onSuccess() {
downloadDictionary();
}
#Override
public void onError(final AMError error) {
}
});
I want to have same result in swift. Is anyone can give me advice how to do this?
More clearly I want get rid of SplashNetworkProtocol and use only closure.
swift result should be this
mPresenter.startDownloadConfigs(onSuccess: {} onError{}
Should be as simple as:
func startDownloadDictionary(onSuccess: () -> Void, onError: () -> Void)
But even better to use a single closure that handles both success and error. For instance, with an error as an optional parameter:
func startDownloadDictionary(onCompletion: (Error?) -> Void)
A full example:
func someOtherFunc() {
startDownloadDictionary(onCompletion: {(error) -> Void in
if let error = error {
print(error.localizedDescription)
}
//no error
})
}
func startDownloadDictionary(onCompletion: (Error?) -> Void)
{
//dostuff
var error: Error?
// if error happens, create it
onCompletion(error)
}
If you need help with Swift closure syntax, this is a good resource:
http://fuckingswiftblocksyntax.com/

Execute completion handler when condition is met

I have a function which relies on the completionHandler of another function. This completionHandler should be called when the delegate method completedLogin is called. Below is a snippet of my code:
class loginClass : LoginScreenDelegate {
var loginCompleted : Bool = false
func performLogin(completionHandler: (() -> Void)) {
...
let qualityOfServiceClass = QOS_CLASS_BACKGROUND
let backgroundQueue = dispatch_get_global_queue(qualityOfServiceClass, 0)
dispatch_async(backgroundQueue, {
while self.loginCompleted != true {
// Do nothing
}
completionHandler()
})
}
func didLogin(sender: LogInScreen, success: Bool) {
// Do nothing
}
func completedLogin(sender: LogInScreen) {
self.loginCompleted = true
}
}
However, using a while loop inside a background thread seems like a very resource intensive way. I have tried using NSTimer() but the problem is is that it executes another function so i cannot use my callback function anymore. Is there a better / resource friendly way to keep checking this?
You have to add a completion handler to the function which needs to be completed before the other like this:
func completedLogin(sender: LogInScreen, completionHandler :(evaluatedSuccessfully: Bool) -> ()){
self.loginCompleted = true
completionHandler(evaluatedSuccessfully: true)
}
And then you can just call this function in any other function like this:
completedLogin(sender: <your sender>){ success in
If success{
//do something
}
else{
//report an error
}
}
class loginClass : LoginScreenDelegate {
var loginCompleted : Bool = false
var completionHandler: (() -> Void)!
func performLogin(completionHandler: (() -> Void)) {
...
self.completionHandler = completionHandler
}
func didLogin(sender: LogInScreen, success: Bool) {
// Do nothing
}
func completedLogin(sender: LogInScreen) {
self.loginCompleted = true
self.completionHandler()
}
}

Write UIView animation function that animates a view specific times

How to refactore code to be more compact by sending the function the view , number of animated times , applied action function and success funtion
If functional programming solution can be proposed it will be helpful
typealias AnimateAction = UIView -> Void
typealias AnimateSuccess = Bool -> Void
func animateThreetimes(animatedView:UIView,animateAction:AnimateAction,animateSuccess:AnimateSuccess)
{
UIView.animateWithDuration(0.5, animations: { () -> Void in
animateAction(animatedView)
}) { (success) -> Void in
UIView.animateWithDuration(1, animations: { () -> Void in
animateAction(animatedView)
}, completion: { (success) -> Void in
UIView.animateWithDuration(0.5, animations: { () -> Void in
animateAction(animatedView)
}, completion: { (success) -> Void in
animateSuccess(success)
})
})
}
}
Recursion would do quite nicely:
func animate(count: Int) {
if count > 0 {
UIView.animateWithDuration(0.2, animations: { () -> Void in
// do the animations
}, completion: { (_) -> Void in
self.animate(count-1);
})
}
}
You could also do something like this to have a bit more control of the variables you are passing in and how you want to handle the completion. Furthermore this answers your request for a functional solution to the problem.
typealias AnimationAction = UIView -> Void
typealias AnimationSuccess = Bool -> Void
func animateView(view: UIView, animationAction: AnimationAction) -> (duration: NSTimeInterval, completionHandler: AnimationSuccess?) -> Void {
// Return a function that takes a duration and a maybe completion handler
return { duration, completionHandler in
return UIView.animateWithDuration(duration, animations: {
animationAction(view)
}, completion: { finished in
completionHandler?(finished) // Optional function only called if exists
})
}
}
// Just showing the mechanism
let durations = [0.5, 1, 0.5]
for index in 0..<durations.count {
let view = UIView(frame: CGRectZero)
let animationAction: AnimationAction = { view in
print("View: \(view)")
}
let completionHandler: AnimationSuccess? = {
if durations[index] == durations.last {
return { finished in
print(finished)
}
} else {
return nil
}
}()
animateView(view, animationAction: animationAction)(duration: durations[index], completionHandler: completionHandler)
}
Assuming that you want a general approach that could be used for an arbitrary number of animations ...
You could include a counter variable in the function call, decrement it when you have animated, and while it is non-zero, call the animate function again

Swift: Return boolean in GCD Completion Block

I have a function written in Swift. I want the completion block to return a boolean. How can I go about doing this? I am using Grand Central Dispatch.
func myFunc() -> Bool
{
var success:Bool = false
// code here
dispatch_async(dispatch_get_main_queue(), {
return success
)}
)}
}
thanks!
Standard why of dealing with this async nature is not to return value, but pass in completion handler:
func myFunc(completion:(success: Bool) -> ()) {
var success:Bool = false
// code here
dispatch_async(dispatch_get_main_queue()) {
completion(success: success)
}
}
Then work with it:
myFunc({ (success) in
// ...
})
More swifty way (Swift 5):
func myFunc(completion: #escaping (Bool) -> Void) {
var success = false
// code...
completion(success)
}
}
Usage:
myFunc { success in
if success {
// code ...
}
}

Resources