Migration guide
The Android SDK has undergone a major revamp; here are some tips on how to successfully migrate your code.
SDK Initialization
Activity argument
The Activity
argument in the ReachFive
constructor is no longer required:
val reach5 = ReachFive(
sdkConfig = sdkConfig,
providersCreators = listOf(GoogleProvider(), FacebookProvider(), WebViewProvider()),
activity = this,
)
val reach5 = ReachFive(
sdkConfig = sdkConfig,
providersCreators = listOf(GoogleProvider(), FacebookProvider(), WebViewProvider()),
)
As a result of this change, the SDK client can now be used across your application rather than having to be reconstructed in every Activity .
|
Split initialize
and loadSocialProviders
Previously, the initialize
method was used to both fetch the identity client configuration and social provider details. However, this forced a dependency on an Activity
(previously passed in the client’s constructor), effectively making it impossible to initialize the SDK Client once per application context. These parameters and extra network calls were superfluous for SDK users who were not using social providers.
SDK initialization has now been split into two separate methods:
-
initialize
: to load client configuration. -
loadSocialProviders
to load provider configuration; aContext
is required instead ofActivity
.
reach5.initialize({ providers ->
providerAdapter.refresh(providers)
}, {
Log.d(TAG, "ReachFive init ${it.message}")
showToast("ReachFive init ${it.message}")
})
this.reach5 = ReachFive(
sdkConfig = sdkConfig,
providersCreators = providersCreators
)
reach5.initialize({ (1)
// The SDK configuration has successfully been fetched
Log.d(TAG, "ReachFive init success")
}, {
Log.d(TAG, "ReachFive init ${it.message}")
showToast("ReachFive init ${it.message}")
})
reach5.loadSocialProviders( (2)
applicationContext, (3)
success = { providers -> providerAdapter.refresh(providers) },
failure = { Log.d(TAG, "Loading providers failed ${it.message}") }
)
1 | Initializes the client. |
2 | Loads the social providers. |
3 | Takes Context instead of Activity . |
Password Login
The loginWithPassword
method no longer takes a single username
argument, but instead requires separately passing an email
or phoneNumber
value. This is to make the API more consistent with our other SDKs and prepares the ground for custom identifiers.
this.reach5.loginWithPassword(
username = usernameValue(), (1)
password = passwordValue(),
success = { handleLoginSuccess(it) },
failure = { showErrorToast(it) }
)
1 | Replaced by email or phoneNumber . |
this.reach5.loginWithPassword(
email = "jerry@gmail.com", (1)
phoneNumber = "+17074201337", (1)
password = "secret",
success = { handleLoginSuccess(it) },
failure = { showErrorToast(it) }
)
1 | Use email or phoneNumber now instead of username . |
Webview Social Providers
Custom Tabs are the new norm for performing secure authentication flows. Custom tabs are opened with users' own browser, meaning that they have full control over their privacy and security settings.
We’ve replaced our internal custom webview with chrome custom tabs to conduct social login flows.
As a result, ReachFiveLoginActivity
has been removed.
In your AndroidManifest.xml
, remove references to ReachFiveLoginActivity
, and instead add the following:
<activity
android:name="co.reachfive.identity.sdk.core.RedirectionActivity"
android:screenOrientation="portrait"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<data
android:host="@string/reachfive_client_id" (1)
android:pathPrefix="@string/reachfive_path"
android:scheme="@string/reachfive_scheme"
tools:ignore="AppLinkUrlError" />
</intent-filter>
</activity>
1 | reachfive_client_id should be defined in the <resources> section of your settings.xml file. |
Remember to configure your redirection scheme as Example
|
If end-users' Android devices do not support Custom Tabs, then the page will be opened directly in their browser. |
Success/Failure Callbacks
We’ve removed the SuccessWithNoContent<Unit>
type alias, leaving only Success<T>
. When no value is returned following an SDK action, Success<Unit>
will continue to be returned.
Simply replace occurrences of successWithNoContent = with success = to fix compile errors.
|
Login Activity Results
Previously, SDK users had to implement complex plumbing logic inside onActivityResult
override to determine which request codes belonged to ReachFive and which SDK method to call. When request codes did not concern ReachFive, an exception was thrown, further exacerbating integration complexity.
The SDK now handles all of this logic in a single method onLoginActivityResult
. If the request code does not concern ReachFive, then nothing happens (a debug log is emitted for development purposes).
Therefore, instead of having to match against request codes like before, you can now safely and always call onLoginActivityResult
with your prepared login success/failure callbacks:
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
// The code of the authentication redirection
REDIRECTION_REQUEST_CODE -> {
client.onLoginWithWebAuthnResult(
intent = data,
resultCode = resultCode,
success = { authToken -> ... }, // Get the profile's authentication token
failure = { error -> ... } // Handle a ReachFive error
)
}
}
public override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
super.onActivityResult(requestCode, resultCode, data)
Log.d(TAG, "MainActivity.onActivityResult requestCode=$requestCode resultCode=$resultCode")
reach5.onLoginActivityResult(
requestCode = requestCode,
resultCode = resultCode,
intent = data,
success = { handleLoginSuccess(it) },
failure = { reachFiveError ->
Log.e(TAG, "Login error!", reachFiveError)
showToast("Login error occurred :O")
},
activity = this
)
}
Alternatively, you can also choose to use a new helper method (resolveResultHandler
) that applies the appropriate callback method depending on the type of flow you are performing (i.e., login callback VS adding a new Webauthn device):
public override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
super.onActivityResult(requestCode, resultCode, data)
// ActivityResultHandler pre-applies request code, result code & Intent. You only have to pass success/failure callbacks!
val handler: ActivityResultHandler? = reach5.resolveResultHandler(requestCode, resultCode, data)
when (handler) {
is LoginResultHandler -> handler.handle(
success = { handleLoginSuccess(it) },
failure = { reachfiveError ->
Log.e(TAG, "Login error!", reachfiveError)
showToast("Login error occurred :O")
},
activity = this
)
is WebAuthnDeviceAddResult -> handler.handle(
success = { showToast("New WebAuthn device added!") },
failure = { reachfiveError ->
Log.e(TAG, "Failed to add WebAuthn device!", reachfiveError)
showToast("Failed to add device!")
}
)
else -> { /* not ReachFive */ }
}
}
Error Handling
Previously, errors could be communicated either through resultCode
in activity callbacks or through ReachFiveError
. Now, all errors are exclusively communicated through the failure callbacks — you no longer have to match against request/result codes yourself.
ReachFiveError.getErrorCode returns an enumeration class to help you understand whether the error is from the ReachFive API or whether it is an internal SDK error (e.g., end-user closes custom tab during login flow).
|
JavaReachFive
The above changes also apply to JavaReachFive
. However, the following changes or fixes have been applied:
-
loginWithNativeProvider
has been renamed tologinWithProvider
. -
When no scope is passed to
loginWithProvider
(prev.loginWithNativeProvider
), then the SDK’s default scope is used instead (ReachFive.defaultScope
).