@escaping in swift

Introduction:

Hello readers,

With the launch of programming language Swift, we as a developers, are provided by some new features and such as some changes in closures.

Today I am here to discuss @escaping and @noescape attributes of a closure.

NOTE:

@noescape is the default is Swift 3.0, every closure is said to be @noescape bydefault

If we use @noescape, compiler generates this warning:

screen-shot-2016-11-06-at-2-39-09-am

An Example:

In this post I would explain the functionality for the use of keyword @escaping.

A part from apple doc for escaping closures:

Escaping-closure.png

I picked up a scenario where a payment id being made at the grocery store that only accept cash only payments (i.e., No payment method from a 3rd party source like Credit cards etc that requires a mechanism of connecting from their respective server for authenticity of the payment).

Have a look at this source example:

no-escaping-closure

Here in the “viewDidLoad” we have initiated a grocery collection procedure that allows cash only pay. In “buyGroceryFromCashOnlyPayStore” we have first collected grocery then payed bill via cash, which is a valid case. For an opposite case we can think of a case that, in the same function we have first collected grocery and later tried to pay via credit card that voilate the rule, as the store don’t allow to pay via 3rd party medium.

NOTE:

While working with swift 3 closures are not allowed to escape the function as a default practice. So completion via credit card medium is not possible due to async process (which leaves the current context) so the error comes out.

Fix:

Prefix the block with @escaping keyword

Once the store started supporting credit card payment we can prefix this code with @escaping attribute. Have a look at this implementation and notice the presence of @escaping keyword:

Error gone.png

NOTE:

As we have marked the credit card completion to be of @escaping, which means in swift 3 this completion block is allowed to escape the method function body so payment can be made through their APIs using async process.

Where to go from here:

Try it out yourself:

override func viewDidLoad() {
        super.viewDidLoad()
        // Do any additional setup after loading the view, typically from a nib.
        
        self.buyGroceryFromCashOnlyPayStore(collectionCompletionBlock: { () -> Void in
            print("Grocery collected")
        }, cashOnlyCheckOutCompletionBlock: {
            print("Checked out")
        })
    }

    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
        // Dispose of any resources that can be recreated.
    }

    func buyGroceryFromCashOnlyPayStore(collectionCompletionBlock: () -> Void, cashOnlyCheckOutCompletionBlock: @escaping () -> Void)
    {
        print("buyGrocery")
        
        collectGrocery(collectionCompletionBlock: collectionCompletionBlock)
        
        // Allowed
        billPaymentUsingCash(cashOnlyCheckOutCompletionBlock: cashOnlyCheckOutCompletionBlock)

        // -------
        // OR
        // -------
        
        // Not allowed, 
        // Conceptually wrong too, creditable check out is NOT equal to cash only check out i.e., (creditableCheckOut != cashOnlyCheckOut)
        billPaymentUsingCreditCard(creditableCheckOutCompletionBlock: cashOnlyCheckOutCompletionBlock)
    }
    
    func collectGrocery(collectionCompletionBlock: () -> Void)
    {
        print("collectGrocery")
        
        collectionCompletionBlock()
    }
    
    func billPaymentUsingCash(cashOnlyCheckOutCompletionBlock: () -> Void)
    {
        print("billPaymentUsingCash")
        
        payBillViaCash(cashOnlyCheckOutCompletionBlock: cashOnlyCheckOutCompletionBlock)
    }
    
    func billPaymentUsingCreditCard(creditableCheckOutCompletionBlock: @escaping () -> Void)
    {
        print("billPaymentUsingOtherMedium")

        payBillViaCard(creditableCheckOutCompletionBlock: creditableCheckOutCompletionBlock)
    }
    
    func payBillViaCash(cashOnlyCheckOutCompletionBlock: () -> Void)
    {
        // Pay bill via cash
        cashOnlyCheckOutCompletionBlock()
    }
    
    func payBillViaCard(creditableCheckOutCompletionBlock: @escaping () -> Void)
    {
        // Pay bill via credit card
        DispatchQueue.main.async{ () -> Void in
            
            // TODO: Hit API asynchronously and pay bill via card
            print("payBillViaCard inside async process")
            
            // ERROR: We are not allow to -- ESCAPE -- (pay bill other than cash)
            creditableCheckOutCompletionBlock()
            
        }
    }

Conclusion:

With this tutorial we have learned the default no escaping behaviour of closure and learned how to mold it to be used for escaping behaviour.

Feedbacks are the way to strengthen the post, which i would welcome. Please share you thoughts or submit a topic request for research.

Regards,
Shahan

Advertisements