Installation
In Xcode, select your project file, select the main target, then scroll down to the “frameworks” section to add a package dependency to your project. See the official documentation for specific steps. Enter this as the package repository url:Usage
Initializing the Rownd SDK
In yourAppDelegate
file, call the Rownd.configure()
method during application launch:
Rownd.requestSignIn()
at some point, if the user is not already authenticated. This will display the Rownd interface for authenticating the user. Once they complete the sign-in process, an access token and the user’s profile information will be available to your app.
Handling authentication
Rownd leverages an observeable architecture to expose data to your app. This means that as the Rownd state changes, an app can dynamically update without complicated logic. For example, a view can display different information based on the user’s authentication status. Here’s an example SwiftUI view that displays different messages depending on the user’s authenticated status:Example usage outside of SwiftUI
.auth
.user
Getting an access token
Whenever your app needs to make an authenticated request to your backend, you’ll need to get an access token. You can do this by callingawait Rownd.getAccessToken(throwIfMissing: true)
. If the user is not authenticated, this function will throw an AuthenticationError.noAccessTokenPresent
error.
If there is an issue fetching the access token (e.g., during a token refresh), an AuthenticationError.serverError
or AuthenticationError.networkConnectionFailure
error will be thrown. Server and network failures are automatically retried before throwing an error.
If the user is signed in, but the refresh token is expired, invalidated, or has been used previously, the user will be signed out and the function will throw an AuthenticationError.invalidRefreshToken
error.
See the API reference for more information.
Customizing the UI
While most customizations are handled via the Rownd dashboard, there are a few things that have to be customized directly in the SDK. TheRowndCustomizations
class exists to facilitate these customizations. It provides the following properties that may be subclassed or overridden.
-
sheetBackgroundColor: UIColor
(default: light: .white, dark: .systemGray6; requires subclassing) - Allows changing the background color underlaying the bottom sheet that appears when signing in, managing the user account, etc. -
sheetCornerBorderRadius: CGFloat
(default:25.0
) - Modifies the curvature radius of the bottom sheet corners. -
loadingAnimation: Lottie.Animation
(default: nil) - Replace Rownd’s use of the system default loading spinner (i.e.,UIActivityIndicatorView
orProgressView
) with a custom animation. Any animation compatible with Lottie should work, but will be scaled to fit a 1:1 aspect ratio (usually with aCGRect
frame width/height of100
)
RowndCustomizations
class. Here’s an example:
Usage within app extensions
It’s possible to access the Rownd state from within an app extension, like a widget. You’ll need to include the Rownd package in the extension’s dependencies and set up an app group for data sharing between the app and the extension. Without the app group, extensions will not be able to sync with your app’s authentication state. Follow these steps to configure your app and extension to work with Rownd:-
Add an app group entitlement to both your app and any extensions that will use Rownd. This app group must be named like this:
<prefix>.io.rownd.sdk
. For example, if you work at a company with the acme.com domain, your app group might look like this:com.acme.app.io.rownd.sdk
. Rownd will store its data in this app group. Your app should store data in a separate app group to prevent any collisions. -
In your app’s
AppDelegate
file as well as your extension’s entry point, set the app group prefix you defined above viaRownd.config.appGroupPrefix = "<prefix>"
(e.g.,Rownd.config.appGroupPrefix = "com.acme.app"
) -
In your extension, call
Rownd.configure()
prior to accessing authentication state. Here’s an example:
If you’re building widgets that need access to Rownd auth state, you should listen for Rownd auth events and notify
WidgetCenter
that widgets may need updating any time the state changes. That way, they’ll re-render while your app is in the foreground and will show an accurate state. Here’s a simple example:Configuration options
Rownd provides a number of configuration options that can be set before callingRownd.configure()
. These options are available via the Rownd.config
object.
enableSmartLinkPasteBehavior: Bool
(default:true
) - Attempts to access the clipboard for smart link pasting behavior if it contains a URL. This will trigger a system alert asking for permission to access the clipboard. If you don’t want this behavior, set this tofalse
.
Events
The Rownd SDK emits lifecycle events that you can listen to within your app. These events are primarily useful for detecting more granular aspects of a user’s session (e.g., starting to sign in, completing sign-in, updated profile, etc.). To listen to events, first create a class that conforms to theRowndEventHandlerDelegate
protocol. It looks something like this:
RowndEvent
object contains the event type and any associated data. The event types are defined in the RowndEventType
enum.
List of events
Here’s a list of events that the Rownd SDK emits and the corresponding data that should be present in the event data dictionary. Remember to write your code defensively, as the data dictionary may be missing keys in some cases.Event | Type | Payload |
---|---|---|
User started signing in | .signInStarted | |
User signed in successfully | .signInCompleted | |
User sign in failed | .signInFailed |
API reference
In addition to the state observable APIs, Rownd provides imperative APIs that you can call to request sign in, get and retrieve user profile information, retrieve a current access token, or encrypt user data with the user’s local key.Rownd.requestSignIn() -> Void
Opens the Rownd sign-in dialog for authentication.
Rownd.requestSignIn(RowndSignInOptions(postSignInRedirect: "https://my-domain.com")) -> Void
Rownd.requestSignIn(RowndSignInOptions(postSignInRedirect: "https://my-domain.com", intent: .signIn)) -> Void
Opens the Rownd sign-in dialog for authentication, as above. When the user completes the authentication challenge via email or SMS, they’ll be redirected to the URL set for postSignInRedirect
. If this is a Universal Link, it will redirect the user back to your app.
Rownd.requestSignIn(with: RowndSignInHint) -> Void
Rownd.requestSignIn(with: RowndSignInHint, signInOptions: RowndSignInOptions?) -> Void
Requests a sign-in, but with a specific authentication provider (e.g., Sign in with Apple). Rownd treats this information as a hint. If the specified authentication provider is enabled within your Rownd app configuration, it will be honored. If not, Rownd will fall back to the default flow.
Supported values:
.appleId
- Prompt user to sign in with their Apple ID.google
- Prompt user to sign in with their Google account.passkey
- Prompt user to sign in with a passkey if they’ve previously set one up.guest
- Sign in the user anonymously as a guest.
RowndSignInOptions
Some of the requestSignIn()
methods accept an optional RowndSignInOptions
parameter. This class contains the following properties:
-
postSignInRedirect: String?
(not recommended) - When the user completes the authentication challenge via email or SMS, they’ll be redirected to the URL set forpostSignInRedirect
. If this is a Universal Link, it will redirect the user back to your app. If you don’t set this value, the user will be redirected to your app’s subdomain as configured in the Rownd dashboard. -
intent: RowndSignInIntent?
- This option applies only when you have opted to split the sign-up/sign-in flow via the Rownd dashboard. Valid values are.signIn
or.signUp
. If you don’t set this value, the user will be presented with the unified sign-in/sign-up flow. If you don’t set this value, the user will be presented with the unified sign-in/sign-up flow.
Rownd.signOut() -> Void
Clears the user’s access token, removes the user’s profile data, and returns the user to a completely unauthenticated state.
Rownd.signOut(scope: RowndSignoutScope) -> Void
Revokes all tokens for the specified user causing them to be signed out on all devices.
Supported values:
.all
- All devices
Rownd.getAccessToken(throwIfMissing: Bool = false) async throws -> String?
Assuming a user is signed-in, returns a valid access token, refreshing the current one if needed.
By default, this function will return nil
if an access token cannot be returned, either because the user is not signed in or because the refresh token is invalid.
If an access token cannot be returned due to a temporary condition (e.g., inaccessible network), this function will throw an AuthenticationError
indicating the failure reason (e.g., server or network error).
You may also set throwIfMissing
to true
to force an error to be thrown if an access token cannot be returned. This will provide more granular reasons for the failure. The possible error cases for AuthenticationError
are:
.noAccessTokenPresent
- the user is not signed in.invalidRefreshToken(details: String)
- the refresh token was invalid (e.g., the token was expired, revoked, or a previous exchange failed to complete successfully). The user will be signed out..networkConnectionFailure(details: String)
- a network condition prevented the token from being refreshed, even after several retries and should be re-attempted later.serverError(details: String)
- an error occurred on the server and you should try again later
Rownd.getAccessToken(_ token: String) async -> String?
When possible, exchanges a non-Rownd access token for a Rownd access token. This is primarily used in scenarios where an app is migrating from some other authentication mechanism to Rownd. Using Rownd integrations, the system will accept a third-party token. If it successfully validates, Rownd will sign-in the user and return a fresh Rownd access token to the caller. This API returnsnil
if the token could not be validated and exchanged. If that occurs, it’s likely
that the user should sign-in normally via Rownd.requestSignIn()
.
NOTE: This API is typically used once. After a Rownd token is available, other tokens should be discarded. Example:
Rownd.user.get() -> Dictionary<String, AnyCodable>
Returns the entire user profile as a dictionary objectRownd.user.get<T>(field: String) -> T?
Returns the value of a specific field in the user’s data dictionary. You can useuser_id
to obtain the user’s unique ID, which is always a string.
Your application code is responsible for knowing which type the value should cast to. If the cast fails or the entry doesn’t exist, a nil
value will be returned.
Rownd.user.set(data: Dictionary<String, AnyCodable>) -> void
Replaces the user’s data with that contained in the dictionary. This may overwrite existing values, but must match the schema you defined within your Rownd application dashboard. Hint: useAnyCodable.init(value)
to conform your values to the required type.
Rownd.user.set(field: String, value: AnyCodable) -> void
Sets a specific user profile field to the provided value, overwriting if a value already exists. If the field is flagged asencrypted
, it will be encrypted on-device prior to storing in Rownd’s platform.
Hint: use AnyCodable.init(value)
to conform your values to the required type.