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 CapabilityType
s
CapabilityType | Device or Account Capability |
---|---|
Capability.Calendar | Access EventKit for the user's calendars etc. |
Capability.Cloud | Access a CloudKit container. |
Capability.Health | Access to HealthKit for reading and writing samples. |
Capability.Location | Access Location Services for the device's current location. |
Capability.Passbook | PassKit does not have access controls, but the capability can confirm whether it is enabled or not. |
Capability.Photos | Access 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.
Updated less than a minute ago