Let's see the Apple forums description of defer keyword.
Use defer to write a block of code that is executed after all other code
in the function, just before the function returns. The code is executed
regardless of whether the function throws an error. You can use defer
to write setup and cleanup code next to each other, even though they
need to be executed at different times.
As per above description, defer block of code is very useful incase If our function ends with an exception without returning anything. Let me discuss this with a real time example by taking an use case.
In my application, users can do their transaction from their wallet but only 2 free wallet transactions are available per a day irrespective of transaction is successful or not. So, every time user tries with his/her wallet I need to update my counter.
var availableMoneyInUserWallet = 200
var transactionTriedWithWallet = 0;
func payFromWallet(amount: Int) throws -> Bool {
transactionTriedWithWallet += 1
return true
}
let response = try payFromWallet(100) //true
transactionTriedWithWallet // 1
I have a payFromWallet function which will get called and increments the wallet counter. But, we need to check the available funds in user wallet also before proceeding for payment. If user is having 0 balance or insufficient funds, We do need to throw an error.
enum WalletError: ErrorType {
case NO_FUNDS
case INSUFFICIENT_FUNDS
}
Above are error types that may happen when accessing user's wallet for payment.
func payFromWallet(amount: Int) throws -> Bool {
if availableMoneyInUserWallet == 0 {
throw WalletError.NO_FUNDS
}
if availableMoneyInUserWallet < amount {
throw WalletError.INSUFFICIENT_FUNDS
}
transactionTriedWithWallet += 1
return true
}
I have added piece of code for balance checking and throwing error.
Let's see what happens If I call like below.
Let's see what happens If I call like below.
do {
let response = try payFromWallet(300)
transactionTriedWithWallet
}
catch WalletError.NO_FUNDS {
print("0 Balance!!!")
transactionTriedWithWallet
}
catch WalletError.INSUFFICIENT_FUNDS {
print("Insufficient balance for transaction!!!")
transactionTriedWithWallet //0
}
It goes to WalletError.INSUFFICIENT_FUNDS catch block and giving transactionTriedWithWallet values as 0 though we are incrementing the counter. This is because the function has thrown an error and increment logic didn't get executed.
Technically, We need that counter to be incremented even there is an error thrown cause we are not worried about transaction successful or not. Here comes the defer block which will get executed just before the function return and the beauty of is, It will get executed even If a function throws an error.
func payFromWallet(amount: Int) throws -> Bool {
defer {
transactionTriedWithWallet += 1
}
if availableMoneyInUserWallet == 0 {
throw WalletError.NO_FUNDS
}
if availableMoneyInUserWallet < amount {
throw WalletError.INSUFFICIENT_FUNDS
}
return true
}
Now call the same function again and see the results.
do {
let response = try payFromWallet(300)
transactionTriedWithWallet
}
catch WalletError.NO_FUNDS {
print("0 Balance!!!")
transactionTriedWithWallet
}
catch WalletError.INSUFFICIENT_FUNDS {
print("Insufficient balance for transaction!!!")
transactionTriedWithWallet //1
}
The below code makes more sense and best use case for defer. Here we increment the counter only if transaction is successful.
func payFromWallet(amount: Int) throws -> Bool {
var isTrasactionSuccessful:Bool = true
defer {
if isTrasactionSuccessful {
transactionTriedWithWallet += 1
}
else {
transactionTriedWithWallet -= 1
}
}
if availableMoneyInUserWallet == 0 {
isTrasactionSuccessful = false
throw WalletError.NO_FUNDS
}
if availableMoneyInUserWallet < amount {
isTrasactionSuccessful = false
throw WalletError.INSUFFICIENT_FUNDS
}
return isTrasactionSuccessful
}
Hope this post is useful. Feel free to comment in case of any queries.
No comments:
Post a Comment