An open API service indexing awesome lists of open source software.

https://github.com/brewkits/Grant

Kotlin Multiplatform permission library for Android & iOS. No Fragment/Activity needed, ViewModel-first, Compose Multiplatform ready. Fixes Android dead clicks & iOS deadlocks.
https://github.com/brewkits/Grant

android android-14-permission bluetooth clean-architecture compose-multiplatform compose-multiplatform-permission coroutines gps ios ios-17-permission jetpack-compose kmp kotlin-multiplatform kotlin-multiplatform-permission moko-permissions-alternative mvvm nfc permission permissions runtime-permissions

Last synced: about 1 month ago
JSON representation

Kotlin Multiplatform permission library for Android & iOS. No Fragment/Activity needed, ViewModel-first, Compose Multiplatform ready. Fixes Android dead clicks & iOS deadlocks.

Awesome Lists containing this project

README

          

Grant Logo

# Grant: Robust Permission Management for KMP

**Production-ready, type-safe permission handling for Kotlin Multiplatform β€” handling the complex edge cases of Android and iOS flows.**

[![Maven Central](https://img.shields.io/maven-central/v/dev.brewkits/grant-core?color=7F52FF&label=Maven%20Central&style=for-the-badge)](https://central.sonatype.com/artifact/dev.brewkits/grant-core)
[![Kotlin](https://img.shields.io/badge/Kotlin-2.1.0-7F52FF?logo=kotlin&logoColor=white&style=for-the-badge)](https://kotlinlang.org)
[![Platform](https://img.shields.io/badge/Platform-Android%20%7C%20iOS-lightgrey?style=for-the-badge)](https://kotlinlang.org/docs/multiplatform.html)
[![License](https://img.shields.io/badge/License-Apache%202.0-blue?style=for-the-badge)](LICENSE)

---

### ⚑ Zero Boilerplate. Zero Lifecycle Binding. Zero Headache.

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**](docs/README.md) β€’ [**Quick Start**](#-quick-start) β€’ [**Why Grant?**](#-why-grant) β€’ [**Demo App**](#-demo)

---

## πŸš€ Killer Features

- **🎯 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 `NSUsageDescription` requirements.
- **πŸ›‘οΈ iOS Crash-Guard** β€” Automatically validates `Info.plist` keys before requesting, preventing the dreaded `SIGABRT` production 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 `RawPermission` to 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.

---

## πŸ’Ž The "Grant" Experience

### 1️⃣ Define your logic (Logic Layer)
```kotlin
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()
}
}
}
```

### 2️⃣ Drop in the UI (Presentation Layer)
```kotlin
@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")
}
}
```

### πŸ† Best Practice: The Full Readiness Check (Logic + Hardware)
Permission is only half the battle. In production, you also need to check if the hardware service (GPS, Bluetooth) is actually enabled.

```kotlin
// 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()
}
}
}
}
```

---

## βš”οΈ Why Grant?

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 |

---

## πŸ—ΊοΈ Platform Support & Coverage

| 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 Checks (`ServiceType`)

| Service | Android | iOS |
| :--- | :---: | :---: |
| **GPS / Location** | βœ… | βœ… |
| **Bluetooth** | βœ… | βœ… |
| **Wi-Fi** | βœ… | βœ… |
| **NFC** | βœ… | β€” |
| **Camera hardware** | βœ… | βœ… |
| **Health Connect / HealthKit** | βœ… | βœ… |

---

## πŸ“¦ Installation

```kotlin
// 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](docs/DEPENDENCY_MANAGEMENT.md).

> [!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](docs/MIGRATION_GUIDE.md).

---

## πŸ“– Deep Dives

| Guide | Description |
| :--- | :--- |
| [Architecture](docs/grant-core/ARCHITECTURE.md) | How concurrency, state machines, and the mutex flow work |
| [iOS Setup](docs/platform-specific/ios/info-plist.md) | Critical `Info.plist` configuration β€” read before shipping |
| [Migration Guide](docs/MIGRATION_GUIDE.md) | Upgrading from v1.2.x to v1.3.1 |
| [Service Checking](docs/grant-core/SERVICES.md) | Combining permission + hardware service checks |
| [Manual Injection](docs/MANUAL_INJECTION.md) | Using Grant without any DI framework |
| [Android Reliability](docs/FIX_DEAD_CLICK_ANDROID.md) | How we fix "Dead Clicks" on Android |
| [Best Practices](docs/BEST_PRACTICES.md) | Patterns for production apps |

---

## 🀝 Contributing

We are on a mission to make permissions a "solved problem" for KMP. Join us!

1. Check out [CONTRIBUTING.md](CONTRIBUTING.md).
2. Run `./gradlew :grant-core:allTests` to ensure stability.
3. Submit your PR.

---

## βš–οΈ License

Grant is licensed under the **Apache License 2.0**. See [LICENSE](LICENSE) for details.


Built with ❀️ by BrewKits