Skip to main content
Version: v0.13 (Latest)

Scopes and Dependency Injection

The Kiwa eLicense Holder SDK uses a hierarchical scope system to manage component lifecycles and service instances. This architecture is inherited from the Identity Development Kit (IDK).

Scope Hierarchy

The SDK operates with three nested scopes:

Kiwa SDK Scope Hierarchy
ScopeLifecyclePurpose
AppApplication lifetimeSingletons, configuration, context manager
ContextTenant/PrincipalMulti-tenancy support, per-user configuration
SessionWork sessionService instances, coroutine grouping

Initialization

Step 1: Initialize the Application Component

The app component is typically created once during application startup. It manages application-wide singletons and provides access to the context manager.

import com.sphereon.kiwa.elicense.sdk.KiwaSdkAppComponent

// Create the application component
val appComponent = KiwaSdkAppComponent.init(
application = yourApplication, // Android Application instance
appId = "your-app-id", // Unique identifier for your app
profile = "prod", // Environment profile (prod, test, dev, etc.)
version = "1.0.0" // Your application version
)

Parameters:

ParameterDescription
applicationPlatform application instance (Android Application or iOS UIApplication)
appIdUnique identifier used in configuration management
profileEnvironment profile for configuration separation (e.g., "prod", "test")
versionApplication version (reserved for future use)

Step 2: Initialize the Context Scope

The context scope manages tenant and principal (user) information. This scope enables multi-tenancy and per-user configuration.

import com.sphereon.idk.common.context.DefaultTenantInputString
import com.sphereon.idk.common.context.DefaultPrincipalInputString

// Initialize with tenant and principal
val userContextInstance = appComponent.userContextManager
.createOrGetFromInputs(
tenantInput = DefaultTenantInputString(tenant = "your-tenant-id"),
principalInput = DefaultPrincipalInputString(principal = "user@example.com")
)
val userContextComponent = userContextInstance.component

// Alternative: Anonymous context (for apps without multi-tenancy)
// val userContextInstance = appComponent.userContextManager.getAnonymous(makeActive = true)
tip

For mobile apps without multi-tenancy requirements, use the anonymous context. This simplifies initialization while still satisfying the SDK's scope requirements.

Step 3: Initialize the Session Scope

The session scope groups related operations and manages service instances. What constitutes a "session" is application-defined:

  • Mobile apps: From user login to logout
  • REST APIs: A single request/response cycle
  • Background tasks: Duration of the task
// Initialize session with a unique identifier
val sessionContextManager = userContextInstance.sessionContextManager
val sessionInstance = sessionContextManager.createOrGetFromId(
sessionId = "unique-session-id",
makeActive = true
)
val sessionComponent = sessionInstance.component
note

When a session is destroyed, all outstanding asynchronous work within that session is cancelled. This ensures clean resource management.

Accessing Services

Once the scopes are initialized, retrieve service instances from the session:

import com.sphereon.kiwa.elicense.sdk.getKiwaServices

// Get the main KiwaServices entry point
val kiwaServices = sessionInstance.getKiwaServices()

// Alternative methods:
// val kiwaServices: KiwaServices = sessionInstance.getService()
// val kiwaServices = sessionInstance.getService<KiwaServices>(KiwaServices.SERVICE_ID)

// Access individual services
val authService = kiwaServices.auth // Wallet certificate operations
val holderService = kiwaServices.holder // License lifecycle operations
val cryptoServices = kiwaServices.crypto // Cryptographic operations

Service Interfaces

ServicePurposeKey Operations
KiwaServicesMain entry pointAccess to all sub-services
AuthServiceAuthenticationgetWalletCertificate, renewWalletCertificate
HolderServiceLicense managementissue, assign, confirm, decode
KiwaCryptoServicesCryptographyCertificate/key storage and management

Complete Initialization Example

Here's a complete example showing all initialization steps:

import com.sphereon.kiwa.elicense.sdk.KiwaSdkAppComponent
import com.sphereon.kiwa.elicense.sdk.getKiwaServices
import com.sphereon.idk.common.context.DefaultTenantInputString
import com.sphereon.idk.common.context.DefaultPrincipalInputString

class MyApplication : Application() {
lateinit var kiwaServices: KiwaServices
private set

override fun onCreate() {
super.onCreate()
initializeKiwaSDK()
}

private fun initializeKiwaSDK() {
// Step 1: App component
val appComponent = KiwaSdkAppComponent.init(
application = this,
appId = "my-kiwa-app",
profile = "prod",
version = BuildConfig.VERSION_NAME
)

// Step 2: Context scope (using anonymous for simplicity)
val userContextInstance = appComponent.userContextManager
.getAnonymous(makeActive = true)

// Step 3: Session scope
val sessionInstance = userContextInstance.sessionContextManager
.createOrGetFromId(sessionId = "main-session", makeActive = true)

// Step 4: Get services
kiwaServices = sessionInstance.getKiwaServices()
}
}

Advanced: Custom Dependency Injection

If you're using kotlin-inject in your own application, you can inject KiwaServices directly:

import me.tatarka.inject.annotations.Inject
import me.tatarka.inject.annotations.Component
import com.sphereon.kiwa.elicense.sdk.KiwaServices

@Inject
class MyLicenseManager(
private val kiwaServices: KiwaServices
) {
suspend fun fetchLicenses() {
val result = kiwaServices.holder.commands.issue.execute(/* request */)
// Handle result
}
}

See the IDK Dependency Injection documentation for advanced DI patterns.

Next Steps

With the SDK initialized, proceed to:

  1. Holder Functions - Implement the complete license lifecycle
  2. eLicense Mdocs - Work with license display and verification