A newer version of this documentation is available.

View Latest

Migration guide

The Android SDK has undergone a major revamp; here are some tips on how to successfully migrate your code.

Major changes

We have tried our best to include as many of the necessary breaking changes in this release as possible. You can expect this version’s API to remain stable in the future. We apologize for any inconvenience this may cause during your upgrade. As always, your continued feedback is greatly appreciated in helping us improve the SDK.

SDK Initialization

Activity argument

The Activity argument in the ReachFive constructor is no longer required:

  • Before

  • Now

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; a Context is required instead of Activity.

  • Before

  • Now

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.

  • Before

  • Now

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 reachfive://${clientId}/callback and whitelist it in your client configuration callback URLs.

Example settings.xml:

<?xml version="1.0" encoding="utf-8"?>
<resources>
  <string name="reachfive_scheme">reachfive</string>
  <string name="reachfive_client_id”>{YOUR_CLIENT_ID}</string>
  <string name="reachfive_path">/callback</string>
</resources>
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:

  • Before

  • Now

  • Alternative

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 */ }
    }
}
What did we just cover?

To clarify this change, the following methods have been removed from the SDK’s API:

  • onLoginWithWebAuthnResult

  • onSignupWithWebAuthnResult

  • onActivityResult.

For convenience purposes, the following methods will help you indicate whether the request code belongs to ReachFive or not:

  • isReachFiveLoginRequestCode: returns true if the request code matches any ReachFive login flow.

  • isReachFiveActionRequestCode: returns true if the request codes matches any ReachFive action flow, such as adding a new WebAuthn device.

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 to loginWithProvider.

  • When no scope is passed to loginWithProvider (prev. loginWithNativeProvider), then the SDK’s default scope is used instead (ReachFive.defaultScope).