Note: This article is a work-in-progress. I was battling this issue and wanted to get some thoughts written down, and encourage Apple to run with this. Submitted as FB13584965.

AccessPerms.png

Apple desperately needs a unified permissions API. An API that allows applications to check and request permission to access sensitive services in the OS, like photos or location. Right now it’s a hodgepodge of APIs, wherein some service have explicit methods for testing requesting access status, and others implicitly prompt the user and give little to no feedback to the caller. Often, the app must be relaunched after permissions have been granted.

This is a proposal for an extensible, pluggable API that all appleOS systems should adopt (well, after Apple implements it, of course).

High-Level Structure

I have a very limited knowledge of how Apple maintains a record of the user’s permission-granting intent. This post describes the Transparency, Consent, and Control utilities and database file. Not mentioned in that article is the tccd dæmon. It is probably the right entity to handle requests through this API, and to record the interactions in its DB.

For the purposes of this discussion, we’ll refer to tccd is the implementing process, despite the fact that today it does nothing of the sort.

Each service with privacy or security concerns has a plug-in module that’s queried by the proposed permissions manager to define the permissions is needs in order to operate. It seems to me that this plug-in can be no more than a configuration file, and likely not require code.

Client API

The client API provides a shared manager, means for enumerating serices and their permissions, testing specific permissions, and requesting permissions.

A singleton TCCManager object is available:

TCCManager.shared

Service Identifier

struct
TCCServiceIdentifier
{
	public static let location				=	TCCServiceIdentifier("com.apple.locationservices")
	public static let photos					=	TCCServiceIdentifier("com.apple.photos")
}

Each Apple framework declares its own service identifier:

extension
TCCServiceIdentifier
{
	public static let eventTap				=	TCCServiceIdentifier("com.apple.eventTap")
}

The string value must match the string value in the Service Plug-In.

Permission

struct
TCCPermission
{
	private(set) public	let	identifier		:	String
	private(set) public	let	localizedName	:	String
	private(set) public	let	granted			:	Bool
	
	
	public static let photosAddMedia			=	TCCPermission("addMedia", service: .photos)
	public static let photosReadMedia			=	TCCPermission("readMedia", service: .photos)
	public static let photosDeleteMedia		=	TCCPermission("deleteMedia", service: .photos)
}
  • How can we namespace the permissions under each Service?

Testing Permission

extension
TCCManager
{
	public	var	services		:	[TCCService]
	
}

struct
TCCService
{
	private(set)	public	let	identifier				:	TCCServiceIdentifier
	private(set)	public	var	permissionsAvailable	:	[TCCPermission]
	private(set)	public	var	permissionsGranted		:	[TCCPermission]
}

Permissions need to work as an OptionSet, but only within a Service.

let mgr = TCCManager.shared
let photosService = mgr.get(service: .photos)
let canOpenPhotos = try photosService.hasPermission(.photosReadMedia)

How to describe a service that’s for a specific item, like a single photo? A URL? A resource UUID?

Notification

Clients can also be notified when permsissions are granted or revoked:

let mgr = TCCManager.shared
for await change: TCCPermissionChange in mgr.permissionsChanges
{
	
}

or

mgr.delegate = self

protocol
TCCManagerDelegate
{
	func permissionChanged(change: TCCPermissionChange)
}

struct
TCCPermisisonChange
{
	var	granted: [TCCPermission]
	var revoked: [TCCPermission]
}

Requesting Permission

let mgr = TCCManager.shared
let photosService = mgr.get(service: .photos)
let (grantedPermissions, disposition) = try await photosService.request(permissions: [.photosReadMedia, .photosWriteMedia])

or 

try photosService.request(permissions: [.photosReadMedia, .photosWriteMedia])
{ inGrantedPermissions, inDisposition in		//	An option set of permissions that were granted.
}

Disposition

Generally right now the system only presents the permissions request UI to the user one time for any given client or system, so as to not constantly pester the user. Instead the user should be given “Grant,” “Deny,” and “Deny and Don’t Ask Again” choices. The disposition result will indicate this.

Generally, a client should not pester the user with the modal system request dialog. But it can be very inconventient for the user to have to visit System Settings and root around for the specific permission they need to grant if they decide to do so after they’ve already denied it. So generally, apps should be able trigger the system UI whenver they need to (generally in response to a direct user action in the client’s UI).

An app that abuses this will get perma-banned by the user.

Service API

For now, I think a service can get by with a configuration file that defines the permissions it grants, constituting the service’s plug-in. This example shows multiple services being defined, but usually it’s one per file.

services:
  - service: com.apple.eventTap
    permissions:
      - permission: com.apple.eventTap.listen
        requiresAdmin: true
        localizedName:
          - en.US: "Listen to events"
          
  - service: com.apple.photos
    permissions:
      - permission: com.apple.photos.readMedia
        requiresAdmin: false
        localizedName:
          - en.US: "Access photos and videos"
      - permission: com.apple.photos.addMedia
        requiresAdmin: false
        localizedName:
          - en.US: "Add photos and videos"
      - permission: com.apple.photos.deleteMedia
        requiresAdmin: false
        localizedName:
          - en.US: "Delete photos and videos"
  • Is yaml the best thing here? Most stuff like this from Apple is PLists. And there’s the newly=opensourced Pikl.
  • How is this localized? Multiple files? Strings files from Xcode would smart.
  • Many permissions are grouped under one name in the UI. For example, under Privacy & Security -> Accessibility (“Allow the applications below to control this computer”), a number of permissions are granted by turning the switch on for a given app that has made a request. The various APIs that make the request record different permissions in the TCC DB, but the UI facing the user is all under this Settings panel.
  • How to signal current or recent use? E.g. location has an indicator next to each app that is using or recently used location data.