Navigation
View as Markdown

Account sharing on iOS and Android

The shift is the same as on web: attach becomes evaluate with the access action. The bigger change on mobile is how challenges are shown and which callbacks exist. Three changes apply on both platforms:

  1. One entry point. Configuring the client, setting the user, and attaching collapse into a single evaluate call that takes user, email, and phone inline.
  2. Challenges render through a container. You add RuptContainerView (iOS) or RuptContainer (Android) to your UI and it observes redirectURL. The SDK no longer pushes its own view controller or activity, so the "should present" hooks are gone.
  3. The callback set is smaller. The device-limit and pre-challenge callbacks are dropped, and two challenge-button callbacks are added.

This is a breaking upgrade. Every app needs code changes, and there is no compatibility shim. The steps below are the full swap.

iOS (3.8.1 to 4.0.0)

Step 1: Replace the framework

Download the new RuptClient.xcframework and replace the old binary in your project.

Step 2: Create the client, then evaluate

// v2
let rupt = Rupt(clientID: "your_client_id", useViewController: true)
rupt.onLogoutCurrentDevice = {
    // Clear your session and sign the user out locally.
}
rupt.setUserID(user.id)
rupt.setEmail(user.email)
rupt.attach()
// v4
let rupt = Rupt(clientID: "your_client_id")
rupt.onLogout = {
    // Fires when this session is logged out remotely. Clear your local session.
}

// Same call site as attach: once per protected screen, for paying users.
try await rupt.evaluate(
    action: "access",
    user: user.id,
    email: user.email,
    phone: user.phone
)

setUserID, setEmail, setPhone, identify, and getHash are gone. Pass the user inline to evaluate instead.

Step 3: Show challenges through the container

Wrap your root view so the challenge can overlay it:

RuptContainerView(rupt: rupt) {
    // your app content
}

UIKit hosts skip the container, observe $redirectURL, and present the challenge web view themselves.

iOS events

v2 (3.8.1)v4 (4.0.0)What changed
onLogoutCurrentDeviceonLogoutRenamed
onChallengeCompletedonCompleteRenamed
onBackButtonPressedonBackButtonPressedUnchanged
onLimitExceededRemovedCap devices with a device_count policy
onChallengeRemovedThe SDK surfaces the challenge via redirectURL
onCreateNewAccountRemovedConversion is handled by the challenge Success URL
shouldPresentChallengeViewControllerRemovedUse RuptContainerView
AddedonPrimaryCtaFires when the challenge primary button is tapped
AddedonSecondaryCtaFires when the challenge secondary button is tapped

Android (2.1.0 to 4.0.0)

Step 1: Swap the dependency

Move from JitPack to Maven Central:

// v2
implementation 'com.github.getrupt:kotlin:2.1.0'

// v4
implementation 'dev.rupt.android:rupt-android:4.0.0'

Make sure mavenCentral() is in your repositories.

Step 2: Create the client, then evaluate

v2 was a configure plus attach on the Rupt singleton. v4 is an instance you create with your client ID.

// v2
Rupt.configure(clientId = "your_client_id", account = user.id)
Rupt.onLogoutCurrentDevice = {
    // Clear your session and sign the user out locally.
}
Rupt.attach(context = this)
// v4
val rupt = Rupt(context = this, clientId = "your_client_id")
rupt.onLogout = {
    // Fires when this session is logged out remotely. Clear your local session.
}

// Same call site as attach: once per protected screen, for paying users.
lifecycleScope.launch {
    rupt.evaluate(
        action = "access",
        user = user.id,
        email = user.email,
        phone = user.phone,
    )
}

detach, identify, and getHash are gone, and there is no longer a separate attach call to repeat on resume.

Step 3: Show challenges through the container

setContent {
    RuptContainer(rupt = rupt) {
        // your app content
    }
}

View-system hosts skip the container, collect rupt.redirectURL in a lifecycle scope, and launch ChallengeActivity with the URL.

Android events

v2 (2.1.0)v4 (4.0.0)What changed
onLogoutCurrentDeviceonLogoutRenamed
onChallengeCompletedonCompleteRenamed
onBackButtonCallbackonBackButtonPressedRenamed, and no longer passed the back state
onLimitExceededRemovedCap devices with a device_count policy
onChallengeRemovedThe SDK surfaces the challenge via redirectURL
onCreateNewAccountRemovedConversion is handled by the challenge Success URL
shouldStartChallengeActivityRemovedUse RuptContainer
AddedonPrimaryCtaFires when the challenge primary button is tapped
AddedonSecondaryCtaFires when the challenge secondary button is tapped

Where the dropped events went

The renamed callbacks are a straight swap. The dropped ones moved out of the client on purpose:

  • Device limits (onLimitExceeded) are now a policy on the access event. Set a device_count cap in the dashboard and the device-limit challenge does the capping. There is no client callback to wire.
  • The pre-challenge hook (onChallenge) is gone because the SDK surfaces the challenge itself through redirectURL. There is nothing to intercept.
  • The new-account hook (onCreateNewAccount, and the new_account web view message) is gone because conversion is handled by the challenge's Success URL. Point it at your signup page.
  • The presentation hooks (shouldPresentChallengeViewController, shouldStartChallengeActivity) are replaced by the container. Adopt RuptContainerView or RuptContainer, or observe redirectURL and present it yourself.

onPrimaryCta and onSecondaryCta are optional. Use them only if you want to react when the user taps a button on the challenge.

Policies and challenge URLs

The client swap above only wires up detection. What actually triggers a challenge is your policies, and those live in the dashboard, the same across every platform. Recreate the three default account-sharing checks (device limits, impossible travel, concurrency) and set the Success and Logout URLs by following Steps 3 and 4 of the web migration. The Logout URL is where a device lands when the user logs it out from the challenge; onLogout is the callback that fires when this session is logged out remotely.