Sessions
Initialisation through the Comapi (or RxComapi) class gives you a client object that you use for any further communication with the SDK.
Learn more in Initialise.
In order to communicate with our services you must start a session in the SDK. You only need to do this once per user log-in. After that, the SDK re-authenticates the session until you explicitly end it.
Where to call this. CallstartSession()once yourChallengeHandleris able to produce a valid JWT. For apps with sign-in, that's typically after the user signs in — thesubclaim in the JWT identifies the profile. For apps that support anonymous users, thesubcan be a stable device-scoped identifier (e.g. a GUID stored locally), in which case you can start the session as soon as the SDK is initialised. See Create a JWT for the rules around thesubclaim.
Imports note. The reactive surface in this SDK uses RxJava 1 types —rx.Observer,rx.schedulers.Schedulers, andrx.android.schedulers.AndroidSchedulers. Make sure your IDE imports therx.*packages rather thanio.reactivex.*(RxJava 2) orio.reactivex.rxjava3.*(RxJava 3), otherwise the samples below won't compile.
Getting the client.Comapi.getShared()andRxComapi.getShared()return the non-null singleton onceinitialiseShared(...)has been called — the client instance is created synchronously insideinitialiseShared(...), before the async network init finishes. They only throwRuntimeExceptionif you call them beforeinitialiseShared(...)has run at all (e.g. from aContentProviderthat runs ahead ofApplication.onCreate()). With init inApplication.onCreate(),getShared()is safe to call from any Activity, Service, or BroadcastReceiver.
Start
To create a session (log a user in to our services):
client.service().session().startSession(new Callback<Session>() {
@Override
public void success(Session session) {
// Session started — SDK can now send and receive on behalf of this user
if (session.isSuccessfullyCreated()) {
Log.d("Comapi", "Session ready for profile " + session.getProfileId());
}
}
@Override
public void error(Throwable t) {
Log.e("Comapi", "startSession failed", t);
}
});rxClient.service().session().startSession()
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Observer<Session>() {
@Override public void onNext(Session session) {
// Session started
}
@Override public void onCompleted() { }
@Override public void onError(Throwable t) {
Log.e("Comapi", "startSession failed", t);
}
});client.service().session().startSession(object : Callback<Session> {
override fun success(session: Session) {
if (session.isSuccessfullyCreated) {
Log.d("Comapi", "Session ready for profile ${session.profileId}")
}
}
override fun error(t: Throwable) {
Log.e("Comapi", "startSession failed", t)
}
})rxClient.service().session().startSession()
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(object : Observer<Session> {
override fun onNext(session: Session) {
// Session started
}
override fun onCompleted() { }
override fun onError(t: Throwable) {
Log.e("Comapi", "startSession failed", t)
}
})This asks ComapiAuthenticator (provided when initialising) for a JWT token, then the SDK creates a session server-side for the profile ID obtained from the token. Any subsequent call to the services uses the same authentication details.
The Session result
Session resultCallback<Session> delivers a com.comapi.Session (not the network model class of the same name):
// Profile ID of the user the session was created for
session.getProfileId();
// true if the server returned all the fields a valid session needs (sessionId, profileId, accessToken, expiresOn)
session.isSuccessfullyCreated();session.profileId
session.isSuccessfullyCreatedTreat isSuccessfullyCreated == false as a soft failure — the callback's success path can still fire even if the server returned an incomplete session. Always check the flag before treating the SDK as authenticated.
Kotlin: starting a session with Coroutines
For Kotlin codebases, wrap startSession in a suspend function so callers can await the result without nesting callbacks:
suspend fun ComapiClient.startSessionAwait(): Session =
suspendCancellableCoroutine { cont ->
service().session().startSession(object : Callback<Session> {
override fun success(session: Session) {
if (cont.isActive) cont.resume(session) {}
}
override fun error(t: Throwable) {
if (cont.isActive) cont.resumeWithException(t)
}
})
}
// Usage from a ViewModel after the user signs in:
viewModelScope.launch {
try {
val session = Comapi.getShared().startSessionAwait()
if (session.isSuccessfullyCreated) {
// SDK is now authenticated
} else {
Log.w("Comapi", "Session returned without required fields")
}
} catch (t: Throwable) {
Log.e("Comapi", "startSession failed", t)
}
}
Thetry / catchin the example above covers the rare case whereComapi.getShared()is called beforeComapi.initialiseShared(...)— it throwsRuntimeExceptionin that case. If you always initialise inApplication.onCreate(), this catch is belt-and-braces.
Stop
To close a currently opened session — for example when the user signs out, or when you want to switch users on the same device:
client.service().session().endSession(new Callback<ComapiResult<Void>>() {
@Override
public void success(ComapiResult<Void> result) {
// Session ended
}
@Override
public void error(Throwable t) {
Log.e("Comapi", "endSession failed", t);
}
});rxClient.service().session().endSession()
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Observer<ComapiResult<Void>>() {
@Override public void onNext(ComapiResult<Void> result) {
// Session ended
}
@Override public void onCompleted() { }
@Override public void onError(Throwable t) {
Log.e("Comapi", "endSession failed", t);
}
});client.service().session().endSession(object : Callback<ComapiResult<Void>> {
override fun success(result: ComapiResult<Void>) {
// Session ended
}
override fun error(t: Throwable) {
Log.e("Comapi", "endSession failed", t)
}
})rxClient.service().session().endSession()
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(object : Observer<ComapiResult<Void>> {
override fun onNext(result: ComapiResult<Void>) {
// Session ended
}
override fun onCompleted() { }
override fun onError(t: Throwable) {
Log.e("Comapi", "endSession failed", t)
}
})Kotlin: ending a session with Coroutines
suspend fun ComapiClient.endSessionAwait(): ComapiResult<Void> =
suspendCancellableCoroutine { cont ->
service().session().endSession(object : Callback<ComapiResult<Void>> {
override fun success(result: ComapiResult<Void>) {
if (cont.isActive) cont.resume(result) {}
}
override fun error(t: Throwable) {
if (cont.isActive) cont.resumeWithException(t)
}
})
}
// Usage on sign-out:
viewModelScope.launch {
runCatching { Comapi.getShared().endSessionAwait() }
.onFailure { t -> Log.e("Comapi", "endSession failed", t) }
}
Switching users. When a different user signs in on the same device, end the current session first, then update thesubclaim yourChallengeHandlerreturns (so the new JWT identifies the new user), then callstartSession()again. Don't try to swap users without anendSession()in between — the server-side session is still bound to the previous profile until you close it.