I'm using Microsoft's msal library to have users sign in to their account. I'm using MVVM so I need to the function to return a Resource<String> but the function is returning a value before the msal callbacks are being invoked and updating the value I need to return. I've tried:
- Using
CoroutineScope(IO).launch {}and running the sign-in function in there but that does not return asynchronously. - Using
CoroutineScope(IO).async {}and then return@async Resource inside the callback. I'm compiler complains since the callback is void type. - Using Kotlin FLow<Resource> and emit value from inside the callback.
Can anyone help me out with any ideas of how to get this to run asynchronously or return a value from within the callbacks? Thanks!
suspend fun microsoftSignIn(activity: Activity): Resource<String> {
var resource: Resource<String> = Resource.Error(Throwable(""))
CoroutineScope(Dispatchers.IO).async {
PublicClientApplication.createSingleAccountPublicClientApplication(context, R.raw.auth_config_single_account, object : IPublicClientApplication.ISingleAccountApplicationCreatedListener {
override fun onCreated(application: ISingleAccountPublicClientApplication?) {
application?.signIn((activity as AllActivityBase), null, Array(1) {"api://ScopeBlahBlah"}, object : AuthenticationCallback {
override fun onSuccess(authenticationResult: IAuthenticationResult?) {
Log.i("TOKEN_MSAL ${authenticationResult?.accessToken}" )
authenticationResult?.accessToken?.let { storeToken(it) }
///// I need this to run first before "return resource" runs!!! /////
resource = Resource.Success("Success")
}
override fun onCancel() {
resource = Resource.Error(Throwable(""))
}
override fun onError(exception: MsalException?) {=
resource = Resource.Error(Throwable(""))
}
})
}
override fun onError(exception: MsalException?) {
Log.i("TOKEN_MSAL_A EX ${exception?.message}")
resource = Resource.Error(Throwable(""))
}
})
}
return resource
}
If you need to consume a traditional asynchronous API and make it suspendable, the easiest is to use either suspendCoroutine() or CompletableDeferred. I can't easily reuse your code, so I will provide you just a generic solution, but it should be easy for you to adjust it to your needs.
suspendCoroutine():CompletableDeferred:As you can see, both solutions are pretty similar. I'm not sure if there are any big differences between them. I guess not, so use whichever you like more.
Also, in both cases you can throw an exception instead of returning an error. You can use
resumeWithException()for the first example andcompleteExceptionally()for the second.One final note: you used
Dispatchers.IOin your example, so I did the same, but I doubt it is needed here. Asynchronous operations don't block the thread by definition, so it should be ok to run them from any dispatcher/thread.