Pattern · Settings

New

Privacy Settings SwiftUI Block

The Privacy page of Settings: each permission's real status with a tap to ask or change it, data toggles, a data export and the policies.

Where people see what the app may use and what it does with their data. Each permission you declare shows its real status from iOS, asks there and then when it was never asked, and opens Settings otherwise. Data toggles, Download my data with working, done and failed states, blocked accounts and the policies follow.

  • SwiftUI
  • iOS 17.0+
  • privacy-settings
Get SwiftPieces Pro

$199 once, every piece included. See how installing works · Sign in

01

What's included

SwiftUI source you own, built on Apple frameworks only.

  • Camera, Photos, Location, Contacts, Microphone and Notifications read live from iOS
  • Allowed, Limited, Not allowed and Not asked chips, with Limited for partial access
  • Tapping Not asked asks iOS in place; any other status opens Settings
  • Data toggles bound to your settings and Download my data with its states
  • Blocked accounts, Privacy policy and Terms rows appear when you pass them

02

Implementation notes

How it behaves, what it handles, and the decisions behind it.

The Permissions card lists the permissions you pass, each with its symbol, name, the reason you give and a status chip read from iOS: Allowed, Limited (selected photos, approximate location, chosen contacts, quiet notifications), Not allowed or Not asked. Statuses are read again whenever the app comes back to the foreground. Tapping a permission that was never asked asks iOS there and then: the chip turns to a spinner while the system alert is up and shows the answer when it closes. Tapping any other opens the app's page in iOS Settings, where the person changes it, and a footnote under the card says so. Your data holds Personalized recommendations and Share analytics as switches bound to your choices. Under it, Download my data which runs your request with a spinner, then a checkmark and "Requested. You'll get an email with a link.", or the error in the danger colour. Blocked accounts shows its count and opens your list, and Privacy policy and Terms of service open in the browser. Every row and group appears only when you pass what it needs.

Notes

  • PrivacySettings(permissions:choices:statuses:blockedCount:privacyPolicy:terms:onDownloadData:onBlockedAccounts:). Only permissions and choices are required. It is a section: no navigation stack, background, padding or scroll view of its own, so place it in your own column, such as a ScrollView with .padding(.horizontal, 20).
  • permissions is a list of PrivacySettings.Permission in display order: .camera(_:), .photos(_:), .location(_:), .contacts(_:), .microphone(_:) and .notifications(_:), each with an optional reason ("Scan receipts and documents."). Pass only the ones your app uses. An empty list hides the Permissions card. Permission.samples is Camera, Photos, Location and Notifications from the preview.
  • Status comes from the system: AVCaptureDevice for camera and microphone, PHPhotoLibrary at the read and write level, CLLocationManager (reduced accuracy reads as Limited), CNContactStore and UNUserNotificationCenter (provisional reads as Limited). Location asks for When In Use. Pass statuses ([Permission.Kind: Status]) to show your own values instead, for previews or an app that tracks them; tapping Not asked still asks iOS.
  • Status is .allowed, .limited, .denied (declined or restricted) or .notAsked.
  • choices is a Binding<PrivacySettings.Choices> with personalizedRecommendations and shareAnalytics, both on by default. Save them where you save other settings; the switches only write the binding.
  • onDownloadData is async throws: ask your backend to prepare an export there. The button shows a spinner, then a checkmark and stays done. Throw to fail: the block shows a LocalizedError's errorDescription, or "The request didn't go through. Try again.", and the button can be tapped again. Nil hides Download my data.
  • onBlockedAccounts opens your blocked accounts list; blockedCount shows beside it. privacyPolicy and terms are URLs that open with the environment's openURL. Each nil hides its row, and the card goes when all three are nil.
  • Haptics: a selection tick on each switch, success when a data request lands and an error when it fails.
  • VoiceOver reads each permission as one element with its reason and status ("Camera, Scan receipts and documents., Allowed"), with a hint for what a tap does, and announces the answer after asking and the result of a data request. Switches are native toggles through accessibilityRepresentation. Rows are at least 52 points tall. At accessibility sizes the chip moves under the permission's name.
  • Reduce Motion turns status changes and the button states into short fades.
  • PrivacySettingsExample at the end of PrivacySettings.swift is the Privacy page from the preview: the sample permissions read live from the system, both data toggles on, three blocked accounts, both policies and a data request that takes a moment and succeeds. Pass wrapsInNavigationStack: false to push it in your own stack. The States preview fixes every status and shows a data request that fails.

Theming

  • Reads SPTheme from @Environment(\.spTheme) and requires the design-system item.
  • Cards are color.surface with radius.lg; rows are 16 points in from the edge (space.lg) with SPHairline dividers in separator, inset to the text. Groups sit 24 points apart (space.xl) under type.headline titles; the footnote under Permissions is type.footnote in textSecondary.
  • Titles are type.body in textPrimary, reasons type.footnote in textSecondary, symbols textSecondary, chevrons and the open arrow textTertiary.
  • Chips are type.footnote medium: Allowed in success and Limited in warning, each on a 14% tint of itself; Not allowed textSecondary on fill; Not asked textPrimary inside a separator hairline.
  • Download my data is a secondary button: fill with textPrimary in type.headline, radius.md, 52 points. Its note is type.footnote in textSecondary, or danger on a failure.
  • Switches are accent with an accentOn thumb when on and fill with a white thumb when off; they are the block's only accent.
  • Changes move with motion.snappy and become short fades under Reduce Motion.

Requirements

  • An Info.plist usage description for each permission you pass, or iOS ends the app when the block asks: NSCameraUsageDescription, NSPhotoLibraryUsageDescription, NSLocationWhenInUseUsageDescription, NSContactsUsageDescription, NSMicrophoneUsageDescription. Notifications need no key.