Completion Handlers in Swift

Completion Handlers is not an easy concept if you are not good with closures. You can read the official docs here.

Why We Need Completion handler :

  • You’re executing a lengthy task in your code, like downloading a file, making a calculation, or waiting for a web service request.
  • You want to execute some code when the lengthy task is completed, but you don’t want to “poll” the task continuously to check if it’s finished.
  • Instead, you provide closure to the lengthy task, which it will call when the task is completed.

Make a Completion handler :

The completion parameter ask you to enter a closure block whose type is () -> () or () -> Void. So, let’s enter a closure block.

When working with completions, after your function is created, simply define the function and then when calling it and it autocompletes, hit enter. See an example here.

Example 1 | Returning Bool

Let's make a function which downloads data from server and after it completes its download it gives a notification to the user .

func downlaod(sucess:Bool,compleationHandler:(Bool)->Void) {
   for _ in 0...100 {
   		print("downloading data from sarver....")
   }
    
    compleationHandler(sucess)
}

in the above example that function will take 1 parameter which is of type Bool. Here's how to call it in another function or view:

let compleationhandler:(Bool)->Void = { (sucess) in
    if sucess {
    	print("Complete download data ")
    } else {
        print("Error")
    }
    
}

to implement the function you could do something like:

downlaod(sucess: true, compleationHandler: compleationhandler)

Example 2 | Void function

Let's say that you wanted to refresh some data and then perform some action. You can first initialize your function like so:

func fetchUserId(completion: @escaping () -> Void) {
        Amplify.Auth.fetchAuthSession { result in
            do {
                let session = try result.get()
                // Get user sub id
                if let identityProvider = session as? AuthCognitoIdentityProvider {
                    let usersub = try identityProvider.getUserSub().get()
                    DispatchQueue.main.async {
                        completion()
                    }
                }
            } catch {
                print("Fetch auth session failed with error - \(error)")
            }
        }
    }

and then you could use the newly refreshed data:

Button(action: {
    fetchUserId {
        print("Printed after data was reloaded")
    }
}) {
    Text("Tests")
}

Example 3 | Returning String

Using the same concept as above, you could return a string as follow:

func fetchUserId(completion: @escaping (String) -> Void) {
        Amplify.Auth.fetchAuthSession { result in
            do {
                let session = try result.get()
                // Get user sub id
                if let identityProvider = session as? AuthCognitoIdentityProvider {
                    let usersub = try identityProvider.getUserSub().get()
                    DispatchQueue.main.async {
                        completion(usersub)
                    }
                }
            } catch {
                print("Fetch auth session failed with error - \(error)")
            }
        }
    }

then you can use its value like so:

fetchUserId { completion in
    print(completion)
}

Example 4:

Here's another good example:

func loadHealthCareList(completionClosure: (indexes: NSMutableArray)-> ()) {
      //some code here
      completionClosure(indexes: list)
}
healthIndexManager.loadHealthCareList { (indexes) -> () in
            print(indexes)
}