Permissions

Permissions

Overview

HudPermissions collapses every per-framework auth API on Apple platforms — AVCaptureDevice.requestAccess, AVAudioApplication.requestRecordPermission, PHPhotoLibrary.requestAuthorization, UNUserNotificationCenter.requestAuthorization — into one async-first surface. App code branches on a single HudPermissionStatus enum instead of four framework-specific status types.

iOS supports camera, microphone, photos, and notifications today. Location is on the roadmap. macOS calls return .unavailable until per-platform plumbing lands. Import from HudsonUI.

Imperative API

HudPermissions is a static enum namespace — there is no shared instance. Call request(_:) from any async context to surface the system prompt and receive the resolved status.

import HudsonUI

func enableDictation() async {
    let status = await HudPermissions.request(.microphone)
    guard status.isAuthorized else {
        if status.isTerminal { HudPermissions.openSettings() }
        return
    }
    startRecording()
}

status(of:) returns the current status without prompting. openSettings() opens the host app's permission page in the system Settings app — the only way out of .denied or .restricted.

Declarative gate

HudPermissionGate wraps permission-protected content with the appropriate UI for each status state — request card, denied + Settings card, or unavailable card — so call sites stop reimplementing the same three branches.

import HudsonUI

struct DictationScreen: View {
    var body: some View {
        HudPermissionGate(.microphone, rationale: "The app uses the microphone for dictation.") {
            RecordingView()
        }
    }
}

The gate reads the current status on .task, swaps to your content as soon as it is .granted or .limited, and otherwise renders a HudCard with the right call-to-action.

HudPermission

CaseInfo.plist keySymbol
.microphoneNSMicrophoneUsageDescriptionmic.fill
.cameraNSCameraUsageDescriptioncamera.fill
.photosNSPhotoLibraryUsageDescriptionphoto.on.rectangle.angled
.notifications— (no Info.plist string required)bell.fill

Each case exposes infoPlistKey, symbolName, and displayName. iOS silently fails to surface the prompt if the matching NSUsageDescription string is missing from your Info.plist or INFOPLIST_KEY_* build settings — verify with HudPermission.microphone.infoPlistKey during integration.

HudPermissionStatus

ValueMeaning
.notDeterminedUser hasn't been asked yet. request will prompt.
.grantedFull access.
.limitedPartial access (Photos .limited, Notifications .provisional / .ephemeral). Treat as success.
.deniedUser said no. request won't re-prompt — only Settings will.
.restrictedParental controls / MDM blocked the prompt.
.unavailableNot supported on the current platform / OS version.

Two helpers keep call sites tidy:

  • isAuthorized — true for .granted or .limited. Gate your protected work on this.
  • isTerminal — true for .denied or .restricted. Use it to switch from "show request button" to "show open-Settings button".
switch status {
case _ where status.isAuthorized: showContent()
case _ where status.isTerminal:   showSettingsLink()
default:                          showRequestButton()
}
For AI agents