Asking for less: minimal app permissions
Least privilege for desktop and mobile apps: macOS sandbox entitlements and privacy prompts, iOS and Android runtime permissions, approximate location, photo pickers and honest explanations.
Minimal app permissions are the cheapest privacy feature you will ever ship. Every permission an app does not ask for is a prompt the user never has to think about, a category of data you never have to protect, and one less line in a breach report. The principle of least privilege is old and dull, and it is still the most useful security rule we apply to the desktop and mobile apps we build.
This post is about what that looks like in practice on macOS, iOS and Android in 2026. The platforms have spent the last few years adding narrower options, and many apps still ask for the wide ones out of habit.
Why asking less is worth the effort
There are three separate reasons, and they reinforce each other.
The first is security. A permission is a capability. If your app has full access to the photo library and it is compromised, through a bad dependency or a bug in an image parser, the attacker has the photo library too. If it only ever saw the three photos the user picked, the damage stops there.
The second is trust. People are better at reading permission prompts than they were ten years ago. A torch app that wants contacts gets deleted. A note app that asks for location on first launch gets a one-star review that says so. On both stores the permissions you declare also show up in the privacy details before anyone installs.
The third is maintenance. Each permission comes with policy. Apple requires a purpose string for every protected resource, and Google Play has policies for sensitive permissions like background location and broad media access that can mean a declaration form and extra review. Fewer permissions means fewer forms, fewer rejections and fewer things to re-justify when the rules change.
macOS: the sandbox and privacy prompts
On the Mac there are two layers. The App Sandbox, required for the Mac App Store and optional for apps you distribute yourself, starts an app with almost nothing and grants capabilities through entitlements in the signed binary. Separately, the system's privacy controls, often called TCC, gate access to things like the camera, microphone, contacts, calendars, photos, screen recording and protected folders, and ask the user at the moment of first use.
A few entitlements do most of the work:
com.apple.security.network.clientfor outgoing connections. Most apps need it. Many do not need the server version.com.apple.security.network.serverfor listening on a port. A local server app like Serve would need it. A photo viewer does not.com.apple.security.files.user-selected.read-onlyorread-write, which gives access to files and folders the user picks in an open or save panel, and nothing else. Security-scoped bookmarks let the app keep that access across launches.
The file entitlement is the one we would encourage every Mac developer to take seriously. The temptation is to ask the user to grant Full Disk Access in System Settings so the app can "just work." Full Disk Access is designed for backup tools, disk utilities and security software. For almost everything else, asking the user to pick a folder once is both safer and clearer about what the app is going to touch.
The hardened runtime, required for notarization, has its own set of exceptions, like allowing unsigned executable memory or disabling library validation. Treat each of those as a permission too. If a dependency needs one, it is worth asking why.
iOS and Android: runtime permissions done narrowly
Both mobile platforms ask for sensitive permissions at runtime, and both now offer narrower versions of the ones people care about most.
Location. On iOS the user can grant location while the app is in use, and can switch off precise location to share only an approximate position, which is often a few kilometres wide. You can also declare that your app prefers reduced accuracy from the start. On Android, ACCESS_COARSE_LOCATION gives an approximate position, and since Android 12 users can downgrade a precise request to approximate in the prompt anyway. Weather, local news and "shops near you" rarely need more than coarse location. Turn-by-turn directions do. Background location, on either platform, is a separate and much harder request, and we avoid it unless the product genuinely cannot work without it.
Photos. This is where the narrow option is also the easiest one. On iOS, the system photo picker (PHPickerViewController, or PhotosPicker in SwiftUI) runs out of process and returns only the items the user selects. It needs no permission prompt at all. On Android, the photo picker does the same, and is available on older versions through Google Play services. If your app lets someone attach a picture to a message or set a profile photo, the picker is the right tool. Full library access, or the Android media permissions, only make sense for apps whose purpose is managing a library, like a gallery or a backup tool. Even then, users can choose limited access on both platforms, so design for it.
Contacts. iOS now lets users share only selected contacts with an app. The better pattern for most apps is a contact picker, which returns one contact and requires no permission. Uploading a whole address book to your server to "find friends" is the kind of feature that looks harmless in a planning meeting and terrible in a news story.
Network. Plain internet access is not a runtime prompt on either platform. Android declares INTERNET as a normal permission granted at install. iOS does, however, prompt for local network access when an app tries to discover or talk to devices on the user's network, and recent macOS versions do the same. If you see that prompt in your app and did not expect it, some library is probably scanning the network. Find out which one.
Asking honestly, and at the right time
How you ask matters as much as what you ask for.
Ask in context. Request the camera when the user taps the camera button, not on first launch before they have seen the app. The system prompt then makes sense on its own, because they just asked to take a photo.
Write purpose strings like a person. The iOS usage description is shown in the prompt, so "We use your location to show weather for your town. We never store it." is better than "This app requires location access to function." Be specific, say what you do not do, and do not promise anything the code does not guarantee.
Handle "no" gracefully. A denied permission should leave a working app with one feature missing, plus a quiet way to change the decision later. Do not nag. Both platforms limit how often you can show the system prompt, and Android will stop showing it after the user denies twice, so an app that depends on asking again will simply break.
Explain before you ask, but only when it helps. A short screen before the system prompt can be useful for permissions that are not obvious, such as local network access or notifications. It becomes a dark pattern when it is designed to herd people into tapping "allow."
A checklist we use
Before any release, we go through the declared permissions and entitlements for each app and ask the same questions:
- Can a system picker do this instead? Photos, files, contacts and documents usually have one.
- Is there a narrower version? Approximate location, read-only files, in-use rather than always.
- Is it asked at the moment it is needed, with a plain explanation?
- Does the app still work if the user says no?
- Did a dependency add something we do not use? Check the merged Android manifest and the final entitlements, not just the ones you wrote.
That last one catches more than you would think. Analytics and advertising libraries in particular have a habit of bringing permissions along, and nobody reads the merged manifest until a store reviewer asks about it.
We wrote about a related idea, a phone with almost nothing on it, in the Humanly Phone concept. Permissions are the same argument at the scale of a single app. If you are planning a mobile app and want to start with the short list rather than trim it later, that conversation is much cheaper before the first build. The usual finding, when we look at an existing app, is one permission that nobody on the team remembers adding.