Capabilities

CapabilityType is a protocol which represents an aspect of the device or user account. It supports querying for the current authorization status and explicitly requesting authorization to access the capability. A CapabilityType might also have a Requirement associated with its permission.

Operations has the following CapabilityTypes

CapabilityTypeDevice or Account Capability
Capability.CalendarAccess EventKit for the user's calendars etc.
Capability.CloudAccess a CloudKit container.
Capability.HealthAccess to HealthKit for reading and writing samples.
Capability.LocationAccess Location Services for the device's current location.
Capability.PassbookPassKit does not have access controls, but the capability can confirm whether it is enabled or not.
Capability.PhotosAccess the user's Photo Library.

Authorizing

Let's say you wish perform a task using the user's calendars. This task should be written as a Operation subclass, and it will add a condition.

class ReminderOperation: Operation {
   override init() {
       super.init()
       name = "Reminder Operation"
       addCondition(AuthorizedFor(Capability.Calendar(.Reminder)))
   }
   
   override func execute() {
       // do something with EventKit here
       finish()
   }
}

AuthorizedFor is an OperationCondition which is initialized with a CapabilityType. If the current authorization status is unknown, it will request permission to access the capability as a dependency. When the attached operation (ReminderOperation in this case) evaluates its conditions, AuthorizedFor will then check the authorization status again, and if the requirements of the CapabiltiyType are met, the condition is satisfied. In this case, the requirement was access to the user's reminders.

Checking the current Authorization Status

Sometimes, it is beneficial to test the current status of a CapabilityType without requesting authorization. For example, it is nice to present your users with information about why the application is requesting permission to access their Reminders. Operations make it very easy to do this, by using the GetAuthorizationStatus operation.

func updateForStatus(enabled: Bool, status: EKAuthorizationStatus) {
    // etc - note that the status is specific to EventKit
}

func checkReminderAuthorization() {
    let capability = Capability.Calendar(.Reminder) 
    let check = GetAuthorizationStatus(capability, completion: updateForStatus)
    queue.addOperation(check)
}

Requesting authorization

To explicitly request authorization to access a CapabilityType use the Authorize operation. It is a subclass of GetAuthorizationStatus with the same initializer. This allows the same completion block to be used to update or progress your user interface. Consider the following:

func requestReminderAuthorization() {
    let capability = Capability.Calendar(.Reminder) 
    let request = Authorize(capability, completion: updateForStatus)
    queue.addOperation(request)
}

Combine with SilentCondition

Indeed, the AuthorizedFor condition returns an Authorize operation as its dependency, which means it will always request access. However, in some situations it can make for a nicer user experience if permission requests are delayed until later on. In which case, compose AuthorizedFor inside a SilentCondition.

func doBonusReminderStuff() {
    let doReminderStuff = ReminderOperation() // from earlier
    let operation = ComposedOperation(doReminderStuff)
    let capability = Capability.Calendar(.Reminder)
    operation.addCondition(SilentCondition(AuthorizedFor(capability)))
    queue.addOperation(operation)
}

In the above example, we want to execute the ReminderOperation, but only if the user has already authorized the application to access their reminders. The operation already adds a "noisy" AuthorizedFor condition, however, we can pre-empt this by composing the ReminderOperation inside ComposedOperation. This operation exists purely to wrap other NSOperation subclasses. It is very useful to attach observers and conditions to NSOperation subclasses which are not Operation subclasses, such as you might get from Cocoa frameworks. In this case however, we can use it to attach a condition to check for the calendar capability. The important difference is, that we will suppress the Authorize dependency by using SilentCondition.

Combine with NegatedCondition

In the above example we performed an operation only when the user had already authorized access to a their reminders, without asking for permission if they had not. Another common scenario is to perform an operation only if the user has not authorized access to the capability. For this, we can use a NegatedCondition.

func fallbackGracefullyWithoutReminders() {
    let fallback = FallbackOperation() // assume we have this `Operation` subclass
    let capability = Capability.Calendar(.Reminder)
    let condition = NegatedCondition(SilentCondition(AuthorizedFor(capability)))
		fallback.addCondition(condition)
    queue.addOperation(fallback)
}

For more examples of device capabilities, check out the example project called Permissions in the repository.