One consistent, async-first API for Apple-platform permissions.
PermissionKit is a privacy-conscious, dependency-free Swift Package for checking, requesting, explaining, observing, and testing Apple platform permissions.
- One vocabulary: normalize framework-specific authorization enums into
PermissionStatus. - Async by default: use structured results and concurrency-safe request coordination.
- Honest capability handling: distinguish denial from unavailable hardware and platform support.
- Privacy first: no private APIs, hidden analytics, remote service, or fabricated authorization.
- Testable: inject
PermissionProvidingor useMockPermissionCenterwithout a system prompt.
Add the package in Xcode using File → Add Package Dependencies, or add it to Package.swift:
.package(url: "https://github.com/magnexis/permissionkit.git", from: "0.2.0")Import PermissionKitUI only when using the optional SwiftUI components.
import PermissionKit
switch await Permission.camera.status() {
case .notDetermined:
let result = await Permission.camera.request()
if result.isGranted { /* Start camera work. */ }
case .authorized:
// Start camera work.
default:
break
}let result = await PermissionGroup([Permission.camera, Permission.microphone])
.request(strategy: .requestOnlyUndetermined)let report = PermissionDiagnostics.validate(permissions: [Permission.camera, Permission.microphone])
for issue in report.issues { print("[\(issue.severity)] \(issue.message)") }The validator reads the host bundle but never changes Info.plist or privacy manifests.
for await state in Permission.camera.updates {
print("Camera is now \(state.status)")
}Streams emit current state immediately, then emit after PermissionKit requests, refreshes, or observes application activation on supported Apple platforms.
PermissionCenter keeps a bounded, in-memory history of authorization outcomes only—never user content, contacts, locations, or other sensitive data.
let history = await PermissionCenter.shared.requestHistory()
await PermissionCenter.shared.clearRequestHistory()Create a portable audit report for support diagnostics or local export. It includes only permission identifiers, statuses, metadata, and validation findings—never user content.
let audit = await PermissionAudit.generate(for: [
Permission.camera,
Permission.microphone,
Permission.locationWhenInUse
])
let json = try audit.encodedJSON()Build a dependency-aware onboarding sequence before you show native prompts:
let plan = await PermissionOnboarding.plan(for: [
Permission.locationWhenInUse,
Permission.locationAlways
])For deterministic tests and previews, apply whole permission scenarios without touching Apple APIs:
let mock = MockPermissionCenter()
await mock.apply(.mixed([.camera: .authorized, .microphone: .denied]))import PermissionKitUI
PermissionOnboardingFlow(permission: .camera) { explanation in
VStack {
Text(explanation.title).font(.title2)
Text(explanation.message)
}
}| Permission | Check | Request | Notes |
|---|---|---|---|
| Camera | Yes | Yes | Public AVFoundation API where available |
| Microphone | Yes | Yes | Public AVFoundation API where available |
| Photos | Yes | Yes | Read/write and add-only access levels |
| Contacts | Yes | Yes | Public Contacts API where available |
| Notifications | Yes | Yes | Alert, badge, and sound options |
| Location | Yes | Yes | When-in-use and always flows |
| Speech recognition | Yes | Yes | Public Speech framework where available |
| Tracking transparency | Yes | Yes | iOS only; never treated as required access |
| Media library | Yes | Yes | Public MediaPlayer framework where available |
Unsupported permissions return .unsupported; unavailable device conditions return .unavailable. Neither is treated as a grant.
let mock = MockPermissionCenter()
await mock.script(permission: .camera, initial: .notDetermined, afterRequest: .authorized)
XCTAssertTrue((await mock.request(.camera)).isGranted)PermissionKit is a pre-1.0 package. The core API, diagnostics, request coordination, dependency planner, onboarding and audit utilities, mock scenarios, observation surface, and optional SwiftUI controls are implemented. Full macOS/Xcode and device validation remains in progress; see CHANGELOG.md.
PermissionKit never bypasses Apple permission systems, collects user data, performs analytics, or stores permission snapshots remotely. Permission denial is treated as a valid user decision.
Please read CONTRIBUTING.md, follow the Code of Conduct, and report security concerns through SECURITY.md.
MIT. See LICENSE.