### Handling Permission Exceptions
Source: https://context7.com/icerockdev/moko-permissions/llms.txt
Implement try-catch blocks to handle `DeniedException`, `DeniedAlwaysException`, and `RequestCanceledException` when requesting permissions with `controller.providePermission()`. `DeniedAlwaysException` requires guiding the user to settings.
```kotlin
import dev.icerock.moko.permissions.DeniedAlwaysException
import dev.icerock.moko.permissions.DeniedException
import dev.icerock.moko.permissions.RequestCanceledException
suspend fun requestWithFullHandling(
controller: PermissionsController,
permission: Permission,
showRationale: () -> Unit,
showSettingsPrompt: (Permission) -> Unit
) {
try {
controller.providePermission(permission)
println("Success: ${permission::class.simpleName} granted")
} catch (e: DeniedAlwaysException) {
// e.permission holds the denied Permission instance
showSettingsPrompt(e.permission) // Guide user to Settings
} catch (e: DeniedException) {
// e is also the base type — DeniedAlwaysException is a subtype
showRationale() // Explain why the permission is needed, allow retry
} catch (e: RequestCanceledException) {
// Android only: the system dialog was dismissed (e.g., back button)
println("Request cancelled for ${e.permission}")
}
}
```
--------------------------------
### iOS PermissionsController Setup
Source: https://context7.com/icerockdev/moko-permissions/llms.txt
On iOS, no binding is required. Instantiate `PermissionsController` directly and pass it to your shared code. Ensure all required permission keys are added to your Info.plist.
```swift
// Swift (iOS AppDelegate or SwiftUI View)
import MultiPlatformLibrary // your shared KMP module
let permissionsController = PermissionsController()
let viewModel = MediaViewModel(permissionsController: permissionsController)
```
```xml
NSCameraUsageDescription
We need camera access to take photos.
NSLocationWhenInUseUsageDescription
We need location to show nearby items.
NSMicrophoneUsageDescription
We need microphone access for audio recording.
NSPhotoLibraryUsageDescription
We need gallery access to pick photos.
NSBluetoothAlwaysUsageDescription
We need Bluetooth to connect to devices.
NSContactsUsageDescription
We need contacts access to find your friends.
NSMotionUsageDescription
We need motion data for fitness tracking.
```
--------------------------------
### Compose Multiplatform Permission Request
Source: https://github.com/icerockdev/moko-permissions/blob/master/README.md
Example of requesting a permission (REMOTE_NOTIFICATION) in a Compose Multiplatform UI using `rememberPermissionsControllerFactory`.
```kotlin
@Composable
fun Sample() {
val factory: PermissionsControllerFactory = rememberPermissionsControllerFactory()
val controller: PermissionsController = remember(factory) { factory.createPermissionsController() }
val coroutineScope: CoroutineScope = rememberCoroutineScope()
Button(
onClick = {
coroutineScope.launch {
controller.providePermission(Permission.REMOTE_NOTIFICATION)
}
}
) {
Text(text = "give permissions")
}
}
```
--------------------------------
### Inspect Detailed Permission State
Source: https://context7.com/icerockdev/moko-permissions/llms.txt
Use `getPermissionState` to get a detailed enum of the permission status. This is helpful for rendering UI elements conditionally without triggering a permission request.
```kotlin
import dev.icerock.moko.permissions.PermissionState
import dev.icerock.moko.permissions.location.BACKGROUND_LOCATION
import dev.icerock.moko.permissions.location.LOCATION
// Common code
suspend fun describeLocationState(controller: PermissionsController) {
when (controller.getPermissionState(Permission.LOCATION)) {
PermissionState.NotDetermined -> println("Never asked — safe to request")
PermissionState.NotGranted -> println("Not granted (Android: could be denied or not asked)")
PermissionState.Granted -> println("Granted — feature available")
PermissionState.Denied -> println("Denied (Android) — show rationale before retry")
PermissionState.DeniedAlways -> println("Permanently denied — direct to Settings")
}
// Background location needs foreground first
val bgState = controller.getPermissionState(Permission.BACKGROUND_LOCATION)
if (bgState != PermissionState.Granted) {
println("Background location not available: $bgState")
}
}
```
--------------------------------
### openAppSettings()
Source: https://context7.com/icerockdev/moko-permissions/llms.txt
Opens the application's system settings page, allowing the user to manually change permissions, especially useful after a permission has been permanently denied.
```APIDOC
## `openAppSettings()` — Navigate to System Settings
### Description
Opens the app's system settings page so the user can manually change a permanently denied permission. Call this after catching `DeniedAlwaysException`.
### Method
`openAppSettings`
### Endpoint
N/A (This is a function call, not an HTTP endpoint)
### Usage
Call this function when a user has permanently denied a permission and needs to be directed to the system settings to enable it.
```
--------------------------------
### Open App Settings for Denied Permissions
Source: https://context7.com/icerockdev/moko-permissions/llms.txt
Call `openAppSettings()` to direct the user to the system settings for your app. This is typically used after catching a `DeniedAlwaysException` to allow manual permission changes.
```kotlin
// Common code
suspend fun handleDeniedAlways(controller: PermissionsController, permission: Permission) {
try {
controller.providePermission(permission)
} catch (e: DeniedAlwaysException) {
// Show a dialog to the user explaining why, then:
controller.openAppSettings()
// On Android: opens Settings > Apps > YourApp > Permissions
// On iOS: opens Settings > YourApp
}
}
```
--------------------------------
### Initializing ViewModel with Permissions Controller (iOS)
Source: https://github.com/icerockdev/moko-permissions/blob/master/README.md
On iOS, initialize your ViewModel by passing the platform-specific permissions controller directly.
```swift
// Just pass the platform implementation of the permission controller to a common code.
let viewModel = ViewModel(permissionsController: PermissionsController())
```
--------------------------------
### Listing All Available Permissions
Source: https://context7.com/icerockdev/moko-permissions/llms.txt
Access all available permissions via `Permission.Companion` extension properties. Each permission requires its specific module to be included in Gradle dependencies.
```kotlin
// Bluetooth (permissions-bluetooth)
Permission.BLUETOOTH_LE // Bluetooth Low Energy
Permission.BLUETOOTH_SCAN // Scan for BLE devices (Android 12+)
Permission.BLUETOOTH_CONNECT // Connect to BLE devices (Android 12+)
Permission.BLUETOOTH_ADVERTISE // Advertise via BLE (Android 12+)
// Camera (permissions-camera)
Permission.CAMERA
// Contacts (permissions-contacts)
Permission.CONTACTS // Read + write contacts
Permission.READ_CONTACTS // Read-only contacts
// Gallery (permissions-gallery)
Permission.GALLERY
// Location (permissions-location)
Permission.LOCATION // Fine (GPS) location
Permission.COARSE_LOCATION // Network-based location
Permission.BACKGROUND_LOCATION // Background location (Android 10+)
// Microphone (permissions-microphone)
Permission.RECORD_AUDIO
// Motion (permissions-motion)
Permission.MOTION // Activity recognition / CoreMotion
// Notifications (permissions-notifications)
Permission.REMOTE_NOTIFICATION // Push notifications
// Storage (permissions-storage)
Permission.STORAGE // Read external storage
Permission.WRITE_STORAGE // Write external storage
```
--------------------------------
### Create Permission Controller Mock
Source: https://context7.com/icerockdev/moko-permissions/llms.txt
Use `createPermissionControllerMock` from the `permissions-test` module to mock permission behavior for unit tests. Configure pre-granted and allowed permissions to simulate different scenarios without needing a device or emulator.
```kotlin
import dev.icerock.moko.permissions.DeniedException
import dev.icerock.moko.permissions.PermissionState
import dev.icerock.moko.permissions.camera.CAMERA
import dev.icerock.moko.permissions.gallery.GALLERY
import dev.icerock.moko.permissions.test.createPermissionControllerMock
import kotlinx.coroutines.test.runTest
import kotlin.test.Test
import kotlin.test.assertFailsWith
import kotlin.test.assertTrue
class MediaViewModelTest {
@Test
fun `camera request succeeds when allowed`() = runTest {
val mock = createPermissionControllerMock(
allow = setOf(Permission.CAMERA), // these will be granted when requested
granted = setOf(Permission.GALLERY) // pre-granted without asking
)
// Already granted — no dialog shown
assertTrue(mock.isPermissionGranted(Permission.GALLERY))
// Requesting allowed permission succeeds silently
mock.providePermission(Permission.CAMERA)
assertTrue(mock.isPermissionGranted(Permission.CAMERA))
// State reflects the grant
val state = mock.getPermissionState(Permission.CAMERA)
assertTrue(state == PermissionState.Granted)
}
@Test
fun `camera request throws DeniedException when not in allow set`() = runTest {
val mock = createPermissionControllerMock(
allow = emptySet() // nothing allowed
)
assertFailsWith {
mock.providePermission(Permission.CAMERA)
}
}
}
```
--------------------------------
### Request Camera Permission with Error Handling
Source: https://context7.com/icerockdev/moko-permissions/llms.txt
Use PermissionsController to request camera permission. Handle exceptions for permanent denial (open settings), temporary denial (show rationale), and request cancellation.
```kotlin
// --- Common code (shared module) ---
import dev.icerock.moko.permissions.DeniedAlwaysException
import dev.icerock.moko.permissions.DeniedException
import dev.icerock.moko.permissions.Permission
import dev.icerock.moko.permissions.PermissionsController
import dev.icerock.moko.permissions.RequestCanceledException
import dev.icerock.moko.permissions.camera.CAMERA
import dev.icerock.moko.permissions.location.LOCATION
import dev.icerock.moko.permissions.microphone.RECORD_AUDIO
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.launch
class MediaViewModel(
val permissionsController: PermissionsController,
private val scope: CoroutineScope
) {
fun requestCamera() {
scope.launch {
try {
permissionsController.providePermission(Permission.CAMERA)
// Camera access granted — proceed with feature
openCamera()
} catch (e: DeniedAlwaysException) {
// User permanently denied; prompt to open Settings
permissionsController.openAppSettings()
} catch (e: DeniedException) {
// User denied this time; can ask again later
showRationale()
} catch (e: RequestCanceledException) {
// Android-only: dialog was dismissed without a choice
}
}
}
fun requestLocation() {
scope.launch {
val granted = permissionsController.isPermissionGranted(Permission.LOCATION)
if (!granted) {
permissionsController.providePermission(Permission.LOCATION)
}
}
}
}
```
--------------------------------
### Importing Permission Modules
Source: https://context7.com/icerockdev/moko-permissions/llms.txt
Import the relevant sub-modules for each permission type you intend to use. Ensure the corresponding Gradle dependency is included.
```kotlin
import dev.icerock.moko.permissions.Permission
// Import the relevant sub-modules:
import dev.icerock.moko.permissions.bluetooth.*
import dev.icerock.moko.permissions.camera.*
import dev.icerock.moko.permissions.contacts.*
import dev.icerock.moko.permissions.gallery.*
import dev.icerock.moko.permissions.location.*
import dev.icerock.moko.permissions.microphone.*
import dev.icerock.moko.permissions.motion.*
import dev.icerock.moko.permissions.notifications.*
import dev.icerock.moko.permissions.storage.*
```
--------------------------------
### Add Moko Permissions Dependencies to Gradle
Source: https://context7.com/icerockdev/moko-permissions/llms.txt
Include the core API and any desired permission-specific modules in your shared module's build.gradle.kts file. Also add the Compose Multiplatform integration and test utilities if needed.
```kotlin
// build.gradle.kts (shared module)
dependencies {
// Core API (required)
commonMainApi("dev.icerock.moko:permissions:0.20.1")
// Optional permission modules — include only what your app needs
commonMainImplementation("dev.icerock.moko:permissions-bluetooth:0.20.1")
commonMainImplementation("dev.icerock.moko:permissions-camera:0.20.1")
commonMainImplementation("dev.icerock.moko:permissions-contacts:0.20.1")
commonMainImplementation("dev.icerock.moko:permissions-gallery:0.20.1")
commonMainImplementation("dev.icerock.moko:permissions-location:0.20.1")
commonMainImplementation("dev.icerock.moko:permissions-microphone:0.20.1")
commonMainImplementation("dev.icerock.moko:permissions-motion:0.20.1")
commonMainImplementation("dev.icerock.moko:permissions-notifications:0.20.1")
commonMainImplementation("dev.icerock.moko:permissions-storage:0.20.1")
// Compose Multiplatform integration
commonMainApi("dev.icerock.moko:permissions-compose:0.20.1")
// Test utilities
commonTestImplementation("dev.icerock.moko:permissions-test:0.20.1")
}
```
--------------------------------
### Add moko-permissions to root build.gradle
Source: https://github.com/icerockdev/moko-permissions/blob/master/README.md
Configure the mavenCentral() repository in your root build.gradle file to download moko-permissions artifacts.
```groovy
allprojects {
repositories {
mavenCentral()
}
}
```
--------------------------------
### providePermission(permission)
Source: https://context7.com/icerockdev/moko-permissions/llms.txt
Suspends until the user grants or denies the permission. If already granted, returns immediately. Throws `DeniedException`, `DeniedAlwaysException`, or `RequestCanceledException` on failure.
```APIDOC
## providePermission(permission) — Request a Permission
Suspends until the user grants or denies the permission. If already granted, returns immediately. Throws `DeniedException`, `DeniedAlwaysException`, or `RequestCanceledException` on failure.
```kotlin
// Common code
suspend fun ensureMicrophoneAccess(controller: PermissionsController) {
try {
controller.providePermission(Permission.RECORD_AUDIO)
println("Microphone granted — start recording")
} catch (e: DeniedAlwaysException) {
println("Microphone permanently denied: ${e.permission}")
controller.openAppSettings()
} catch (e: DeniedException) {
println("Microphone denied: ${e.permission} — show explanation")
}
}
```
```
--------------------------------
### Add moko-permissions dependencies to project build.gradle
Source: https://github.com/icerockdev/moko-permissions/blob/master/README.md
Include the moko-permissions library and its specific permission modules in your project's build.gradle file. The compose module provides extensions for Jetpack Compose.
```groovy
dependencies {
commonMainApi("dev.icerock.moko:permissions:0.20.1")
// specific permissions support
commonMainImplementation("dev.icerock.moko:permissions-bluetooth:0.20.1")
commonMainImplementation("dev.icerock.moko:permissions-camera:0.20.1")
commonMainImplementation("dev.icerock.moko:permissions-contacts:0.20.1")
commonMainImplementation("dev.icerock.moko:permissions-gallery:0.20.1")
commonMainImplementation("dev.icerock.moko:permissions-location:0.20.1")
commonMainImplementation("dev.icerock.moko:permissions-microphone:0.20.1")
commonMainImplementation("dev.icerock.moko:permissions-motion:0.20.1")
commonMainImplementation("dev.icerock.moko:permissions-notifications:0.20.1")
commonMainImplementation("dev.icerock.moko:permissions-storage:0.20.1")
// compose multiplatform
commonMainApi("dev.icerock.moko:permissions-compose:0.20.1") // permissions api + compose extensions
commonTestImplementation("dev.icerock.moko:permissions-test:0.20.1")
}
```
--------------------------------
### Binding Permissions Controller to Lifecycle (Compose)
Source: https://github.com/icerockdev/moko-permissions/blob/master/README.md
For Compose applications, bind the permissions controller to the `LocalLifecycleOwner`'s lifecycle using `BindEffect`.
```kotlin
@Composable
fun TestScreen() {
val viewModel = getViewModel {
// Pass the platform implementation of the permission controller to a common code.
ViewModel(PermissionsController())
}
// Binds the permissions controller to the LocalLifecycleOwner lifecycle.
BindEffect(viewModel.permissionsController)
}
```
--------------------------------
### Requesting Permissions in ViewModel (Common Kotlin)
Source: https://github.com/icerockdev/moko-permissions/blob/master/README.md
Use this common code within your ViewModel to request permissions. It handles success and various denial scenarios.
```kotlin
class ViewModel(val permissionsController: PermissionsController): ViewModel() {
fun onPhotoPressed() {
viewModelScope.launch {
try {
permissionsController.providePermission(Permission.GALLERY)
// Permission has been granted successfully.
} catch(deniedAlways: DeniedAlwaysException) {
// Permission is always denied.
} catch(denied: DeniedException) {
// Permission was denied.
}
}
}
}
```
--------------------------------
### Ensure Microphone Access with Moko Permissions
Source: https://context7.com/icerockdev/moko-permissions/llms.txt
This function checks if microphone access is granted and requests it if not. It handles permanent denial by opening app settings and temporary denial by showing an explanation.
```kotlin
// Common code
suspend fun ensureMicrophoneAccess(controller: PermissionsController) {
try {
controller.providePermission(Permission.RECORD_AUDIO)
println("Microphone granted — start recording")
} catch (e: DeniedAlwaysException) {
println("Microphone permanently denied: ${e.permission}")
controller.openAppSettings()
} catch (e: DeniedException) {
println("Microphone denied: ${e.permission} — show explanation")
}
}
```
--------------------------------
### PermissionsController Interface
Source: https://context7.com/icerockdev/moko-permissions/llms.txt
The central interface for all permission operations, implemented natively per platform and consumed from common Kotlin code.
```APIDOC
## PermissionsController Interface
The central interface for all permission operations. Implemented natively per platform; consumed from common Kotlin code. On Android an instance must be bound to a `ComponentActivity`; on iOS no binding is needed.
```kotlin
// --- Common code (shared module) ---
import dev.icerock.moko.permissions.DeniedAlwaysException
import dev.icerock.moko.permissions.DeniedException
import dev.icerock.moko.permissions.Permission
import dev.icerock.moko.permissions.PermissionsController
import dev.icerock.moko.permissions.RequestCanceledException
import dev.icerock.moko.permissions.camera.CAMERA
import dev.icerock.moko.permissions.location.LOCATION
import dev.icerock.moko.permissions.microphone.RECORD_AUDIO
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.launch
class MediaViewModel(
val permissionsController: PermissionsController,
private val scope: CoroutineScope
) {
fun requestCamera() {
scope.launch {
try {
permissionsController.providePermission(Permission.CAMERA)
// Camera access granted — proceed with feature
openCamera()
} catch (e: DeniedAlwaysException) {
// User permanently denied; prompt to open Settings
permissionsController.openAppSettings()
} catch (e: DeniedException) {
// User denied this time; can ask again later
showRationale()
} catch (e: RequestCanceledException) {
// Android-only: dialog was dismissed without a choice
}
}
}
fun requestLocation() {
scope.launch {
val granted = permissionsController.isPermissionGranted(Permission.LOCATION)
if (!granted) {
permissionsController.providePermission(Permission.LOCATION)
}
}
}
}
```
```
--------------------------------
### Binding Permissions Controller to Activity (Android)
Source: https://github.com/icerockdev/moko-permissions/blob/master/README.md
In Android, bind the permissions controller to the activity's lifecycle within `onCreate` for proper management.
```kotlin
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val viewModel = getViewModel {
// Pass the platform implementation of the permission controller to a common code.
ViewModel(PermissionsController())
}
// Binds the permissions controller to the activity lifecycle.
viewModel.permissionsController.bind(activity)
}
```
--------------------------------
### Compose Multiplatform with Moko MVVM
Source: https://github.com/icerockdev/moko-permissions/blob/master/README.md
Integrates Moko Permissions with Moko MVVM, ensuring correct configuration change handling on Android and binding the controller to the ViewModel.
```kotlin
@Composable
fun Sample() {
val factory: PermissionsControllerFactory = rememberPermissionsControllerFactory()
val viewModel: PermissionsViewModel = getViewModel(
key = "permissions-screen",
factory = viewModelFactory { PermissionsViewModel(factory.createPermissionsController()) }
)
BindEffect(viewModel.permissionsController)
Button(onClick = viewModel::onButtonClick) {
Text(text = "give permissions")
}
}
class PermissionsViewModel(
val permissionsController: PermissionsController
) : ViewModel() {
fun onButtonClick() {
viewModelScope.launch {
permissionsController.providePermission(Permission.REMOTE_NOTIFICATION)
}
}
}
```
--------------------------------
### Create PermissionsController with rememberPermissionsControllerFactory
Source: https://context7.com/icerockdev/moko-permissions/llms.txt
Use `rememberPermissionsControllerFactory` to create a `PermissionsController` within a Composable. This is recommended when the controller should survive configuration changes and is owned by a ViewModel.
```kotlin
import dev.icerock.moko.permissions.PermissionsController
import dev.icerock.moko.permissions.compose.BindEffect
import dev.icerock.moko.permissions.compose.PermissionsControllerFactory
import dev.icerock.moko.permissions.compose.rememberPermissionsControllerFactory
import dev.icerock.moko.permissions.gallery.GALLERY
// Shared Composable (works on Android + iOS)
@Composable
fun GalleryScreen() {
val factory: PermissionsControllerFactory = rememberPermissionsControllerFactory()
val controller: PermissionsController = remember(factory) {
factory.createPermissionsController()
}
val scope = rememberCoroutineScope()
BindEffect(controller) // Binds lifecycle on Android; no-op on iOS
Button(onClick = {
scope.launch {
try {
controller.providePermission(Permission.GALLERY)
openPhotoPicker()
} catch (e: DeniedAlwaysException) {
controller.openAppSettings()
} catch (e: DeniedException) {
// Retry later
}
}
}) {
Text("Pick Photo")
}
}
// With moko-mvvm (survives Android config changes):
@Composable
fun GalleryScreenWithViewModel() {
val factory: PermissionsControllerFactory = rememberPermissionsControllerFactory()
val viewModel: GalleryViewModel = getViewModel(
key = "gallery-screen",
factory = viewModelFactory { GalleryViewModel(factory.createPermissionsController()) }
)
BindEffect(viewModel.permissionsController)
Button(onClick = viewModel::onPickPhoto) { Text("Pick Photo") }
}
```
--------------------------------
### getPermissionState(permission)
Source: https://context7.com/icerockdev/moko-permissions/llms.txt
Retrieves the detailed current state of a permission. This is useful for conditional UI rendering without triggering a permission request.
```APIDOC
## `getPermissionState(permission)` — Inspect Detailed Permission State
### Description
Returns a `PermissionState` enum value describing the current state in detail. Useful for conditional UI rendering without triggering a request.
### Method
`getPermissionState`
### Parameters
#### Path Parameters
- **permission** (Permission) - Required - The permission to inspect.
### Response
#### Success Response (PermissionState)
- **PermissionState.NotDetermined**: The permission has never been asked for.
- **PermissionState.NotGranted**: The permission is not granted (on Android, this could be denied or not asked).
- **PermissionState.Granted**: The permission is granted, and the feature is available.
- **PermissionState.Denied**: The permission has been denied (on Android, a rationale might be shown before retrying).
- **PermissionState.DeniedAlways**: The permission has been permanently denied, and the user should be directed to Settings.
```
--------------------------------
### bind(activity)
Source: https://context7.com/icerockdev/moko-permissions/llms.txt
Binds the PermissionsController to a ComponentActivity on Android. This is essential for registering the ActivityResultLauncher and ensuring proper lifecycle cleanup for permission requests.
```APIDOC
## Android — `bind(activity)` — Lifecycle Binding
### Description
On Android, `PermissionsController` must be bound to a `ComponentActivity` before any permission can be requested. This registers the `ActivityResultLauncher` and attaches lifecycle cleanup.
### Method
`bind`
### Parameters
#### Path Parameters
- **activity** (ComponentActivity) - Required - The activity to bind the controller to.
```
--------------------------------
### Compose Lifecycle Binding
Source: https://context7.com/icerockdev/moko-permissions/llms.txt
In Jetpack Compose, use the `BindEffect(permissionsController)` composable. It automatically handles binding the controller to the `LocalLifecycleOwner` and re-binds on recomposition, simplifying lifecycle management.
```kotlin
import dev.icerock.moko.permissions.PermissionsController
import dev.icerock.moko.permissions.compose.BindEffect
import dev.icerock.moko.permissions.notifications.REMOTE_NOTIFICATION
@Composable
fun NotificationsScreen(viewModel: NotificationsViewModel) {
// Automatically binds on Android; no-op on iOS
BindEffect(viewModel.permissionsController)
val scope = rememberCoroutineScope()
Button(onClick = {
scope.launch {
try {
viewModel.permissionsController.providePermission(Permission.REMOTE_NOTIFICATION)
} catch (e: DeniedAlwaysException) {
viewModel.permissionsController.openAppSettings()
}
}
}) {
Text("Enable Notifications")
}
}
```
--------------------------------
### Evaluating Permission State
Source: https://context7.com/icerockdev/moko-permissions/llms.txt
Use `PermissionState` enum with `controller.getPermissionState()` to determine the current status of a permission. This is useful for conditionally updating the UI.
```kotlin
import dev.icerock.moko.permissions.PermissionState
// Evaluate permission state for conditional UI
suspend fun updatePermissionUI(
controller: PermissionsController,
permission: Permission
): String = when (controller.getPermissionState(permission)) {
PermissionState.NotDetermined -> "Not yet requested" // Both platforms
PermissionState.NotGranted -> "Not granted" // Android-only
PermissionState.Granted -> "Granted" // Both platforms
PermissionState.Denied -> "Denied (retry ok)" // Android-only
PermissionState.DeniedAlways -> "Permanently denied" // Android (all), iOS (push only)
}
```
--------------------------------
### Android Lifecycle Binding
Source: https://context7.com/icerockdev/moko-permissions/llms.txt
On Android, `PermissionsController` must be bound to a `ComponentActivity` using `bind(activity)`. This is essential for registering `ActivityResultLauncher` and ensuring proper lifecycle cleanup.
```kotlin
// Android Activity
import dev.icerock.moko.permissions.PermissionsController
class MainActivity : AppCompatActivity() {
private lateinit var viewModel: MyViewModel
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val controller = PermissionsController(applicationContext = applicationContext)
viewModel = ViewModelProvider(this)[MyViewModel::class.java].also { vm ->
vm.permissionsController = controller
}
// Must bind before any permission request fires
controller.bind(this)
}
}
```
--------------------------------
### BindEffect(permissionsController)
Source: https://context7.com/icerockdev/moko-permissions/llms.txt
A Composable function that automatically binds the PermissionsController to the current LifecycleOwner within a Compose UI. It handles binding on Android and is a no-op on iOS, simplifying lifecycle management in Compose applications.
```APIDOC
## Compose — `BindEffect(permissionsController)` — Compose Lifecycle Binding
### Description
In a Composable, use `BindEffect` instead of manually calling `bind()`. It automatically binds the controller to the current `LocalLifecycleOwner` and re-binds on recomposition.
### Method
`BindEffect`
### Parameters
#### Path Parameters
- **permissionsController** (PermissionsController) - Required - The controller to bind to the Compose lifecycle.
```
--------------------------------
### Check if Permission is Granted
Source: https://context7.com/icerockdev/moko-permissions/llms.txt
Use `isPermissionGranted` to check the status without requesting. It returns true if granted, false otherwise. Useful for conditional logic before performing actions.
```kotlin
suspend fun checkBeforeUpload(controller: PermissionsController) {
val hasStorage = controller.isPermissionGranted(Permission.STORAGE)
val hasCamera = controller.isPermissionGranted(Permission.CAMERA)
when {
hasStorage && hasCamera -> startUpload()
!hasCamera -> println("Camera permission missing")
!hasStorage -> println("Storage permission missing")
}
}
```
--------------------------------
### isPermissionGranted(permission)
Source: https://context7.com/icerockdev/moko-permissions/llms.txt
Checks if a specific permission is currently granted without prompting the user. Returns true if granted, false otherwise.
```APIDOC
## `isPermissionGranted(permission)` — Check Permission Without Requesting
### Description
Returns `true` if the permission is currently granted, `false` otherwise. Does not show any system dialog.
### Method
`isPermissionGranted`
### Parameters
#### Path Parameters
- **permission** (Permission) - Required - The permission to check.
### Response
#### Success Response (Boolean)
- **true**: The permission is granted.
- **false**: The permission is not granted.
```
=== COMPLETE CONTENT === This response contains all available snippets from this library. No additional content exists. Do not make further requests.