kmp-chime-sdk
0.4.0indexedUnified API for Amazon Chime SDK meetings: join/leave, send/receive audio/video, composable video views, device routing and controls, real-time topic messaging, and session event callbacks.
Unified API for Amazon Chime SDK meetings: join/leave, send/receive audio/video, composable video views, device routing and controls, real-time topic messaging, and session event callbacks.
A Kotlin Multiplatform library for Amazon Chime SDK meetings on Android and iOS. Exposes a single shared API via Compose Multiplatform: join meetings, send and receive audio/video, and exchange real-time data messages without writing platform-specific code.
Add these to your KMP module's build.gradle.kts:
kotlin {
sourceSets {
// Shared API + iOS implementation (cinterop baked in)
commonMain.dependencies {
implementation("com.wannaverse:chimesdk:<version>")
}
// Android implementation
androidMain.dependencies {
implementation("com.wannaverse:chimesdk-android:<version>")
}
}
}
The
chimesdkartifact covers all targets including iOS. The separatechimesdk-androidartifact is needed on Android because the Chime SDK ships native media libraries that must be declared explicitly for the Android build.
1. Set the application context before calling setContent. The library needs it to initialise the Chime session:
class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
com.wannaverse.chimesdk.appContext = applicationContext
super.onCreate(savedInstanceState)
// ...
}
}
2. Request runtime permissions before joining a meeting:
// CAMERA and RECORD_AUDIO must be granted at runtime (Android 6+)
val launcher = rememberLauncherForActivityResult(
ActivityResultContracts.RequestMultiplePermissions()
) { /* handle results */ }
launcher.launch(arrayOf(Manifest.permission.CAMERA, Manifest.permission.RECORD_AUDIO))
The following permissions are declared in the library manifest and merged automatically:
CAMERA, RECORD_AUDIO, MODIFY_AUDIO_SETTINGS, INTERNET, ACCESS_NETWORK_STATE, BLUETOOTH_CONNECT
The iOS implementation is compiled entirely in Kotlin via cinterop. No Swift files are required in your project. You only need the AmazonChimeSDK framework available for the Xcode linker.
https://github.com/aws/amazon-chime-sdk-ios-spmUp to Next Minor from 0.25.0# iosApp/Podfile
platform :ios, '16.0'
use_frameworks!
target 'iosApp' do
pod 'AmazonChimeSDK', '~> 0.25.0'
end
cd iosApp && pod install
Both options require these keys in Info.plist:
<key>NSCameraUsageDescription</key>
<string>Camera is used for video calls.</string>
<key>NSMicrophoneUsageDescription</key>
<string>Microphone is used for audio calls.</string>
Pass credentials from your backend (obtained via the AWS CreateMeeting + CreateAttendee APIs):
leaveMeeting() // ends the session and releases all resources
Render video anywhere in your Compose layout. The composables render nothing when no tile is active.
@Composable
fun MeetingScreen() {
Box(Modifier.fillMaxSize()) {
// Full-screen remote video
RemoteVideoView(
modifier = Modifier.fillMaxSize(),
tileId = remoteTileId,
isOnTop = false
)
// Local camera preview
LocalVideoView(
modifier = Modifier.size(120.dp, 160.dp).align(Alignment.BottomEnd),
cameraFacing = CameraFacing.FRONT,
isOnTop = true
)
}
}
Call startLocalVideo() after joining to begin sending camera frames.
startLocalVideo() // start sending camera frames
stopLocalVideo() // stop camera
setMute(true) // mute microphone
switchCamera() // toggle front / back camera
switchAudioDevice(device.id) // route audio to a specific output
leaveMeeting() // end session and release all resources
Subscribe to named topics after joining. Any number of topics can be active simultaneously.
// Subscribe
subscribeToTopic("chat") { message ->
println("${message.senderId}: ${message.content}")
}
// Send
sendRealtimeMessage(topic = "chat", data = "Hello!", lifetimeMs = 0)
// Unsubscribe
unsubscribeFromTopic("chat")
MIT
joinMeeting(
externalMeetingId = info.externalMeetingId,
meetingId = info.meetingId,
audioHostURL = info.audioHostURL,
audioFallbackURL = info.audioFallbackURL,
turnControlURL = info.turnControlURL,
signalingURL = info.signalingURL,
ingestionURL = info.ingestionURL,
attendeeId = info.attendeeId,
externalUserId = info.externalUserId,
joinToken = info.joinToken,
realTimeListener = object : RealTimeEventListener {
override fun onAttendeesJoined(attendeeIds: List<String>) { /* ... */ }
override fun onAttendeesLeft(attendeeIds: List<String>) { /* ... */ }
override fun onAttendeesDropped(attendeeIds: List<String>) { /* ... */ }
override fun onAttendeesMuted(attendeeIds: List<String>) { /* ... */ }
override fun onAttendeesUnmuted(attendeeIds: List<String>) { /* ... */ }
override fun onSignalStrengthChanged(attendeeId: String, externalAttendeeId: String, signal: Int) { /* ... */ }
override fun onVolumeChanged(attendeeId: String, externalAttendeeId: String, volume: Int) { /* ... */ }
override fun onAudioDevicesUpdated(devices: List<AudioDevice>, selected: AudioDevice?) { /* ... */ }
},
onActiveSpeakersChanged = { speakers -> /* highlight active speaker */ },
onConnectionStatusChanged = { status ->
if (status == ConnectionStatus.CONNECTED) { /* update UI */ }
},
onRemoteVideoAvailable = { isAvailable, count -> /* show/hide remote grid */ },
onSessionError = { message, isRecoverable -> /* handle error */ },
onLocalAttendeeIdAvailable = { id -> /* store local attendee ID */ },
isJoiningOnMute = false
)
Surfaced from shared tags and platforms — no rankings paid for.