### Complete Info.plist Example for iOS Source: https://github.com/brewkits/grant/blob/main/docs/platform-specific/ios/info-plist.md This example shows a complete Info.plist file with descriptions for camera, microphone, photo library, location, contacts, Bluetooth, and motion usage. ```xml CFBundleDisplayName My Awesome App NSCameraUsageDescription Camera is needed to scan QR codes for secure login NSMicrophoneUsageDescription Microphone is needed to record audio for video messages NSPhotoLibraryUsageDescription Photo library access lets you select images for your profile NSLocationWhenInUseUsageDescription Location is needed to show nearby restaurants and stores NSContactsUsageDescription Access to contacts helps you find and invite friends NSBluetoothAlwaysUsageDescription Bluetooth is used to connect to your fitness tracker NSMotionUsageDescription Motion data is used to track your steps and activity level ``` -------------------------------- ### Build and Install Android Demo App Source: https://github.com/brewkits/grant/blob/main/docs/demo/DEMO_SETUP.md Use this command to build and install the Android demo application onto a connected device or emulator. ```bash ./gradlew :demo:installDebug ``` -------------------------------- ### Actionable Settings Guide for Camera Source: https://github.com/brewkits/grant/blob/main/docs/BEST_PRACTICES.md Illustrates an actionable, step-by-step settings message for enabling camera permission, guiding the user through the process. ```kotlin settingsMessage = "Camera access was denied. To enable:\n\n1. Tap 'Open Settings' below 2. Enable Camera permission 3. Return to app and try again" ``` -------------------------------- ### Install and Run Android Demo via Gradle Source: https://github.com/brewkits/grant/blob/main/docs/demo/DEMO_SETUP.md Use these Gradle commands to install the Android demo application and launch it. This is the primary method for running the demo on Android using the command line. ```bash # Option 1: Gradle ./gradlew :demo:installDebug adb shell am start -n dev.brewkits.grant.demo/.MainActivity ``` -------------------------------- ### Install Grant Core on Android Source: https://github.com/brewkits/grant/blob/main/docs/grant-core/QUICK_START.md Installs the Grant Core demo app on a connected Android device or emulator. Follow the on-screen prompts to test camera grant functionality. ```bash # Install on device/emulator ./gradlew :demo:installDebug # Test camera grant: # 1. Tap "Request Camera" # 2. Allow grant # 3. Should show "Granted" ``` -------------------------------- ### Test Case 1: First Request (Fresh Install) Source: https://github.com/brewkits/grant/blob/main/docs/FIX_DEAD_CLICK_ANDROID.md Verifies the expected behavior for the initial permission request on a fresh app install, including the system dialog and subsequent rationale dialog. ```text 1. Install app 2. Click "Request Camera" 3. EXPECTED: System dialog appears 4. Deny 5. EXPECTED: No dialog (isFirstRequest=true) 6. Click again 7. EXPECTED: Rationale dialog appears ✅ ``` -------------------------------- ### Grant Handler ViewModel Setup Source: https://github.com/brewkits/grant/blob/main/docs/grant-core/ARCHITECTURE.md Example of setting up a GrantHandler in a ViewModel. This requires the permission manager, the specific app grant, and the viewModelScope. ```kotlin // ViewModel (3 lines!) val cameraGrant = GrantHandler(manager, AppGrant.CAMERA, viewModelScope) ``` -------------------------------- ### Platform-Specific Settings Guide Source: https://github.com/brewkits/grant/blob/main/docs/BEST_PRACTICES.md Shows how to provide platform-specific instructions for enabling permissions in the device settings. ```kotlin settingsMessage = when (platform) { Platform.ANDROID -> "Enable in Settings > Apps > ${appName} > Permissions > Camera" Platform.IOS -> "Enable in Settings > ${appName} > Camera" } ``` -------------------------------- ### Launch Android Demo App Source: https://github.com/brewkits/grant/blob/main/docs/demo/DEMO_SETUP.md Use this command to launch the installed Android demo application via ADB. ```bash adb shell am start -n dev.brewkits.grant.demo/.MainActivity ``` -------------------------------- ### List Available Simulators Source: https://github.com/brewkits/grant/blob/main/docs/grant-core/QUICK_START_iOS.md Use this command to list all available simulators on your system. You can install more simulators via Xcode settings. ```bash # List available simulators xcrun simctl list devices # Install more simulators via Xcode: # Xcode > Settings > Platforms ``` -------------------------------- ### All Permission Types Test Example Source: https://github.com/brewkits/grant/blob/main/docs/TESTING_IMPROVEMENTS_SUMMARY.md Test file covering all permission types. ```kotlin package dev.brewkits.grant import kotlin.test.Test class AllPermissionTypesTest { @Test fun testAllPermissionTypes() { // Add assertions for all 14 permission types } } ``` -------------------------------- ### Setup Grant Koin Modules Source: https://github.com/brewkits/grant/blob/main/docs/DEPENDENCY_MANAGEMENT.md Configure Koin to include Grant's DI modules. This should be done during your Koin setup. ```kotlin startKoin { modules( grantModule, // Common Grant DI grantPlatformModule // Platform-specific (Android/iOS) ) } ``` -------------------------------- ### iOS Basic Test Example Source: https://github.com/brewkits/grant/blob/main/docs/TESTING_IMPROVEMENTS_SUMMARY.md This is a basic test file for iOS, part of the completed iOS test infrastructure. ```kotlin package dev.brewkits.grant import kotlin.test.Test import kotlin.test.assertTrue class IosBasicTest { @Test fun testExample() { assertTrue(true, "iOS basic test should pass") } } ``` -------------------------------- ### Initializing modules on iOS Source: https://github.com/brewkits/grant/blob/main/docs/ios/APPLE_FRAMEWORK_LINKING_ISSUE.md Shows how to initialize the Grant permission modules once at application start on iOS. Call `initialize()` for each module that has been added as a dependency. ```kotlin // iosMain — call once, e.g. in ApplicationDelegate GrantContacts.initialize() GrantCalendar.initialize() GrantMotion.initialize() ``` -------------------------------- ### Example Info.plist Permission Descriptions Source: https://github.com/brewkits/grant/blob/main/docs/ios/INFO_PLIST_LOCALIZATION.md Shows how permission descriptions are defined within the Info.plist file using XML keys and string values. ```xml NSCameraUsageDescription Camera is used to scan QR codes NSLocationWhenInUseUsageDescription Location is used to show nearby stores ``` -------------------------------- ### iOS Bluetooth Manager Delegate Example Source: https://github.com/brewkits/grant/blob/main/docs/demo/DEMO_SETUP.md An example of a custom delegate for handling iOS CoreBluetooth state changes and asynchronous grant requests. ```kotlin class BluetoothManagerDelegate { // Handles CoreBluetooth state changes // Async grant requests } ``` -------------------------------- ### Good Info.plist Descriptions Source: https://github.com/brewkits/grant/blob/main/docs/platform-specific/ios/info-plist.md Examples of specific and user-friendly usage descriptions for Info.plist keys, recommended for clarity and user trust. ```xml NSCameraUsageDescription Camera is needed to scan QR codes NSMicrophoneUsageDescription Microphone is needed to record voice messages ``` -------------------------------- ### Install Xcode Command Line Tools Source: https://github.com/brewkits/grant/blob/main/docs/grant-core/QUICK_START_iOS.md Run this command in the terminal to install the Xcode Command Line Tools if you encounter 'xcodebuild: command not found' errors. ```bash xcode-select --install ``` -------------------------------- ### Consumer build.gradle.kts example Source: https://github.com/brewkits/grant/blob/main/docs/ios/APPLE_FRAMEWORK_LINKING_ISSUE.md Illustrates how a consumer's `build.gradle.kts` file can manage dependencies to avoid unnecessary framework inclusion. Shows that `grant-contacts.klib` is not downloaded or compiled when not explicitly added. ```kotlin Consumer build.gradle.kts: implementation("dev.brewkits:grant-core:2.1.0") // grant-contacts NOT added ▼ Dependency resolution grant-core.klib downloaded ✅ grant-contacts.klib NOT downloaded — never compiled ▼ K/N ObjC interop ContactsPermissionHandler.kt NEVER processed "Contacts" framework string NEVER emitted into metadata ▼ Apple static analyzer No "Contacts" metadata found NSContactsUsageDescription NOT required ✅ ``` -------------------------------- ### Dependency Injection with Koin and Manual Setup Source: https://github.com/brewkits/grant/blob/main/grant-core/README.md Shows how to integrate GrantManager with dependency injection frameworks like Koin or through manual class construction. Requires context for Koin. ```kotlin // Koin val grantModule = module { single { GrantFactory.create(androidContext()) } } // Manual class MyRepository(private val grantManager: GrantManager) { suspend fun requestCamera() = grantManager.request(AppGrant.CAMERA) } ``` -------------------------------- ### Optimize Activity Launch Time (Kotlin) Source: https://github.com/brewkits/grant/blob/main/docs/grant-core/TRANSPARENT_ACTIVITY_GUIDE.md Implement minimal overhead in `onCreate` by avoiding layout inflation and view setup. Directly proceed to the grant request to improve launch speed. ```kotlin override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) // No layout inflation // No view setup // Direct to grant request requestGrants() } ``` ```kotlin override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.transparent) // ❌ Don't do this! setupViews() // ❌ Don't do this! requestGrants() } ``` -------------------------------- ### Performance Test Example Source: https://github.com/brewkits/grant/blob/main/docs/TESTING_IMPROVEMENTS_SUMMARY.md Performance test file for measuring request latency and memory efficiency. ```kotlin package dev.brewkits.grant.performance import kotlin.test.Test class PerformanceTest { @Test fun testRequestLatencyAndMemory() { // Add assertions for performance tests } } ``` -------------------------------- ### Abstraction with GrantManager (Good) Source: https://github.com/brewkits/grant/blob/main/docs/grant-core/ARCHITECTURE.md This example demonstrates abstracting platform dependencies by depending on a `GrantManager` interface, improving testability and platform independence. ```kotlin // ✅ GOOD: Depend on abstraction class CameraViewModel( private val grantManager: GrantManager // Platform-agnostic! ) { suspend fun requestCamera() { val status = grantManager.checkStatus(AppGrant.CAMERA) when (status) { GrantStatus.GRANTED -> openCamera() // ... } } } ``` -------------------------------- ### Android Koin Setup Source: https://github.com/brewkits/grant/blob/main/docs/grant-core/GRANTS.md Integrate Grant's Koin modules into your Android application's onCreate method. Ensure the DemoApplication class extends Application and calls startKoin with the appropriate modules. ```kotlin package dev.brewkits.grant.demo import android.app.Application import dev.brewkits.grant.di.grantModule import dev.brewkits.grant.di.grantPlatformModule import org.koin.android.ext.koin.androidContext import org.koin.core.context.startKoin class DemoApplication : Application() { override fun onCreate() { super.onCreate() startKoin { androidContext(this@DemoApplication) modules( grantModule, grantPlatformModule ) } } } ``` ```xml ``` -------------------------------- ### Setup iOS Simulator Run Configuration (External Tool) Source: https://github.com/brewkits/grant/blob/main/docs/grant-core/QUICK_START_iOS.md Configure Android Studio to run an iOS simulator using an external tool. This method allows for more granular control over the execution. ```bash /bin/bash ``` ```bash $ProjectFileDir$/setup-ios-app.sh ``` -------------------------------- ### Simplified ViewModel with GrantHandler (Good) Source: https://github.com/brewkits/grant/blob/main/docs/grant-core/ARCHITECTURE.md This example demonstrates using the `GrantHandler` composition pattern to significantly reduce boilerplate code in a ViewModel for handling permission requests. ```kotlin // ✅ GOOD: 3 lines! class CameraViewModel(manager: GrantManager) : ViewModel() { val cameraGrant = GrantHandler( manager, AppGrant.CAMERA, viewModelScope ) fun onCaptureClick() { cameraGrant.request { openCamera() } } } ``` -------------------------------- ### Gradle Installation (Kotlin DSL) Source: https://github.com/brewkits/grant/blob/main/grant-core/README.md Add the Grant Core dependency to your project using Gradle with Kotlin DSL. Ensure mavenCentral() is configured in your repositories. ```kotlin // settings.gradle.kts dependencyResolutionManagement { repositories { mavenCentral() } } // shared/build.gradle.kts kotlin { sourceSets { commonMain.dependencies { implementation("dev.brewkits.grant:grant-core:1.0.2") } } } ``` -------------------------------- ### Setup ViewModel with GrantHandler Source: https://github.com/brewkits/grant/blob/main/grant-compose/README.md Set up your ViewModel by initializing a GrantHandler for the desired permission. This handler manages the permission request lifecycle and state. The request block runs only when the permission is granted. ```kotlin import dev.brewkits.grant.* class CameraViewModel(grantManager: GrantManager) : ViewModel() { val cameraGrant = GrantHandler( grantManager = grantManager, grant = AppGrant.CAMERA, scope = viewModelScope ) fun onTakePhotoClick() { cameraGrant.request { // This runs ONLY when permission is granted openCamera() } } } ``` -------------------------------- ### Setup iOS Simulator Run Configuration (Shell Script) Source: https://github.com/brewkits/grant/blob/main/docs/grant-core/QUICK_START_iOS.md Configure Android Studio to run an iOS simulator using a shell script. This script automates the build and launch process. ```bash cd "$PROJECT_DIR" && ./setup-ios-app.sh ``` -------------------------------- ### Create Service Manager Source: https://github.com/brewkits/grant/blob/main/docs/getting-started/installation.md Instantiate a service manager to check hardware states like GPS or Bluetooth. This example requires a `Context` on Android. ```kotlin val serviceManager = ServiceFactory.createServiceManager(context) ``` -------------------------------- ### Check Specific UI States Source: https://github.com/brewkits/grant/blob/main/grant-compose/README.md Check specific states within the collected UI state object to conditionally display UI elements or perform actions. For example, check if a rationale should be shown or if a settings guide is needed. ```kotlin if (state.showRationale) { /* ... */ } if (state.showSettingsGuide) { /* ... */ } ``` -------------------------------- ### Error Handling Test Example Source: https://github.com/brewkits/grant/blob/main/docs/TESTING_IMPROVEMENTS_SUMMARY.md Example test file for error handling scenarios. ```kotlin package dev.brewkits.grant import kotlin.test.Test class ErrorHandlingTest { @Test fun testErrorPaths() { // Add assertions for error handling paths } } ``` -------------------------------- ### Compose Multiplatform Manual Instance Creation Source: https://github.com/brewkits/grant/blob/main/docs/DEPENDENCY_MANAGEMENT.md Create GrantManager and GrantHandler instances within a Compose Multiplatform Composable. This example shows how to manage the lifecycle of these instances using `remember`. ```kotlin @Composable fun App() { // Create once and remember val grantManager = remember { GrantFactory.create( context = LocalContext.current // Android // or null for iOS ) } val handler = remember { GrantHandler( grantManager = grantManager, grant = AppGrant.CAMERA, scope = rememberCoroutineScope() ) } // Use handler } ``` -------------------------------- ### Build iOS Framework for Demo Source: https://github.com/brewkits/grant/blob/main/docs/demo/DEMO_SETUP.md Build the iOS framework for the demo application using Gradle. This command is necessary before opening the project in Xcode. ```bash # Build framework ./gradlew :demo:linkDebugFrameworkIosSimulatorArm64 # Open Xcode open demo/iosApp/iosApp.xcodeproj # Build & Run on simulator ``` -------------------------------- ### iOS Location Manager Delegate Example Source: https://github.com/brewkits/grant/blob/main/docs/demo/DEMO_SETUP.md An example of a custom delegate for handling iOS CoreLocation callbacks. It converts native asynchronous callbacks into Kotlin coroutines and ensures main thread safety. ```kotlin // Custom delegates for complex grants class LocationManagerDelegate { // Handles CoreLocation async callbacks // Converts to Kotlin coroutines // Main thread safety } ``` -------------------------------- ### Provide User Guidance for Service Issues Source: https://github.com/brewkits/grant/blob/main/docs/grant-core/SERVICES.md When a service is not ready, inform the user clearly and provide an actionable prompt to resolve the issue, such as opening service settings. ```kotlin when (checker.checkLocationReady()) { is LocationReadyStatus.ServiceDisabled -> { showDialog( title = "GPS is Off", message = "Please turn on GPS to use location features", action = "Enable GPS" ) { serviceManager.openServiceSettings(ServiceType.LOCATION_GPS) } } // ... } ``` -------------------------------- ### Validate Persistent Grant Store on App Start Source: https://github.com/brewkits/grant/blob/main/docs/architecture/grant-store.md If using a persistent grant store, implement validation logic to detect and correct potential desynchronization between the cached state and the actual grant status when the app starts. ```kotlin class ValidatedGrantStore(private val delegate: GrantStore) : GrantStore by delegate { suspend fun validateOnStart(grantManager: GrantManager) { AppGrant.entries.forEach { grant -> val cached = getStatus(grant) val actual = grantManager.checkStatus(grant) if (cached != actual) { // Desync detected! Update cache setStatus(grant, actual) } } } } ``` -------------------------------- ### Start Location Tracking with Service and Permission Checks Source: https://github.com/brewkits/grant/blob/main/docs/getting-started/quick-start.md This function checks if location services are enabled and if the necessary location permission is granted before starting location tracking. It handles opening settings if services or permissions are denied. ```kotlin import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.withContext suspend fun startLocationTracking(): Boolean { val serviceManager = ServiceFactory.create(context) // Step 1: Check service if (!serviceManager.isLocationEnabled()) { withContext(Dispatchers.Main) { showDialog("Please enable GPS in Settings") } serviceManager.openLocationSettings() return false } // Step 2: Check permission val status = grantManager.request(AppGrant.LOCATION) if (status != GrantStatus.GRANTED) { withContext(Dispatchers.Main) { if (status == GrantStatus.DENIED_ALWAYS) { showDialog("Location permission denied. Please enable in Settings") grantManager.openSettings() } else { showDialog("Location permission is required") } } return false } // All good! return true } ``` -------------------------------- ### Build and Run Demo App Source: https://github.com/brewkits/grant/blob/main/docs/grant-core/QUICK_START.md Build all modules and run the Android demo application. For iOS, open the Xcode project. ```bash # Build everything ./gradlew clean build # Run Android demo ./gradlew :demo:installDebug # Run iOS demo (requires Xcode) open demo/iosApp/iosApp.xcodeproj ``` -------------------------------- ### Expected SharedPreferences Content Source: https://github.com/brewkits/grant/blob/main/docs/FIX_DEAD_CLICK_ANDROID.md Example of the XML content expected in SharedPreferences after a permission has been requested. ```xml ``` -------------------------------- ### Bad Info.plist Descriptions Source: https://github.com/brewkits/grant/blob/main/docs/platform-specific/ios/info-plist.md Examples of generic and unhelpful usage descriptions for Info.plist keys, which are not recommended. ```xml NSCameraUsageDescription This app needs access to function NSMicrophoneUsageDescription This app needs access to function ``` -------------------------------- ### Setup Media Picker Screen with Fallback Source: https://github.com/brewkits/grant/blob/main/docs/recipes/photo-picker-fallback.md Sets up the Photo Picker launcher and a function to either launch the picker on Android 13+ or fall back to requesting gallery permissions for older versions. Requires `GrantHandler` for the fallback. ```kotlin import androidx.activity.compose.rememberLauncherForActivityResult import androidx.activity.result.contract.ActivityResultContracts import androidx.compose.runtime.Composable import androidx.compose.ui.platform.LocalContext import dev.brewkits.grant.GrantHandler @Composable fun MediaPickerScreen(galleryGrantHandler: GrantHandler) { // 1. Setup the Photo Picker launcher val photoPickerLauncher = rememberLauncherForActivityResult( contract = ActivityResultContracts.PickVisualMedia() ) { uri -> if (uri != null) { // User selected an image processImage(uri) } } // 2. Function to launch the picker or fallback to permissions fun pickImage() { if (ActivityResultContracts.PickVisualMedia.isPhotoPickerAvailable()) { // Android 13+ (or backported via Google Play Services) // No permissions needed! photoPickerLauncher.launch( PickVisualMediaRequest(ActivityResultContracts.PickVisualMedia.ImageOnly) ) } else { // Fallback for older Android versions galleryGrantHandler.requestSuspend( rationaleMessage = "We need gallery access to let you pick a photo." ) { status -> if (status == GrantStatus.GRANTED) { // Launch traditional ACTION_GET_CONTENT or custom gallery UI launchLegacyImagePicker() } } } } // ... UI ... } ``` -------------------------------- ### Regression Test Example Source: https://github.com/brewkits/grant/blob/main/docs/TESTING_IMPROVEMENTS_SUMMARY.md Regression test file created to prevent previously fixed bugs from reappearing. ```kotlin package dev.brewkits.grant.regression import kotlin.test.Test class RegressionTest { @Test fun testBugPrevention() { // Add assertions for regression tests } } ``` -------------------------------- ### Boot Simulator and Open App Manually Source: https://github.com/brewkits/grant/blob/main/docs/ios/QUICK_START_IOS_ANDROID_STUDIO.md If the app doesn't launch, use these bash commands to manually boot a specific simulator and open the Simulator application. ```bash xcrun simctl boot "iPhone 16" open -a Simulator ``` -------------------------------- ### Group Handler Stress Test Example Source: https://github.com/brewkits/grant/blob/main/docs/TESTING_IMPROVEMENTS_SUMMARY.md Stress test file for the GroupHandler, simulating high-volume operations. ```kotlin package dev.brewkits.grant.performance import kotlin.test.Test class GroupHandlerStressTest { @Test fun testHighVolumeOperations() { // Add assertions for stress tests } } ``` -------------------------------- ### Grant Group Handler Integration Test Example Source: https://github.com/brewkits/grant/blob/main/docs/TESTING_IMPROVEMENTS_SUMMARY.md Integration test for the GrantGroupHandler, focusing on multi-permission flows. ```kotlin package dev.brewkits.grant.integration import kotlin.test.Test class GrantGroupHandlerIntegrationTest { @Test fun testMultiPermissionFlows() { // Add assertions for GrantGroupHandler integration tests } } ``` -------------------------------- ### Grant Handler Integration Test Example Source: https://github.com/brewkits/grant/blob/main/docs/TESTING_IMPROVEMENTS_SUMMARY.md Integration test for the GrantHandler, covering complete user flows. ```kotlin package dev.brewkits.grant.integration import kotlin.test.Test class GrantHandlerIntegrationTest { @Test fun testCompleteUserFlows() { // Add assertions for GrantHandler integration tests } } ``` -------------------------------- ### Inject GrantAndServiceChecker with Koin Source: https://github.com/brewkits/grant/blob/main/docs/grant-core/SERVICES.md Example of injecting the GrantAndServiceChecker into a ViewModel using Koin's automatic dependency injection. ```kotlin class MyViewModel( private val checker: GrantAndServiceChecker // Auto-injected ) : ViewModel() ``` -------------------------------- ### Open Demo App on Android Source: https://github.com/brewkits/grant/blob/main/docs/demo/DEMO_GUIDE.md Use this ADB command to open the Grant Demo app's main activity on an Android device. ```bash adb shell am start -n dev.brewkits.grant.demo/dev.brewkits.grant.demo.MainActivity ``` -------------------------------- ### Saved State Delegate Integration Test Example Source: https://github.com/brewkits/grant/blob/main/docs/TESTING_IMPROVEMENTS_SUMMARY.md Integration test for SavedStateDelegate, validating process death recovery. ```kotlin package dev.brewkits.grant.integration import kotlin.test.Test class SavedStateDelegateIntegrationTest { @Test fun testProcessDeathRecovery() { // Add assertions for SavedStateDelegate integration tests } } ``` -------------------------------- ### Available iOS App Configurations Source: https://github.com/brewkits/grant/blob/main/docs/ios/QUICK_START_IOS_ANDROID_STUDIO.md View the different configurations available for running the iOS app, including specific simulators and building the framework. ```text 📱 iosApp → iPhone 16 (default) 📱 iosApp (iPhone 16 Pro) → iPhone 16 Pro 📱 iosApp (iPad Pro) → iPad Pro 11-inch 🔧 Build iOS Framework Only → Build framework only ``` -------------------------------- ### Boot an iOS Simulator Source: https://github.com/brewkits/grant/blob/main/docs/ios/IOS_SETUP_ANDROID_STUDIO.md Boots a specific iOS simulator before it can be used. Replace 'iPhone 16' with the desired simulator name. ```bash xcrun simctl boot "iPhone 16" ``` -------------------------------- ### Grant Manager Integration Source: https://github.com/brewkits/grant/blob/main/demo/README.md Initialize the GrantManager and GrantDemoViewModel in your Android application's setup, typically in the MainActivity or AppModule. ```kotlin // In your app setup (MainActivity or AppModule) val grantManager = get() val viewModel = GrantDemoViewModel( grantManager = grantManager, scope = viewModelScope // or rememberCoroutineScope() ) // In your Compose UI GrantDemoScreen(viewModel = viewModel) ``` -------------------------------- ### List Available iOS Simulators Source: https://github.com/brewkits/grant/blob/main/docs/ios/IOS_SETUP_ANDROID_STUDIO.md Executes a helper script to display a list of available iOS simulators on the system. ```bash ./list-ios-simulators.sh ``` -------------------------------- ### Documentation as Tests with Annotations Source: https://github.com/brewkits/grant/blob/main/docs/TESTING_IMPROVEMENTS_SUMMARY.md Shows how tests can serve as living documentation. Use descriptive test names to clarify behavior. ```kotlin // Tests serve as documentation @Test fun `CAMERA permission should be requestable`() @Test fun `request with DENIED status should allow retry`() ``` -------------------------------- ### GrantSettingsDialog Source: https://github.com/brewkits/grant/blob/main/grant-compose/README.md A composable dialog shown when a permission is permanently denied, guiding the user to open the app's settings. ```APIDOC ## GrantSettingsDialog ### Description Shows settings guide when permission is permanently denied. ### Parameters - **message** (String) - Required - The message to display to the user, instructing them to enable the permission in settings. - **title** (String) - Optional - Default: "Permission Denied" - The title of the dialog. - **confirmText** (String) - Optional - Default: "Open Settings" - The text for the confirmation button, typically to open settings. - **dismissText** (String) - Optional - Default: "Cancel" - The text for the dismiss button. - **onConfirm** (() -> Unit) - Required - Callback function when the user confirms, usually to open settings. - **onDismiss** (() -> Unit) - Required - Callback function when the user dismisses the dialog. ### Usage ```kotlin GrantSettingsDialog( message = "Please enable camera access in Settings", onConfirm = { handler.onSettingsConfirmed() }, onDismiss = { handler.onDismiss() } ) ``` ``` -------------------------------- ### Initialize Koin with Grant Modules (Kotlin) Source: https://github.com/brewkits/grant/blob/main/docs/grant-core/GRANTS.md Properly initialize Koin with the necessary grant modules. Incorrect Koin initialization can lead to issues with grant requests. ```kotlin // DemoApplication.kt startKoin { androidContext(this@DemoApplication) modules(grantModule, grantPlatformModule) } ``` -------------------------------- ### Comprehensive State Transition Testing Source: https://github.com/brewkits/grant/blob/main/docs/TESTING_IMPROVEMENTS_SUMMARY.md Illustrates testing all possible state transitions for permissions. Ensure all state changes are validated. ```kotlin // Test all state transitions NOT_DETERMINED → GRANTED NOT_DETERMINED → DENIED DENIED → GRANTED (after Settings) DENIED → DENIED_ALWAYS ``` -------------------------------- ### Run iOS Simulator Tests Source: https://github.com/brewkits/grant/blob/main/docs/TESTING.md Execute only the iOS Simulator tests for a specific module. This requires macOS and Xcode to be installed. ```bash # iOS Simulator tests only (requires macOS + Xcode) ./gradlew :grant-core:iosSimulatorArm64Test ``` -------------------------------- ### Migrate from Koin to Manual Dependency Creation Source: https://github.com/brewkits/grant/blob/main/docs/DEPENDENCY_MANAGEMENT.md Shows how to refactor ViewModel to use manual dependency injection instead of Koin's `inject()`. ```kotlin class MyViewModel : ViewModel() { private val grantManager: GrantManager by inject() } ``` ```kotlin class MyViewModel( private val grantManager: GrantManager ) : ViewModel() // In your composition root val grantManager = GrantFactory.create(context) val viewModel = MyViewModel(grantManager) ``` -------------------------------- ### Testing ViewModel with Grant Core Source: https://github.com/brewkits/grant/blob/main/grant-core/README.md Example of testing a ViewModel that uses Grant Core's fake manager to simulate permission states. ```kotlin class MyViewModelTest { @Test fun `test camera permission flow`() = runTest { val fakeManager = FakeGrantManager() fakeManager.setStatus(AppGrant.CAMERA, GrantStatus.GRANTED) val viewModel = MyViewModel(fakeManager) viewModel.requestCamera() assertTrue(viewModel.cameraOpened) } } ``` -------------------------------- ### After: Simplified Camera Permission Handling with grant-compose Source: https://github.com/brewkits/grant/blob/main/docs/grant-compose/COMPOSE_SUPPORT_RELEASE_NOTES.md This snippet demonstrates the significantly reduced code needed to handle camera permissions using the grant-compose library's GrantDialog. ```kotlin import dev.brewkits.grant.compose.GrantDialog import androidx.compose.material3.Button import androidx.compose.material3.Text import androidx.compose.runtime.Composable @Composable fun CameraScreen(viewModel: CameraViewModel) { // ✅ ONE LINE - handles everything GrantDialog(handler = viewModel.cameraGrant) // Your UI Button(onClick = { viewModel.onCaptureClick() }) { Text("Take Photo") } } ``` -------------------------------- ### Build Framework for iOS Simulator Source: https://github.com/brewkits/grant/blob/main/docs/grant-core/QUICK_START_iOS.md Execute this Gradle command to build the framework for the iOS simulator, which is necessary if you encounter 'Framework not found' errors. ```bash ./gradlew :demo:linkDebugFrameworkIosSimulatorArm64 ``` -------------------------------- ### Summary of Build Pipeline Layers and Analysis Source: https://github.com/brewkits/grant/blob/main/docs/ios/APPLE_FRAMEWORK_LINKING_ISSUE.md Illustrates the flow from Kotlin/Native imports through various build stages to the final App Store analysis, highlighting where the static analyzer identifies framework references. ```text import platform.CoreMotion.* │ ▼ K/N ObjC interop (COMPILE TIME) .klib metadata: "CoreMotion" ◄── Apple static analyzer reads HERE │ ▼ K/N DCE (BUILD TIME) Strips Kotlin class bodies — cannot retract metadata already emitted │ ▼ Linker (-weak_framework) Changes LC_LOAD_DYLIB → LC_LOAD_WEAK_DYLIB — analyzer unaffected │ ▼ Apple static analyzer (UPLOAD TIME) Finds "CoreMotion" in metadata → requires NSMotionUsageDescription │ ▼ Runtime hasInfoPlistKey guard — prevents crash, too late for App Store ``` -------------------------------- ### GrantSettingsDialog Component Source: https://github.com/brewkits/grant/blob/main/grant-compose/README.md GrantSettingsDialog is shown when a permission is permanently denied, guiding the user to enable it in settings. It requires a message and callbacks for confirmation and dismissal. ```kotlin fun GrantSettingsDialog( message: String, title: String = "Permission Denied", confirmText: String = "Open Settings", dismissText: String = "Cancel", onConfirm: () -> Unit, onDismiss: () -> Unit ) ``` ```kotlin GrantSettingsDialog( message = "Please enable camera access in Settings", onConfirm = { handler.onSettingsConfirmed() }, onDismiss = { handler.onDismiss() } ) ``` -------------------------------- ### English Localized InfoPlist.strings Source: https://github.com/brewkits/grant/blob/main/docs/ios/INFO_PLIST_LOCALIZATION.md This file provides English descriptions for various permission keys used in the Info.plist. ```strings "NSCameraUsageDescription" = "Camera is used to scan QR codes for quick login and product identification"; /* Location Permissions */ "NSLocationWhenInUseUsageDescription" = "Location is used to show nearby stores and calculate delivery distance"; "NSLocationAlwaysAndWhenInUseUsageDescription" = "Background location is used to track delivery routes even when the app is closed. This helps provide accurate delivery ETAs."; /* Photo Library */ "NSPhotoLibraryUsageDescription" = "Photo library access is used to upload profile pictures and share product images"; /* Microphone */ "NSMicrophoneUsageDescription" = "Microphone is used to record voice messages and audio notes"; /* Contacts */ "NSContactsUsageDescription" = "Contacts access is used to help you find and invite friends to the app"; /* Bluetooth */ "NSBluetoothAlwaysUsageDescription" = "Bluetooth is used to connect to fitness trackers and smart devices"; /* Motion & Fitness */ "NSMotionUsageDescription" = "Motion data is used for step counting, activity tracking, and fitness goals"; ``` -------------------------------- ### Initialize ServiceFactory on Android Source: https://github.com/brewkits/grant/blob/main/docs/grant-core/SERVICES.md Required for Android. Initialize ServiceFactory with the application context in your Application class's onCreate method before starting Koin. ```kotlin class MyApplication : Application() { override fun onCreate() { super.onCreate() // REQUIRED: Initialize ServiceFactory with context ServiceFactory.init(this) // Start Koin startKoin { modules( grantModule, // Includes ServiceManager grantPlatformModule ) } } } ``` -------------------------------- ### Grant Compose Permission Handling Source: https://github.com/brewkits/grant/blob/main/grant-compose/README.md Demonstrates the simplified approach to permission handling using Grant Compose with a single line of code. ```kotlin import androidx.compose.runtime.* // ✅ GRANT COMPOSE: One line, handles everything @Composable fun CameraScreen(viewModel: CameraViewModel) { GrantDialog(handler = viewModel.cameraGrant) // That's it! Button(onClick = { viewModel.onTakePhotoClick() }) { Text("Take Photo") } } ``` -------------------------------- ### Conventional Commit Example Source: https://github.com/brewkits/grant/blob/main/CONTRIBUTING.md Commit changes following the Conventional Commits specification. Use prefixes like 'feat:', 'fix:', 'docs:' to categorize your commits. ```bash git commit -m "feat: Add your feature description" ``` -------------------------------- ### Create Grant Manager with Custom Storage Source: https://github.com/brewkits/grant/blob/main/grant-core/README.md Demonstrates creating a GrantManager instance with default in-memory storage and how to provide a custom GrantStore implementation for advanced persistence needs. ```kotlin // Default (recommended): In-memory storage val grantManager = GrantFactory.create(context) // Custom: Implement your own GrantStore class MyCustomStore : GrantStore { /* ... */ } val grantManager = GrantFactory.create(context, MyCustomStore()) ``` -------------------------------- ### Test Case 3: After Settings Enable Source: https://github.com/brewkits/grant/blob/main/docs/FIX_DEAD_CLICK_ANDROID.md Tests the scenario where the user enables the permission through system settings and then returns to the app. ```text 1. Continue from Test Case 2 2. Go to Settings → Enable Camera 3. Return to app 4. Click "Request Camera" 5. EXPECTED: Granted immediately, callback runs ✅ ``` -------------------------------- ### Bloated ViewModel with Boilerplate (Bad) Source: https://github.com/brewkits/grant/blob/main/docs/grant-core/ARCHITECTURE.md This example illustrates a ViewModel with extensive boilerplate code for handling permission requests, including state management for rationale and settings. ```kotlin // ❌ BAD: 40+ lines of boilerplate per grant class CameraViewModel(private val manager: GrantManager) : ViewModel() { private val _showRationale = MutableStateFlow(false) val showRationale = _showRationale.asStateFlow() private val _showSettings = MutableStateFlow(false) val showSettings = _showSettings.asStateFlow() private val _isRequesting = MutableStateFlow(false) val isRequesting = _isRequesting.asStateFlow() fun onCaptureClick() { viewModelScope.launch { _isRequesting.value = true when (manager.checkStatus(AppGrant.CAMERA)) { GrantStatus.GRANTED -> openCamera() GrantStatus.NOT_DETERMINED -> requestGrant() GrantStatus.DENIED -> _showRationale.value = true GrantStatus.DENIED_ALWAYS -> _showSettings.value = true } _isRequesting.value = false } } fun onRationaleConfirmed() { viewModelScope.launch { _showRationale.value = false _isRequesting.value = true val result = manager.request(AppGrant.CAMERA) if (result == GrantStatus.GRANTED) { openCamera() } else if (result == GrantStatus.DENIED_ALWAYS) { _showSettings.value = true } _isRequesting.value = false } } fun onSettingsConfirmed() { _showSettings.value = false manager.openSettings() } fun onDismiss() { _showRationale.value = false _showSettings.value = false } // ... 40+ lines total } ``` -------------------------------- ### Documenting Manual Creation Source: https://github.com/brewkits/grant/blob/main/docs/DEPENDENCY_MANAGEMENT.md Document your choice to use manual creation for the Grant library in your project's code. This clarifies the dependency management strategy. ```kotlin // Manual creation approach // We don't use Koin to avoid version conflicts private val grantManager = GrantFactory.create(context) ```