Configuration and lifecycle
Point the SDK at a custom server, open Octopus screens from your deep links, switch communities at runtime, and control the SDK lifecycle.
Before you begin
- Install and initialize the SDK: see Initialize the SDK.
- If your users sign in with your own account system, read Link your user to the SDK. You reconnect them after a community switch.
How it works
The SDK holds one community at a time, identified by the API key you pass at initialization. Four lifecycle operations change that state:
| Operation | User session | Local SDK data | SDK stays initialized | Community |
|---|---|---|---|---|
| Initialize | Starts empty | Kept | Yes | The one from the API key |
| Switch community | Ended | Cleared | Yes | The new one |
| Reset | Ended | Cleared, on Android only | Yes | Unchanged |
| Stop | Not ended | Kept | No | None until you initialize again |
After a switch or a reset, reconnect your user and rebuild any community screen on display. After a stop, initialize the SDK again before you call anything else.
Point the SDK at a custom server
Android ≥ 1.12.0iOS ≥ 1.12.0Flutter ≥ 1.12.0React Native ≥ 1.13.0Unity ≥ 1.12.2
By default, the SDK talks to the Octopus production endpoint. Pass a server only when Octopus gives you a dedicated host. Traffic always uses TLS.
The host is a DNS name, an IPv4 address or an IPv6 address. It carries no scheme, port, path or whitespace: api.example.com is valid, https://api.example.com is not. The port defaults to 443.
- Android
- iOS
- Flutter
- React Native
- Unity
Parameters of ApiServer:
host(String): required.port(Int): optional, default443.
import com.octopuscommunity.sdk.ApiServer
import com.octopuscommunity.sdk.OctopusSDK
import com.octopuscommunity.sdk.domain.model.ConnectionMode
OctopusSDK.initialize(
context = applicationContext,
apiKey = "YOUR_API_KEY",
connectionMode = ConnectionMode.SSO(),
apiServer = ApiServer(host = "api.example.com"),
)
ApiServer throws ApiServer.ValidationError, an IllegalArgumentException, when the host is empty, contains a scheme, a port, a path or whitespace, or has invalid IPv6 brackets.
Parameters of OctopusSDK.Configuration.ApiServer:
host: String: required.port: Int: optional, default443.
import Octopus
let octopus = try OctopusSDK(
apiKey: "YOUR_API_KEY",
connectionMode: .sso(.init(loginRequired: {
// Start your app's sign-in flow
})),
configuration: .init(apiServer: try .init(host: "api.example.com"))
)
ApiServer.init throws a ValidationError: emptyHost, hostContainsScheme, hostContainsPortOrPath, hostContainsWhitespace or invalidIPv6Bracketing.
Parameters of ApiServer:
host(String): required.port(int): optional, default443.
import 'package:octopus_sdk_flutter/octopus_sdk_flutter.dart';
final octopus = OctopusSDK();
await octopus.initialize(
apiKey: 'YOUR_API_KEY',
apiServer: ApiServer(host: 'api.example.com'),
);
ApiServer throws an ApiServerValidationError, an ArgumentError. Its kind tells you why: emptyHost, hostContainsWhitespace, hostContainsScheme, hostContainsPortOrPath or invalidIPv6Bracketing.
Parameters of the apiServer option:
host(string): required.port(number): optional, default443.
import { initialize } from '@octopus-community/react-native';
await initialize({
apiKey: 'YOUR_API_KEY',
connectionMode: { type: 'sso', appManagedFields: [] },
apiServer: { host: 'api.example.com' },
});
An invalid host makes initialize() reject. The native SDK validates it.
Parameters of Initialize:
apiServerHost(string): optional.nullor empty targets the default endpoint.apiServerPort(int): optional, default443. Ignored when no host is set.
OctopusSDK.Initialize(
"YOUR_API_KEY",
ConnectionMode.SSO(),
apiServerHost: "api.example.com",
apiServerPort: 443);
Open Octopus screens from your deep links
Android availableiOS not availableFlutter not availableReact Native not availableUnity not available
- Android
- iOS
- Flutter
- React Native
- Unity
The SDK can register its screens under your own deep link base paths. A link such as https://www.example.com/community/post then opens the post creation screen inside your navigation graph. In Octopus Auth mode, the magic link email also points to <base path>/magic-link/confirm, so the user comes back to your app.
The SDK appends a / to each base path when it is missing, then adds the sub-path of the screen:
| Sub-path | Screen |
|---|---|
(empty) or home | Community home |
post/create | Post creation |
post/{postId}/comment/{commentId} | A comment in its post |
comment/{commentId}/reply/{replyId} | A reply in its comment thread |
current-profile | The user's own profile |
current-profile/edit | The user's profile editor |
settings | Community settings |
magic-link/confirm | Magic link confirmation (Octopus Auth) |
Pass deepLinksBasePaths to initialize, or to switchCommunity. The deep links are attached to the destinations that octopusComposables registers.
OctopusSDK.initialize(
context = applicationContext,
apiKey = "YOUR_API_KEY",
connectionMode = ConnectionMode.OctopusAuth,
deepLinksBasePaths = listOf("https://www.example.com/community"),
)
Then declare an intent filter for the same base path on the activity that hosts your NavHost:
<activity android:name=".MainActivity" android:exported="true">
<intent-filter android:autoVerify="true">
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<data
android:scheme="https"
android:host="www.example.com"
android:pathPrefix="/community" />
</intent-filter>
</activity>
The SDK does not declare any intent filter. Without yours, Android never routes the link to your app.
Deep link base paths are not yet available on iOS. In Octopus Auth mode, pass the link that re-opens your app after the magic link through ConnectionMode.octopus(deepLink:): see Connect your users.
Deep link base paths are not yet available on Flutter. In Octopus Auth mode, pass the deepLink parameter of initializeOctopusAuth: see Connect your users.
Deep link base paths are not yet available on React Native. In Octopus Auth mode, pass deepLink in connectionMode: { type: 'octopus', deepLink }: see Connect your users.
Deep link base paths are not yet available on Unity.
Switch community
Use switchCommunity when your app serves several communities with different API keys. It flushes pending analytics events, signs out the current user, clears cached user data and files, then initializes the SDK on the new API key.
switchCommunity also works when the SDK is not initialized: it initializes it on the new community.
- Do not call any other Octopus function until
switchCommunitycompletes. - When it completes, reconnect your user if you use SSO.
- Rebuild any community screen on display, so it binds to the new community.
- Android
- iOS
- Flutter
- React Native
- Unity
The OctopusSDK object stays the same after the switch; only its internal state changes.
Parameters:
context(Context): required. The application context.apiKey(String): required. The API key of the new community.connectionMode(ConnectionMode): optional, defaultConnectionMode.SSO().deepLinksBasePaths(List<String>): optional, default empty. See deep links.apiServer(ApiServer?): optional, defaultnull. See custom server.
switchCommunity and connectUser are suspend functions: call them from a coroutine.
import com.octopuscommunity.sdk.OctopusSDK
import com.octopuscommunity.sdk.domain.model.ClientUser
import com.octopuscommunity.sdk.domain.model.ConnectionMode
import com.octopuscommunity.sdk.domain.model.ProfileField
import com.octopuscommunity.sdk.domain.model.Resource
import com.octopuscommunity.sdk.domain.network.OctopusResult
lifecycleScope.launch {
OctopusSDK.switchCommunity(
context = applicationContext,
apiKey = "NEW_COMMUNITY_API_KEY",
connectionMode = ConnectionMode.SSO(
appManagedFields = setOf(ProfileField.NICKNAME, ProfileField.PICTURE)
),
)
// Reconnect the user to the new community (SSO)
val result = OctopusSDK.connectUser(
ClientUser(
userId = yourUser.id,
profile = ClientUser.Profile(
nickname = yourUser.name,
bio = yourUser.bio,
picture = yourUser.avatarUrl?.let { Resource.Remote(it) },
),
)
) {
// Fetch the user token from your backend
fetchOctopusToken()
}
when (result) {
is OctopusResult.Success -> { /* The user is connected */ }
is OctopusResult.Failure -> { /* Show an error or retry */ }
}
}
The OctopusSDK object stays the same after the switch; only its internal state changes.
Parameters:
apiKey: String: required. The API key of the new community.connectionMode: ConnectionMode: optional, default.octopus(deepLink: nil).configuration: Configuration: optional, default.init(). Pass the sameapiServeragain to stay on a custom server.
do {
try await octopus.switchCommunity(
apiKey: "NEW_COMMUNITY_API_KEY",
connectionMode: .sso(
.init(
appManagedFields: [.nickname, .picture],
loginRequired: {
// Open your login flow
},
modifyUser: { fieldToEdit in
// Open your profile editor
}
)
)
)
// Reconnect the user to the new community (SSO)
try await octopus.connectUser(
ClientUser(
userId: yourUser.id,
profile: .init(nickname: yourUser.name, bio: yourUser.bio)
),
tokenProvider: {
// Fetch the user token from your backend
try await fetchOctopusToken()
}
)
// Rebuild the displayed OctopusHomeScreen, for example by changing its .id
octopusHomeScreenId = UUID()
} catch {
// Handle the error
}
See the Switch Community scenario in the iOS sample.
The OctopusSDK object stays the same after the switch; only its internal state changes.
Parameters:
apiKey(String): required. The API key of the new community.appManagedFields(List<ProfileField>?): optional. The profile fields your app manages.apiServer(ApiServer?): optional.nulltargets the default endpoint.
In Octopus Auth mode, call switchCommunityOctopusAuth(apiKey:, deepLink:, apiServer:) instead.
await octopus.switchCommunity(
apiKey: 'NEW_COMMUNITY_API_KEY',
appManagedFields: [ProfileField.nickname, ProfileField.picture],
);
// Reconnect the user to the new community (SSO)
final result = await octopus.connectUser(
userId: yourUserId,
nickname: yourUserNickname,
tokenProvider: () async => fetchOctopusToken(),
);
if (result is OctopusFailure) {
// Show an error or retry
}
Give the displayed OctopusHomeScreen a key: ValueKey(apiKey), so Flutter rebuilds the native view for the new community.
Parameters:
apiKey(string): required. The API key of the new community.connectionMode: required. The same shape as ininitialize():{ type: 'sso', appManagedFields }or{ type: 'octopus', deepLink? }.apiServer({ host: string; port?: number }): optional. Omit it to use the default endpoint.
The token provider you registered with useUserTokenProvider or addUserTokenRequestListener stays active across the switch.
import {
connectUser,
isConnectUserError,
switchCommunity,
useUserTokenProvider,
} from '@octopus-community/react-native';
// In a component that stays mounted:
useUserTokenProvider(async () => fetchOctopusToken());
async function moveToCommunity(apiKey: string) {
await switchCommunity({
apiKey,
connectionMode: { type: 'sso', appManagedFields: ['username', 'profilePicture'] },
});
// Reconnect the user to the new community (SSO)
try {
await connectUser({ userId: yourUser.id, profile: { username: yourUser.name } });
} catch (error) {
if (isConnectUserError(error)) {
// Show an error or retry
}
}
}
Remount <OctopusUIView> after the switch: change its key prop, for example to the new API key.
Parameters:
apiKey(string): the API key of the new community.mode(ConnectionMode): the connection mode and app-managed fields.apiServerHost(string) ≥ 1.14.0: the custom host, as inInitialize. Pass it withapiServerPort, or use the overload without them.apiServerPort(int) ≥ 1.14.0: the port for the custom host, usually443.onCompleted(Action): called on the Unity main thread. Reconnect your SSO user here.onError(Action<string>): called on the Unity main thread. A failure does not restore the previous community.
SwitchCommunity closes the community UI; the next Open shows the new community. Call it from the Unity main thread.
OctopusSDK.SwitchCommunity("NEW_COMMUNITY_API_KEY",
ConnectionMode.SSO(ProfileField.NICKNAME, ProfileField.PICTURE),
onCompleted: () =>
{
// Reconnect your user with OctopusSDK.ConnectUser, then open the community.
},
onError: message => { /* Handle the failure */ });
// On a custom server, pass the host again on every switch:
OctopusSDK.SwitchCommunity("NEW_COMMUNITY_API_KEY",
ConnectionMode.SSO(ProfileField.NICKNAME, ProfileField.PICTURE),
apiServerHost: "api.example.com",
apiServerPort: 443,
onCompleted: () => { /* Reconnect your user */ },
onError: message => { /* Handle the failure */ });
The host passed to Initialize is not remembered. SwitchCommunityOctopusAuth(apiKey, onCompleted, onError) always targets the default server; for Octopus Auth on a custom host, call SwitchCommunity with ConnectionMode.OctopusAuth() and the host.
Reset and stop the SDK
Android availableiOS not availableFlutter ≥ 1.12.0React Native ≥ 1.13.0Unity ≥ 1.13.0
- Reset signs out the user and clears the local SDK data. The SDK stays initialized on the same community. Use it for a full sign-out of your app.
- Stop releases the SDK. Initialize it again, or switch community, before any other call.
- Android
- iOS
- Flutter
- React Native
- Unity
reset() is a suspend function. It ends the session and removes the SDK data stored on the device: user data, content and cached images. stop() cancels ongoing operations and keeps the data. Both do nothing when the SDK is not initialized.
lifecycleScope.launch {
// Sign out and clear the local data; the SDK stays initialized
OctopusSDK.reset()
}
// Release the SDK; call initialize() or switchCommunity() before reusing it
OctopusSDK.stop()
Flows you collect from the SDK do not complete on stop(). They emit again once you initialize the SDK.
Reset and stop are not yet available on iOS. To sign the user out, call disconnectUser(). To move to another community, call switchCommunity. To release the SDK, drop your reference to the OctopusSDK instance.
try await octopus.disconnectUser()
reset() signs out the user and keeps the SDK initialized. On Android, it also clears the SDK data stored on the device; on iOS, it only disconnects the user. stop() releases the SDK: isInitialised becomes false, and the event streams stay open.
// Sign out and clear the local data; the SDK stays initialized
await octopus.reset();
// Release the SDK; call initialize() or switchCommunity() before reusing it
await octopus.stop();
reset() signs out the user and keeps the SDK initialized. On Android, it also clears the SDK data stored on the device; on iOS, it only disconnects the user. reset() rejects when the SDK is not initialized. stop() releases the SDK.
import { reset, stop } from '@octopus-community/react-native';
// Sign out and clear the local data; the SDK stays initialized
await reset();
// Release the SDK; call initialize() or switchCommunity() before reusing it
await stop();
Unity exposes three operations. Call them from the Unity main thread, one at a time, and wait for onCompleted or onError.
| Method | Effect |
|---|---|
Reset(onCompleted, onError) | Closes the UI and signs out the user on the same community. Android also clears SDK data and image caches; iOS only disconnects. On iOS in Octopus Auth mode, it reports onError. |
Stop(onCompleted, onError) | Closes and releases the SDK. Call Initialize or SwitchCommunity before reusing it. Android keeps persistent data. |
Close() | Dismisses the UI only. The user stays connected. |
OctopusSDK.Reset(
onCompleted: () => { /* Ready to reconnect on the same community */ },
onError: message => { /* Handle the failure */ });
// To release the SDK instead:
OctopusSDK.Stop(
onCompleted: () => { /* Initialize again before reuse */ },
onError: message => { /* Handle the failure */ });
Reset and stop also end community data observation. C# event subscriptions stay registered: unsubscribe when the screen that owns them is disposed.
Check whether the SDK is initialized
Android availableiOS not availableFlutter ≥ 1.12.0React Native ≥ 1.13.0Unity not available
Read the initialization state to gate a splash screen, or observe it to update your UI when the SDK starts or stops. It becomes true after initialize or switch community, and false after stop. Reset does not change it.
- Android
- iOS
- Flutter
- React Native
- Unity
if (!OctopusSDK.isInitialised) {
OctopusSDK.initialize(context = applicationContext, apiKey = "YOUR_API_KEY")
}
// Observe the state
lifecycleScope.launch {
OctopusSDK.isInitialisedFlow.collect { ready -> showCommunityEntry(ready) }
}
The initialization state is not yet available on iOS. The SDK is ready as soon as OctopusSDK(apiKey:) returns without throwing.
if (!OctopusSDK.isInitialised) {
await octopus.initialize(apiKey: 'YOUR_API_KEY');
}
// Observe the state; the current value is replayed to new listeners
final subscription = OctopusSDK.isInitialisedFlow.listen((ready) {
setState(() => _sdkReady = ready);
});
isInitialised() is synchronous. It reflects the last lifecycle call made from JavaScript.
import {
addIsInitialisedListener,
initialize,
isInitialised,
} from '@octopus-community/react-native';
if (!isInitialised()) {
await initialize({ apiKey: 'YOUR_API_KEY', connectionMode: { type: 'sso', appManagedFields: [] } });
}
// Observe the state
const subscription = addIsInitialisedListener((ready) => setSdkReady(ready));
// Later
subscription.remove();
The initialization state is not yet available on Unity. Track the onCompleted and onError callbacks of the lifecycle methods instead.
Behavior and limits
- What each lifecycle operation keeps or clears is summarized in How it works, and the versions per platform are in Platform support.
- A failed switch does not restore the previous community. Initialize again or retry the switch.
- A custom server is not remembered across a switch. Pass it again on every call that must stay on it.