Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions .github/workflows/ci-coverage.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,9 @@ name: "Coverage Report (KMP & iOS)"

on:
push:
branches: [ main, develop ]
branches: [ "**" ]
pull_request:
branches: [ main ]
branches: [ "**" ]

jobs:
coverage:
Expand Down
4 changes: 2 additions & 2 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,9 @@ name: CI

on:
push:
branches: [ "main", "feature/**" ]
branches: [ "**" ]
pull_request:
branches: [ "main" ]
branches: [ "**" ]
workflow_dispatch:

jobs:
Expand Down
4 changes: 2 additions & 2 deletions .github/workflows/code-quality.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,9 @@ name: Code Quality

on:
push:
branches: [ "main", "feature/**" ]
branches: [ "**" ]
pull_request:
branches: [ "main" ]
branches: [ "**" ]
workflow_dispatch:

jobs:
Expand Down
4 changes: 2 additions & 2 deletions .github/workflows/ios.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,9 @@ name: iOS Build

on:
push:
branches: [ "main" ]
branches: [ "**" ]
pull_request:
branches: [ "main" ]
branches: [ "**" ]
workflow_dispatch:

jobs:
Expand Down
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,12 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).

---

## [1.3.1] - 2026-05-05

### 🛡️ Critical Bug Fix: iOS Deadlock
- **iOS Mutex Re-entrancy Fix**: Resolved a critical deadlock in `PlatformGrantDelegate.ios.kt` where calling `request()` would hang indefinitely. This occurred because `requestInternal()` incorrectly called the public `checkStatus()` (which acquires a per-permission Mutex) while the same Mutex was already held by the parent `request()` call.
- **Regression Safety**: Added `Issue29IosMutexDeadlockTest` to ensure this class of deadlock is automatically detected in the future using `withTimeout` guards.

## [1.3.0] - 2026-04-29

### 🚀 Major Architectural Shift: Koin Decoupling
Expand Down
10 changes: 5 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -155,9 +155,9 @@ Most KMP permission libraries are simple wrappers around native APIs. Grant is a
kotlin {
sourceSets {
commonMain.dependencies {
implementation("dev.brewkits:grant-core:1.3.0")
implementation("dev.brewkits:grant-compose:1.3.0") // Optional: Compose dialogs
implementation("dev.brewkits:grant-core-koin:1.3.0") // Optional: Koin DI support
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
}
}
}
Expand All @@ -167,7 +167,7 @@ kotlin {
> 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.0. Add the new artifact alongside `grant-core` and replace `GrantPlatformModule` imports. See the [Migration Guide](docs/MIGRATION_GUIDE.md).
> **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).

---

Expand All @@ -177,7 +177,7 @@ kotlin {
| :--- | :--- |
| [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.0 |
| [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 |
Expand Down
27 changes: 26 additions & 1 deletion ROADMAP.md
Original file line number Diff line number Diff line change
@@ -1,11 +1,36 @@
# Grant Library — Roadmap

> Last updated: 2026-04-30 | Current stable: **v1.3.0**
> Last updated: 2026-05-05 | Current stable: **v1.3.1**

---

## 🛠️ In Progress / Upcoming

### v1.4.0 (Target: Q3 2026) — Enterprise Hardening & Extensibility
*Focus: Resilience against Process Death, Performance at Scale, and Architectural Purity.*

**1. Resilience (Android)**
- [ ] **Process Death Recovery**: Implement `SavedStateHandle` support in `GrantRequestActivity`. Ensure active permission requests survive when the OS kills the app in the background.
- [ ] **Activity Launch Guard**: Prevent multiple `GrantRequestActivity` instances from overlapping during rapid concurrent calls.

**2. Extensibility (iOS)**
- [ ] **Handler Registry**: Allow developers to register custom `IosPermissionHandler` implementations for `RawPermission`. No more "Not Implemented" dead-ends on iOS.
- [ ] **Modern iOS API Support**: Guidelines and helpers for `PHPicker` (no-permission photo selection) and `NSLocationTemporaryFullAccuracyUsageDescriptionKey`.

**3. Performance & UI**
- [ ] **Parallel Status Checks**: Refactor `GrantGroupHandler` to use `async/awaitAll` for status verification, eliminating "UI Jank" when checking 10+ permissions.
- [ ] **Emission Throttling**: Use `distinctUntilChanged` on internal flows to prevent redundant UI re-compositions.

**4. Core Architecture**
- [ ] **Robust Locking**: Replace the brittle `checkStatusInternal` pattern with a cleaner internal/external separation or a re-entrant safe locking strategy.
- [ ] **Atomic Store Operations**: Ensure `GrantStore` updates are atomic across all platforms to prevent race conditions during rapid state changes.

## ✅ Released

### v1.3.1 (2026-05-05)
- HOTFIX: iOS `request()` mutex deadlock resolution (Issue #29)
- Regression tests for non-reentrant mutex patterns

### v1.3.0 (2026-04-29)
- Koin decoupled into `grant-core-koin` module
- `GrantAndServiceHandler` for unified permission + service flows
Expand Down
4 changes: 2 additions & 2 deletions demo/iosApp/GrantDemo/GrantDemo.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -360,7 +360,7 @@
PRODUCT_NAME = "$(TARGET_NAME)";
REGISTER_APP_GROUPS = YES;
STRING_CATALOG_GENERATE_SYMBOLS = YES;
SUPPORTED_PLATFORMS = "iphoneos iphonesimulator macosx";
SUPPORTED_PLATFORMS = "iphoneos iphonesimulator";
SUPPORTS_MACCATALYST = NO;
SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO;
SUPPORTS_XR_DESIGNED_FOR_IPHONE_IPAD = NO;
Expand Down Expand Up @@ -423,7 +423,7 @@
PRODUCT_NAME = "$(TARGET_NAME)";
REGISTER_APP_GROUPS = YES;
STRING_CATALOG_GENERATE_SYMBOLS = YES;
SUPPORTED_PLATFORMS = "iphoneos iphonesimulator macosx";
SUPPORTED_PLATFORMS = "iphoneos iphonesimulator";
SUPPORTS_MACCATALYST = NO;
SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO;
SUPPORTS_XR_DESIGNED_FOR_IPHONE_IPAD = NO;
Expand Down
4 changes: 2 additions & 2 deletions docs/DEPENDENCY_MANAGEMENT.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ Grant library has the following dependencies:
- **AndroidX Activity Compose** `1.12.1` (Android only) - For permission requests

### Optional Dependencies
- **Koin** `4.1.1` - For dependency injection (OPTIONAL). Use `dev.brewkits:grant-core-koin:1.3.0`.
- **Koin** `4.1.1` - For dependency injection (OPTIONAL). Use `dev.brewkits:grant-core-koin:1.3.1`.

---

Expand All @@ -26,7 +26,7 @@ Grant library uses **Koin 4.1.1** for optional dependency injection support.

✅ **Koin is OPTIONAL** - You don't need Koin to use Grant library!

As of version **1.3.0**, Koin support has been moved to a separate module to ensure `grant-core` remains a pure, dependency-free artifact.
As of version **1.3.1**, Koin support has been moved to a separate module to ensure `grant-core` remains a pure, dependency-free artifact.

1. **Manual Creation (Recommended)** - Use `grant-core`
2. **Koin DI Modules** - Use `grant-core-koin`
Expand Down
14 changes: 7 additions & 7 deletions docs/MIGRATION_GUIDE.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# Migration Guide to Grant

**Version:** 1.3.0
**Version:** 1.3.1
**Last Updated:** April 29, 2026

This guide helps you migrate from previous versions of Grant or other permission libraries.
Expand All @@ -9,7 +9,7 @@ This guide helps you migrate from previous versions of Grant or other permission

## 📚 Table of Contents

1. [Upgrading from Grant 1.2.x to 1.3.0](#upgrading-from-grant-12x-to-130)
1. [Upgrading from Grant 1.2.x to 1.3.1](#upgrading-from-grant-12x-to-130)
2. [From moko-permissions](#from-moko-permissions)
3. [From Google Accompanist](#from-google-accompanist)
4. [From Custom Implementation](#from-custom-implementation)
Expand All @@ -19,11 +19,11 @@ This guide helps you migrate from previous versions of Grant or other permission

---

## 1️⃣ Upgrading from Grant 1.2.x to 1.3.0
## 1️⃣ Upgrading from Grant 1.2.x to 1.3.1

### Overview

Version 1.3.0 is a major release focusing on architectural purity and iOS stability. The biggest change is the extraction of Koin into its own module.
Version 1.3.1 is a major release focusing on architectural purity and iOS stability. The biggest change is the extraction of Koin into its own module.

### What Changed?

Expand All @@ -34,16 +34,16 @@ Version 1.3.0 is a major release focusing on architectural purity and iOS stabil
### Step-by-Step Upgrade

#### 1. Update Version
Update your `build.gradle.kts` to version `1.3.0`.
Update your `build.gradle.kts` to version `1.3.1`.

#### 2. Handle Koin (If you use it)
If you were using `grantModule` or `grantPlatformModule`, you must now add the `grant-core-koin` dependency:

```kotlin
// shared/build.gradle.kts
commonMain.dependencies {
implementation("dev.brewkits:grant-core:1.3.0")
implementation("dev.brewkits:grant-core-koin:1.3.0") // New module!
implementation("dev.brewkits:grant-core:1.3.1")
implementation("dev.brewkits:grant-core-koin:1.3.1") // New module!
}
```

Expand Down
4 changes: 2 additions & 2 deletions docs/grant-core/ARCHITECTURE.md
Original file line number Diff line number Diff line change
Expand Up @@ -53,9 +53,9 @@ This document explains the architectural decisions behind KMP Grant library and
└──────────────────┘ └──────────────────┘
```

## 🍎 iOS Framework Isolation (NEW in v1.3.0)
## 🍎 iOS Framework Isolation (NEW in v1.3.1)

To avoid Apple App Store rejections due to "unused sensitive permissions" (e.g., your app doesn't use Location but the library contains Location code), Grant v1.3.0 introduced a strict isolation architecture.
To avoid Apple App Store rejections due to "unused sensitive permissions" (e.g., your app doesn't use Location but the library contains Location code), Grant v1.3.1 introduced a strict isolation architecture.

### The Problem
Kotlin/Native's dead code elimination (DCE) sometimes fails to remove framework linkages if they are referenced in a large, monolithic platform delegate. This causes the App Store static scanner to detect frameworks like `CoreLocation` or `CoreBluetooth` even if your app never requests them.
Expand Down
4 changes: 2 additions & 2 deletions docs/ios/APPLE_FRAMEWORK_LINKING_ISSUE.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

> **Related:** Issue #25 — *"Apple wants me to add why I have used them, but I have not used them"*
> **Severity:** 🔴 Critical (causes App Store rejection)
> **Status:** ✅ RESOLVED in v1.3.0
> **Status:** ✅ RESOLVED in v1.3.1

---

Expand Down Expand Up @@ -87,7 +87,7 @@ Grant currently bundles **all 17 permissions** into a single `grant-core` module
```
User only needs CAMERA + MICROPHONE
Add dependency: implementation("dev.brewkits:grant-core:1.3.0")
Add dependency: implementation("dev.brewkits:grant-core:1.3.1")
Binary ships with: CoreLocation + EventKit + CoreMotion + CoreBluetooth + ...
Expand Down
6 changes: 3 additions & 3 deletions docs/platform-specific/ios/info-plist.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,11 @@

iOS requires you to declare **usage descriptions** in `Info.plist` for each permission before requesting it. **If you forget to add these keys, your app will crash immediately** when requesting the permission.

### 🍎 v1.3.0 Architectural Improvement (Framework Isolation)
### 🍎 v1.3.1 Architectural Improvement (Framework Isolation)

Prior to version 1.3.0, Apple's static analysis would often require **all** usage description keys (Location, Bluetooth, Calendar, etc.) even if your app didn't use them, because the library linked those frameworks statically.
Prior to version 1.3.1, Apple's static analysis would often require **all** usage description keys (Location, Bluetooth, Calendar, etc.) even if your app didn't use them, because the library linked those frameworks statically.

**Starting with v1.3.0, Grant uses a "Handler Pattern" to isolate framework imports.** This means:
**Starting with v1.3.1, Grant uses a "Handler Pattern" to isolate framework imports.** This means:
1. **Only requested permissions are linked**: If you don't use Location in your code, the `CoreLocation` framework will not be linked to your binary.
2. **No more "Dummy" keys**: You no longer need to add usage descriptions for permissions your app doesn't use.
3. **App Store Connect safety**: Apple's scanners will only flag frameworks that are actually referenced in your binary.
Expand Down
4 changes: 2 additions & 2 deletions grant-compose/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ plugins {
}

group = "dev.brewkits"
version = "1.3.0"
version = "1.3.1"

kotlin {
androidTarget {
Expand Down Expand Up @@ -105,7 +105,7 @@ publishing {
publications.configureEach {
(this as? MavenPublication)?.let {
groupId = "dev.brewkits"
version = "1.3.0"
version = "1.3.1"

pom {
name.set("KMP Grant Compose")
Expand Down
2 changes: 1 addition & 1 deletion grant-core-koin/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ plugins {
}

group = "dev.brewkits"
version = "1.3.0"
version = "1.3.1"

kotlin {
androidTarget {
Expand Down
4 changes: 2 additions & 2 deletions grant-core/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ plugins {
}

group = "dev.brewkits"
version = "1.3.0"
version = "1.3.1"

kotlin {
androidTarget {
Expand Down Expand Up @@ -129,7 +129,7 @@ publishing {
publications.configureEach {
(this as? MavenPublication)?.let {
groupId = "dev.brewkits"
version = "1.3.0"
version = "1.3.1"

pom {
name.set("KMP Grant")
Expand Down
Loading
Loading