Stop Fighting with Android Manifests and Info.plist. Request permissions in shared code like a boss.
After solving the UI Command problem, I ran into the next biggest wall in Kotlin Multiplatform (KMP) development: Runtime Permissions.
The Current Chaos
Android: You need ActivityResultLauncher, ContextCompat.checkSelfPermission, manifest entries, and complex logic for shouldShowRequestPermissionRationale.
iOS: You need AVCaptureDevice.requestAccess, PHPhotoLibrary, delegates, and async completion blocks.
The Shared Problem: How do you unite these two chaotic worlds into a clean, linear flow in your ViewModel?
Enter Grant.
🎯 What is Grant?
Grant is a permission manager built specifically for Kotlin Multiplatform, designed with a "Headless & Coroutines-First" philosophy.
- ❌ It is NOT a UI library (it won't force ugly popups on you)
- ✅ It is a System Engine that abstracts the operating system, giving you full control over the UI/UX while it handles the dirty work
🏆 Why Grant is the "De-Facto Standard" for 2026
1️⃣ Unified API (One Enum to Rule Them All)
Forget about checking Android SDK versions or iOS authorization statuses manually. Grant abstracts everything into a simple, type-safe Enum.
You don't need to know the difference between Android API 33 and iOS 16 under the hood. Grant handles it.
2️⃣ Linear Logic (Suspend Functions)
Permissions are inherently transactional: Ask → Wait → Result.
Instead of callbacks (which lead to "Callback Hell") or complex Delegates, Grant uses suspend functions natively. Your logic runs top-to-bottom, making it incredibly readable and maintainable.
3️⃣ Smart Status Handling
The hardest part of permissions isn't asking; it's handling when the user says "No, and don't ask again."
- Android: The system dialog stops appearing
-
iOS: The status becomes
.denied
Grant detects this state automatically across platforms, allowing you to guide the user to System Settings instantly.
🛠️ The "Clean" Architecture Way
Let's look at the difference between the traditional way and the Grant way.
❌ The Old Way (The Pain)
You have to inject an Activity into your ViewModel (causing leaks), or create complex interfaces in commonMain and implement them separately for each platform. Your code becomes fragmented and hard to test.
✅ The Grant Way (The Solution)
You handle permissions directly in your Shared Code (ViewModel/UseCase), with zero Android Context or iOS Delegates required.
class CameraViewModel : ViewModel() {
fun onCaptureClicked() {
viewModelScope.launch {
// 1. Check current status
val status = Grant.check(Permission.CAMERA)
when (status) {
PermissionStatus.GRANTED -> openCamera()
PermissionStatus.DENIED -> {
// 2. Request (Suspends until user decides)
val result = Grant.request(Permission.CAMERA)
if (result.isGranted) {
openCamera()
} else {
showErrorUI("We need camera access to take photos.")
}
}
PermissionStatus.PERMANENTLY_DENIED -> {
// 3. Handle "Don't ask again" scenario
showSettingsDialog()
}
}
}
}
}
🚀 The Killer Feature: Handling "Permanently Denied"
A major UX pain point occurs when a user accidentally clicks "Don't ask again" (Android) or denies permission (iOS). Subsequent requests fail silently, making your app look broken.
Grant solves this with a single line of code:
if (Grant.status(Permission.CAMERA) == PermissionStatus.PERMANENTLY_DENIED) {
// Opens the specific System Settings page for your app
Grant.openSettings()
}
This is the polish of a library built by product veterans: It solves the UX problem, not just the technical one.
📊 Technical Face-Off: Grant vs. The World
Let's break down exactly why Grant outperforms traditional methods (like Moko-Permissions or Accompanist):
1. Configuration & Setup
| Approach | Experience |
|---|---|
| Traditional | ❌ High friction. Often requires binding to an Activity or wrapping your Compose UI in a special Provider. |
| Grant | ✅ Zero-Config. Just add the dependency. It automatically hooks into the lifecycle context-aware components. |
2. Control Flow
| Approach | Experience |
|---|---|
| Traditional | ❌ Relies on Callbacks or reactive Streams/Flows, which can complicate simple transactional logic. |
| Grant | ✅ Uses Suspend Functions. This keeps your business logic linear and easy to reason about. |
3. Architecture Compatibility
| Approach | Experience |
|---|---|
| Traditional | ❌ Tightly coupled to the UI Layer. Hard to use inside a ViewModel or Domain UseCase. |
| Grant | ✅ Headless. It works perfectly inside ViewModels, keeping your UI code dumb and your logic code smart. |
4. Boilerplate
| Approach | Experience |
|---|---|
| Traditional | ❌ Requires passing Context or Activity references manually. |
| Grant | ✅ Context-Aware. It handles the platform context injection under the hood, keeping your shared code pure. |
👨💻 Final Verdict
In modern software architecture, specifically KMP, we strive for "Platform Transparency." Your business logic shouldn't care if it's running on an iPhone 15 or a Pixel 9.
Grant is the perfect abstraction layer to achieve this transparency for permissions. It hides the ugly, fragmented platform APIs and gives you a clean, powerful, and safe toolset.
If you are looking for a "Complete & Elite" permission solution for your next KMP project, Grant is the way to go.
🔗 Links
👉 GitHub Repo: https://www.github.com/brewkits/Grant
Tags: #KotlinMultiplatform #KMP #AndroidDev #iOSDev #Permissions #CleanArchitecture
Top comments (0)