How do we wait for an async/await function and then run the following line of code in Swift?
await res.checkExistingUser(email: loginForm.email)
print("after that") // it doesn't wait for the above function to run completely
in the res.checkExistingUser() is used for calling API and mapping the model.
@MainActor
final class LoginViewModelImpl: LoginViewModel {
@Published var hasError = false
@Published private(set) var errorMesg = ""
@Published private(set) var isFetching = false
@Published private(set) var isLoggedIn = false
@Published private(set) var isVerify = false
func checkExistingUser(email: String) async {
self.isFetching = true
let res = UserServiceImpl()
// Call API
res.checkExistingUser(email: email).responseDecodable(of: BasedResponse<UserExistModel>.self) { response in
switch response.result {
case .success(let apiListingResponse):
print(apiListingResponse)
if !apiListingResponse.success {
self.hasError = true
self.errorMesg = String(localized: String.LocalizationValue((apiListingResponse.error?.message)!) )
self.isFetching = false
break
}
if (apiListingResponse.payload?.message) != nil {
self.isVerify = false
self.hasError = true
self.errorMesg = String(localized: String.LocalizationValue("verify_email"))
self.isFetching = false
return
}
...
}
}
I would suggest watching WWDC 2021 video Swift concurrency: Update a sample app which is a nice, practical demonstration of refactoring old completion-handler-based code to async
-await
.
For example, as discussed in that video, you can have the compiler create an async
“wrapper” implementation of responseDecodable
. E.g., control-click (or right-click) on the responseDecodable
implementation and choose “Refactor” » “Add Async Wrapper”, and it will produce an async
rendition that calls your completion handler rendition.
Now, you didn't share the function signature of responseDecodable
, but for illustrative purposes, let us assume it was the following:
func responseDecodable<T: Decodable>(of dataType: T.Type = T.self, completion: @escaping (BasedResponse<T>) -> Void) {
…
}
Then you can add a wrapper implementation:
func responseDecodable<T: Decodable>(of dataType: T.Type = T.self) async -> BasedResponse<T> {
await withCheckedContinuation { continuation in
responseDecodable(of: dataType) { result in
continuation.resume(returning: result)
}
}
}
Clearly, if your responseDecodable
was different, just modify that wrapper implementation accordingly. (Or let “Refactor” » “Add Async Wrapper” feature do this for you.) And if responseDecodable
is from some library you cannot edit, do not worry: Just put your async
rendition in an extension
of its original type in your own codebase.
Anyway, you can now replace the incorrect checkExistingUser
:
func checkExistingUser(email: String) async {
self.isFetching = true
let res = UserServiceImpl()
res.checkExistingUser(email: email).responseDecodable(of: BasedResponse<UserExistModel>.self) { response in
switch response.result { … }
}
}
With a rendition that will await
your new async
implementation of responseDecodable
:
func checkExistingUser(email: String) async {
self.isFetching = true
let res = UserServiceImpl()
let response = await res.checkExistingUser(email: email).responseDecodable(of: BasedResponse<UserExistModel>.self)
switch response.result { … }
}
You now have an implementation of checkExistingUser
which will properly await
the result.
Frankly, this process of wrapping a completion handler rendition with an async
rendition is merely an interim solution. You really want to eventually retire all of your completion handler implementations, altogether. Personally, as advised in that video, the pattern I've used in my projects is as follows:
async
wrapper rendition;async
-await
patterns; and, when all done with thatasync
-await
, itselfNote, I did not have a reproducible example of your issue, so I made some assumptions in the above. So, do not get bogged down in the details. The idea is to wrap/refactor your completion handler method with an async
one that uses withCheckedContinuation
(or withThrowingCheckedContinuation
), and now you can await
a function that formerly had a completion handler.