Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
17 commits
Select commit Hold shift + click to select a range
a40f94f
feat(apple): create SPM source directory structure
TheoGermain May 5, 2026
3c9fa11
feat(apple): move ObjC sources to SPM-compatible structure
TheoGermain May 5, 2026
8d46c4b
feat(apple): add public header for SPM plugin registration
TheoGermain May 5, 2026
500a25d
feat(apple): move PrivacyInfo.xcprivacy to SPM sources
TheoGermain May 5, 2026
28b453d
feat(apple): add Package.swift for Swift Package Manager support
TheoGermain May 5, 2026
23c8e5c
feat(apple): update podspec to reference SPM source structure
TheoGermain May 5, 2026
d8f1a76
chore(apple): ignore SPM build artifacts
TheoGermain May 5, 2026
4f6cb0d
docs: add SPM setup instructions to README
TheoGermain May 5, 2026
6f6c29b
fix(apple): move Package.swift to correct SPM location (ios/permissio…
TheoGermain May 5, 2026
44f9287
fix(apple): add missing PERMISSION_PHOTOS_ADD_ONLY and PERMISSION_LOC…
TheoGermain May 5, 2026
dffe7cb
fix(apple): use forward declaration in public header to avoid missing…
TheoGermain May 5, 2026
c29aed7
fix(example): bump compileSdk to 36, AGP to 8.9.1, Gradle to 8.11.1
TheoGermain May 5, 2026
b115938
fix(apple): add explicit UIKit import to strategies that use UIApplic…
TheoGermain May 5, 2026
2163cac
feat(apple): auto-detect permissions from Info.plist in Package.swift
TheoGermain May 6, 2026
6406813
docs: update SPM setup instructions to use Info.plist auto-detection
TheoGermain May 6, 2026
329e42a
chore(example): update iOS example app and remove plan artifact
TheoGermain May 6, 2026
526e62f
chore(apple): bump version to 9.4.8 and update CHANGELOG
TheoGermain May 6, 2026
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
45 changes: 45 additions & 0 deletions permission_handler/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -174,6 +174,51 @@ You must list the permission you want to use in your application:

</details>

<details>
<summary>Swift Package Manager (SPM)</summary>

> Requires Flutter 3.24.0 or higher and Xcode 15.0 or higher.

With SPM, `Package.swift` automatically detects which permissions to enable by reading your app's `Info.plist`. A permission is compiled in when its corresponding usage description key is present:

| Permission group | Info.plist key |
|---|---|
| `PermissionGroup.calendar` (< iOS 17) | `NSCalendarsUsageDescription` |
| `PermissionGroup.calendarFullAccess` (iOS 17+) | `NSCalendarsFullAccessUsageDescription` |
| `PermissionGroup.reminders` | `NSRemindersUsageDescription` |
| `PermissionGroup.contacts` | `NSContactsUsageDescription` |
| `PermissionGroup.camera` | `NSCameraUsageDescription` |
| `PermissionGroup.microphone` | `NSMicrophoneUsageDescription` |
| `PermissionGroup.speech` | `NSSpeechRecognitionUsageDescription` |
| `PermissionGroup.photos` | `NSPhotoLibraryUsageDescription` |
| `PermissionGroup.photosAddOnly` | `NSPhotoLibraryAddUsageDescription` |
| `PermissionGroup.location` / `locationWhenInUse` | `NSLocationWhenInUseUsageDescription` |
| `PermissionGroup.locationAlways` | `NSLocationAlwaysAndWhenInUseUsageDescription` |
| `PermissionGroup.mediaLibrary` | `NSAppleMusicUsageDescription` |
| `PermissionGroup.sensors` | `NSMotionUsageDescription` |
| `PermissionGroup.bluetooth` | `NSBluetoothAlwaysUsageDescription` |
| `PermissionGroup.appTrackingTransparency` | `NSUserTrackingUsageDescription` |
| `PermissionGroup.assistant` | `NSSiriUsageDescription` |

Because you must already add these keys to `Info.plist` for any permission to work, no additional configuration file is needed.

**`PermissionGroup.notification` and `PermissionGroup.criticalAlerts`** have no required `Info.plist` key. Enable them via environment variable instead (set once per Mac session, before opening Xcode):

```bash
launchctl setenv PERMISSION_NOTIFICATIONS 1
launchctl setenv PERMISSION_CRITICAL_ALERTS 1 # optional
```

**After adding or removing a key**, clear Xcode's package cache once so `Package.swift` is re-evaluated:

```bash
rm -rf ~/Library/Developer/Xcode/DerivedData
```

Then run `flutter build ios` or rebuild in Xcode as usual.

</details>

## How to use

There are a number of [`Permission`](https://pub.dev/documentation/permission_handler_platform_interface/latest/permission_handler_platform_interface/Permission-class.html#constants)s.
Expand Down
2 changes: 1 addition & 1 deletion permission_handler/example/android/app/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ android {
if (project.android.hasProperty("namespace")) {
namespace 'com.baseflow.permissionhandlerexample'
}
compileSdkVersion 35
compileSdkVersion 36

compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,4 @@ distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-8.10.2-all.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-8.11.1-all.zip
2 changes: 1 addition & 1 deletion permission_handler/example/android/settings.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ pluginManagement {

plugins {
id "dev.flutter.flutter-plugin-loader" version "1.0.0"
id "com.android.application" version "8.7.0" apply false
id "com.android.application" version "8.9.1" apply false
}

include ":app"
10 changes: 10 additions & 0 deletions permission_handler_apple/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,13 @@
## 9.4.8

* Adds Swift Package Manager (SPM) support for Flutter 3.24+. Permissions are
enabled automatically based on usage description keys present in `Info.plist`
— no additional configuration required beyond clearing DerivedData once after
changes: `rm -rf ~/Library/Developer/Xcode/DerivedData`.
* Moves ObjC sources to SPM-compatible layout (`Sources/permission_handler_apple/`).
CocoaPods continues to work unchanged.
* Bumps minimum iOS deployment target to 12.0.

## 9.4.7

* Increases minimum supported Flutter version to 3.3.0, and removes code only
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,5 @@
<string>????</string>
<key>CFBundleVersion</key>
<string>1.0</string>
<key>MinimumOSVersion</key>
<string>12.0</string>
</dict>
</plist>
2 changes: 1 addition & 1 deletion permission_handler_apple/example/ios/Podfile
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
# Uncomment this line to define a global platform for your project
# platform :ios, '12.0'
platform :ios, '13.0'

# CocoaPods analytics sends network stats synchronously affecting flutter build latency.
ENV['COCOAPODS_DISABLE_STATS'] = 'true'
Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,28 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "1510"
version = "1.3">
version = "1.7">
<BuildAction
parallelizeBuildables = "YES"
buildImplicitDependencies = "YES">
<PreActions>
<ExecutionAction
ActionType = "Xcode.IDEStandardExecutionActionsCore.ExecutionActionType.ShellScriptAction">
<ActionContent
title = "Run Prepare Flutter Framework Script"
scriptText = "/bin/sh &quot;$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh&quot; prepare&#10;">
<EnvironmentBuildable>
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "97C146ED1CF9000F007C117D"
BuildableName = "Runner.app"
BlueprintName = "Runner"
ReferencedContainer = "container:Runner.xcodeproj">
</BuildableReference>
</EnvironmentBuildable>
</ActionContent>
</ExecutionAction>
</PreActions>
<BuildActionEntries>
<BuildActionEntry
buildForTesting = "YES"
Expand All @@ -26,9 +44,8 @@
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
customLLDBInitFile = "$(SRCROOT)/Flutter/ephemeral/flutter_lldbinit"
shouldUseLaunchSchemeArgsEnv = "YES">
<Testables>
</Testables>
<MacroExpansion>
<BuildableReference
BuildableIdentifier = "primary"
Expand All @@ -38,18 +55,20 @@
ReferencedContainer = "container:Runner.xcodeproj">
</BuildableReference>
</MacroExpansion>
<AdditionalOptions>
</AdditionalOptions>
<Testables>
</Testables>
</TestAction>
<LaunchAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
customLLDBInitFile = "$(SRCROOT)/Flutter/ephemeral/flutter_lldbinit"
launchStyle = "0"
useCustomWorkingDirectory = "NO"
ignoresPersistentStateOnLaunch = "NO"
debugDocumentVersioning = "YES"
debugServiceExtension = "internal"
enableGPUValidationMode = "1"
allowLocationSimulation = "YES">
<BuildableProductRunnable
runnableDebuggingMode = "0">
Expand All @@ -61,8 +80,6 @@
ReferencedContainer = "container:Runner.xcodeproj">
</BuildableReference>
</BuildableProductRunnable>
<AdditionalOptions>
</AdditionalOptions>
</LaunchAction>
<ProfileAction
buildConfiguration = "Profile"
Expand Down
11 changes: 7 additions & 4 deletions permission_handler_apple/example/ios/Runner/AppDelegate.swift
Original file line number Diff line number Diff line change
@@ -1,13 +1,16 @@
import UIKit
import Flutter
import UIKit

@UIApplicationMain
@objc class AppDelegate: FlutterAppDelegate {
@main
@objc class AppDelegate: FlutterAppDelegate, FlutterImplicitEngineDelegate {
override func application(
_ application: UIApplication,
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
) -> Bool {
GeneratedPluginRegistrant.register(with: self)
return super.application(application, didFinishLaunchingWithOptions: launchOptions)
}

func didInitializeImplicitFlutterEngine(_ engineBridge: FlutterImplicitEngineBridge) {
GeneratedPluginRegistrant.register(with: engineBridge.pluginRegistry)
}
}
132 changes: 63 additions & 69 deletions permission_handler_apple/example/ios/Runner/Info.plist
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CADisableMinimumFrameDurationOnPhone</key>
<true/>
<key>CFBundleDevelopmentRegion</key>
<string>$(DEVELOPMENT_LANGUAGE)</string>
<key>CFBundleExecutable</key>
Expand All @@ -22,6 +24,65 @@
<string>$(FLUTTER_BUILD_NUMBER)</string>
<key>LSRequiresIPhoneOS</key>
<true/>
<key>NSAppleMusicUsageDescription</key>
<string>Music!</string>
<key>NSBluetoothAlwaysUsageDescription</key>
<string>bluetooth</string>
<key>NSBluetoothPeripheralUsageDescription</key>
<string>bluetooth</string>
<key>NSCalendarsFullAccessUsageDescription</key>
<string>Calendar full access</string>
<key>NSCalendarsUsageDescription</key>
<string>Calendars</string>
<key>NSContactsUsageDescription</key>
<string>contacts</string>
<key>NSLocationAlwaysAndWhenInUseUsageDescription</key>
<string>Always and when in use!</string>
<key>NSLocationAlwaysUsageDescription</key>
<string>Can I have location always?</string>
<key>NSLocationUsageDescription</key>
<string>Older devices need location.</string>
<key>NSLocationWhenInUseUsageDescription</key>
<string>Need location when in use</string>
<key>NSMicrophoneUsageDescription</key>
<string>microphone</string>
<key>NSMotionUsageDescription</key>
<string>motion</string>
<key>NSPhotoLibraryAddUsageDescription</key>
<string>photos add only</string>
<key>NSPhotoLibraryUsageDescription</key>
<string>photos</string>
<key>NSRemindersUsageDescription</key>
<string>reminders</string>
<key>NSSiriUsageDescription</key>
<string>The example app would like access to Siri Kit to demonstrate requesting authorization.</string>
<key>NSSpeechRecognitionUsageDescription</key>
<string>speech</string>
<key>NSUserTrackingUsageDescription</key>
<string>appTrackingTransparency</string>
<key>UIApplicationSceneManifest</key>
<dict>
<key>UIApplicationSupportsMultipleScenes</key>
<false/>
<key>UISceneConfigurations</key>
<dict>
<key>UIWindowSceneSessionRoleApplication</key>
<array>
<dict>
<key>UISceneClassName</key>
<string>UIWindowScene</string>
<key>UISceneConfigurationName</key>
<string>flutter</string>
<key>UISceneDelegateClassName</key>
<string>FlutterSceneDelegate</string>
<key>UISceneStoryboardFile</key>
<string>Main</string>
</dict>
</array>
</dict>
</dict>
<key>UIApplicationSupportsIndirectInputEvents</key>
<true/>
<key>UILaunchStoryboardName</key>
<string>LaunchScreen</string>
<key>UIMainStoryboardFile</key>
Expand All @@ -41,74 +102,7 @@
</array>
<key>UIViewControllerBasedStatusBarAppearance</key>
<false/>

<!-- Permission options for the `location` group -->
<key>NSLocationWhenInUseUsageDescription</key>
<string>Need location when in use</string>
<key>NSLocationAlwaysAndWhenInUseUsageDescription</key>
<string>Always and when in use!</string>
<key>NSLocationUsageDescription</key>
<string>Older devices need location.</string>
<key>NSLocationAlwaysUsageDescription</key>
<string>Can I have location always?</string>

<!-- Permission options for the `mediaLibrary` group -->
<key>NSAppleMusicUsageDescription</key>
<string>Music!</string>
<key>kTCCServiceMediaLibrary</key>
<string>media</string>

<!-- Permission options for the `calendar` group -->
<key>NSCalendarsUsageDescription</key>
<string>Calendars</string>
<key>NSCalendarsFullAccessUsageDescription</key>
<string>Calendar full access</string>

<!-- Permission options for the `camera` group -->
<key>NSCameraUsageDescription</key>
<string>camera</string>

<!-- Permission options for the `contacts` group -->
<key>NSContactsUsageDescription</key>
<string>contacts</string>

<!-- Permission options for the `microphone` group -->
<key>NSMicrophoneUsageDescription</key>
<string>microphone</string>

<!-- Permission options for the `speech` group -->
<key>NSSpeechRecognitionUsageDescription</key>
<string>speech</string>

<!-- Permission options for the `sensors` group -->
<key>NSMotionUsageDescription</key>
<string>motion</string>

<!-- Permission options for the `photos` group -->
<key>NSPhotoLibraryUsageDescription</key>
<string>photos</string>

<!-- Permission options for the `reminder` group -->
<key>NSRemindersUsageDescription</key>
<string>reminders</string>

<!-- Permission options for the `bluetooth` -->
<key>NSBluetoothAlwaysUsageDescription</key>
<string>bluetooth</string>
<key>NSBluetoothPeripheralUsageDescription</key>
<string>bluetooth</string>

<!-- Permission options for the `appTrackingTransparency` -->
<key>NSUserTrackingUsageDescription</key>
<string>appTrackingTransparency</string>

<!-- Permission options for the `assistant` group -->
<key>NSSiriUsageDescription</key>
<string>The example app would like access to Siri Kit to demonstrate requesting authorization.</string>

<key>CADisableMinimumFrameDurationOnPhone</key>
<true/>
<key>UIApplicationSupportsIndirectInputEvents</key>
<true/>
<key>kTCCServiceMediaLibrary</key>
<string>media</string>
</dict>
</plist>
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,10 @@
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<!--
Uncomment to test PERMISSION_ASSISTANT (requires a paid Apple Developer account):
Comment on lines +5 to +6
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

would be good to keep this in

<key>com.apple.developer.siri</key>
<true/>
-->
</dict>
</plist>
4 changes: 4 additions & 0 deletions permission_handler_apple/ios/.gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -34,3 +34,7 @@ Icon?
.tags*

/Flutter/Generated.xcconfig

# Swift Package Manager
.build/
*.resolved
8 changes: 4 additions & 4 deletions permission_handler_apple/ios/permission_handler_apple.podspec
Original file line number Diff line number Diff line change
Expand Up @@ -12,12 +12,12 @@ Permission plugin for Flutter. This plugin provides a cross-platform (iOS, Andro
s.license = { :file => '../LICENSE' }
s.author = { 'Baseflow' => 'hello@baseflow.com' }
s.source = { :path => '.' }
s.source_files = 'Classes/**/*'
s.public_header_files = 'Classes/**/*.h'
s.source_files = 'permission_handler_apple/Sources/permission_handler_apple/**/*.{h,m}'
s.public_header_files = 'permission_handler_apple/Sources/permission_handler_apple/include/**/*.h'
s.dependency 'Flutter'

s.ios.deployment_target = '8.0'
s.ios.deployment_target = '12.0'
s.static_framework = true
s.resource_bundles = {'permission_handler_apple_privacy' => ['Resources/PrivacyInfo.xcprivacy']}
s.resource_bundles = {'permission_handler_apple_privacy' => ['permission_handler_apple/Sources/permission_handler_apple/PrivacyInfo.xcprivacy']}
end

Loading
Loading