Production-ready, type-safe permission handling for Kotlin Multiplatform β handling the complex edge cases of Android and iOS flows.
Grant is not just another permission library. It is a production-hardened engine designed to handle complex edge cases that lead to crashes and hangs in other solutions. Built for professionals who demand absolute reliability.
Explore Documentation β’ Quick Start β’ Why Grant? β’ Demo App
- π― Pure Logic-First API β Works anywhere: ViewModels, Repositories, or Composables. No Activity or Fragment references required.
- π iOS Framework Isolation β Each permission type is isolated to its own handler, preventing unused Apple frameworks (Location, Bluetooth, Motion, etc.) from being linked into your binary. No more phantom
NSUsageDescriptionrequirements. - π‘οΈ iOS Crash-Guard β Automatically validates
Info.plistkeys before requesting, preventing the dreadedSIGABRTproduction crashes. - π Android Process-Death Resilience β The only library that handles system-initiated process death gracefully with zero timeouts.
- β‘ iOS Deadlock Fix β Built-in protection against the infamous Camera/Microphone first-request deadlock.
- π¦ 17 Native Permissions β Deep, native integration for Camera, Gallery (Partial access!), Location, Bluetooth, Motion, Health, and more.
- π οΈ Service Intelligence β Don't just check permissions; check if services (GPS, Bluetooth, Health) are actually enabled.
- π§© Custom Extensibility β Use
RawPermissionto support new OS permissions (Android 15+, iOS 18+) instantly without library updates. - π§ͺ Ultra-Robust Testing β 782 automated tests (423 Android + 359 iOS Simulator) covering every platform edge case, state invariant, and UI interaction. 100% pass rate.
class CameraViewModel(private val grantManager: GrantManager) : ViewModel() {
val cameraGrant = GrantHandler(
grantManager = grantManager,
grant = AppGrant.CAMERA,
scope = viewModelScope
)
fun capturePhoto() {
cameraGrant.request {
// Only runs when permission is FULLY granted
cameraEngine.startCapture()
}
}
}@Composable
fun CameraScreen(viewModel: CameraViewModel) {
// Handles Rationale, Denied, and Settings dialogs automatically
GrantDialog(handler = viewModel.cameraGrant)
IconButton(onClick = { viewModel.capturePhoto() }) {
Icon(Icons.Default.Camera, contentDescription = "Capture")
}
}Permission is only half the battle. In production, you also need to check if the hardware service (GPS, Bluetooth) is actually enabled.
// Use GrantAndServiceChecker to combine both worlds
class LocationViewModel(
private val checker: GrantAndServiceChecker,
private val grantManager: GrantManager
) : ViewModel() {
fun startTracking() {
viewModelScope.launch {
when (val status = checker.checkLocationReady()) {
LocationReadyStatus.Ready -> sensor.start()
LocationReadyStatus.ServiceDisabled -> _uiState.showEnableGPS()
LocationReadyStatus.GrantDenied -> requestPermission()
LocationReadyStatus.BothRequired -> _uiState.showTotalFailure()
}
}
}
}Most KMP permission libraries are simple wrappers around native APIs. Grant is an Architectural Solution.
| Feature | Grant | moko-permissions | accompanist-permissions |
|---|---|---|---|
| No Lifecycle Binding | β | β (needs BindEffect) | β (needs Activity) |
| ViewModel Support | Full | Partial | β |
| iOS Crash Prevention | β | β | β |
| iOS Framework Isolation | β | β | N/A |
| Android Deadlock Fix | β | β | β |
| Process Death Recovery | Native | β | Manual |
| Service Checks (GPS/BT/Health) | β | β | β |
| Android 14 Partial Access | β | Partial | β |
| Custom Permissions | β | Limited | Limited |
| Permission | Android | iOS | Notes |
|---|---|---|---|
| Camera | β | β | iOS main-thread safe + deadlock fix |
| Microphone | β | β | Shares AVFoundation handler with Camera |
| Gallery (full) | β | β | Android 14+ partial access (PARTIAL_GRANTED) |
| Gallery (images only) | β | β | AppGrant.GALLERY_IMAGES_ONLY |
| Gallery (video only) | β | β | AppGrant.GALLERY_VIDEO_ONLY |
| Storage (legacy) | β | β | Pre-API 33 fallback |
| Location (when in use) | β | β | Intelligent GPS service check included |
| Location (always) | β | β | Android 2-step background flow handled |
| Notifications | β | β | Android 13+ and legacy flows |
| Bluetooth | β | β | Service status check + Scan/Connect |
| Bluetooth Advertise | β | β | AppGrant.BLUETOOTH_ADVERTISE |
| Contacts (full) | β | β | Read + Write access |
| Contacts (read-only) | β | β | AppGrant.READ_CONTACTS |
| Calendar (full) | β | β | iOS 17+ FullAccess / WriteOnly mapped correctly |
| Calendar (read-only) | β | β | AppGrant.READ_CALENDAR |
| Motion / Activity | β | β | Simulator-aware (safe mock on Simulator) |
| Schedule Exact Alarm | β | β | Android 12+ SCHEDULE_EXACT_ALARM |
| Service | Android | iOS |
|---|---|---|
| GPS / Location | β | β |
| Bluetooth | β | β |
| Wi-Fi | β | β |
| NFC | β | β |
| Camera hardware | β | β |
| Health Connect / HealthKit | β | β |
// shared/build.gradle.kts
kotlin {
sourceSets {
commonMain.dependencies {
implementation("dev.brewkits:grant-core:1.3.1")
implementation("dev.brewkits:grant-compose:1.3.1") // Optional: Compose dialogs
implementation("dev.brewkits:grant-core-koin:1.3.1") // Optional: Koin DI support
}
}
}Important
For projects targeting Web (JS) or Desktop (JVM), use an intermediate mobileMain source set to avoid linking iOS/Android dependencies on unsupported platforms. Read the Guide.
Note
Koin users: The Koin integration was moved to grant-core-koin in v1.3.1. Add the new artifact alongside grant-core and replace GrantPlatformModule imports. See the Migration Guide.
| Guide | Description |
|---|---|
| Architecture | How concurrency, state machines, and the mutex flow work |
| iOS Setup | Critical Info.plist configuration β read before shipping |
| Migration Guide | Upgrading from v1.2.x to v1.3.1 |
| Service Checking | Combining permission + hardware service checks |
| Manual Injection | Using Grant without any DI framework |
| Android Reliability | How we fix "Dead Clicks" on Android |
| Best Practices | Patterns for production apps |
We are on a mission to make permissions a "solved problem" for KMP. Join us!
- Check out CONTRIBUTING.md.
- Run
./gradlew :grant-core:allTeststo ensure stability. - Submit your PR.
Grant is licensed under the Apache License 2.0. See LICENSE for details.