Lesson 03 / 12
Domain Frameworks
Measuring that requested permissions intersect with granted ones, that a capability missing from the device gives rejection, and that the scope of a permission does not change the rule.
Published Updated
In this lesson
- Defines a domain framework as a system framework dedicated to a single domain
- Measures the intersection of requested and granted permissions
- Measures that a capability missing from the device gives rejection
- Shows that the scope of a permission does not change the class of the meeting
Contents
The previous lesson measured two modules opening the same name; what met was a name. In this lesson what meets is an authority: what the app asks for and what the user grants. The question stays the same: how are these two definitions resolved when they meet? The resolution examined in this lesson is intersection: in the result, only the authorities contained in both sides remain.
Some of the system frameworks are general purpose: collections, dates, files, networking. Others are dedicated to a single domain and are called domain frameworks. The location framework (Core Location) reports where the device is; the map framework (MapKit) shows it on a map; the health framework (HealthKit) reads and writes health data; the media framework (AVFoundation) manages audio and video capture and playback; the graphics frameworks take over drawing; the augmented reality framework (ARKit) turns the image from the camera into a model of the surroundings; the on-device model framework (Core ML) runs a trained model on the device. What these frameworks have in common is that most of them touch the user’s data or a capability of the device. What they touch is defined in two separate places: the app declares what it asks for, and the user and the device determine what they offer.
// Meeting.swift — classifies how two definitions are resolved when they meet
struct Meeting {
let label: String
let first: Set<String>
let second: Set<String>
let result: Set<String>?
init(_ label: String, first: Set<String>, second: Set<String>, result: Set<String>?) {
self.label = label
self.first = first
self.second = second
self.result = result
}
var resolution: String {
if first.isEmpty || second.isEmpty
|| first.isSubset(of: second) || second.isSubset(of: first) {
return "out-of-scope"
}
guard let result, !result.isEmpty else { return "rejection" }
if result == first.union(second) { return "union" }
if result == first.intersection(second) { return "intersection" }
if result == first || result == second { return "precedence" }
return "off-rule"
}
static func list(_ values: Set<String>) -> String {
values.isEmpty ? "empty" : values.sorted().joined(separator: ",")
}
func report() {
let outcome = result.map(Meeting.list) ?? "none"
print("\(label): first \(Meeting.list(first)) | second \(Meeting.list(second)) | result \(outcome) -> \(resolution)")
}
}
func summarize(_ meetings: [Meeting]) {
for resolution in ["precedence", "intersection", "union", "rejection"] {
let count = meetings.filter { $0.resolution == resolution }.count
print("\(resolution): \(count)")
}
}
Permission, Usage Description and Device Capability
Three concepts need to be separated. A usage description is the reason, written in the app’s property list (Info.plist) for a permission, that is shown to the user; if there is no description, the permission question never reaches the user. A permission is the authority that the user gives or does not give to the app, and it can change independently of the app’s request. A device capability is a feature the hardware offers: a camera, an accelerometer, or a depth sensor. The app says what it asks for, the user says what they grant, the device says what it offers; a feature of the app works only if the three parties agree.
When location permission is requested, the usage description is written to the property list as a key, and the code asks for the authority only while the app is in use. The two pieces below depend on a platform framework, so they stand as source and no output is claimed for them.
<key>NSLocationWhenInUseUsageDescription</key> <string>Location is used to show nearby stops.</string>
// Source snippet: location permission is requested only while the app is in use import CoreLocation let manager = CLLocationManager() manager.requestWhenInUseAuthorization()
The helper below models two of these three parties: a filter that finds which of the requested permissions are granted, and a filter that finds which of the requested capabilities exist on the device. Both filter one set against the other, so the result is the intersection of two sets. This file has no output.
// Permissions.swift — filters requested permissions and capabilities against what is granted
func granted(requested: Set<String>, grants: [String: String]) -> Set<String> {
requested.filter { grants[$0] != nil }
}
func usable(requested: Set<String>, hardware: Set<String>) -> Set<String> {
requested.filter { hardware.contains($0) }
}
Requested and Granted
In the measurement below the new version of the app requests three permissions: camera, location, and health. The user has declined the camera prompt and granted the location and health permissions. The microphone, on the other hand, is a permission that the previous version of the app requested and the user granted at the time: the new version no longer requests the microphone, but the granted permission stays in the app’s record on the device. So each side has an element the other lacks.
// main.swift — requested permissions intersect with granted ones
let requested: Set = ["camera", "location", "health"]
let grants = ["location": "when-in-use", "microphone": "granted", "health": "granted"]
let request = Meeting("permission-request",
first: requested,
second: Set(grants.keys),
result: granted(requested: requested, grants: grants))
request.report()
permission-request: first camera,health,location | second health,location,microphone | result health,location -> intersection
The camera, which was requested but declined, dropped out, and the microphone, which is left over from the previous version and not requested by the new one, dropped out as well. The two names that remain are present on both sides: the set of authorities the app can use is the common part of what it asks for in this version and what is granted in the user’s record. Within a single version the granted permissions are a subset of the requested ones; the surplus in the record arises only when the request set changes between versions. It differs from the union in the previous lesson in direction: there the number of names grew, here it shrinks.
PS7. This measurement is for a single case made of three requests and three granted permissions in the record. Revoking a permission later, the user denying a permission without asking, and a permission being granted only once were not measured.
A Capability the Device Lacks
On the capability side, the intersection appears in two forms. The app asks for two capabilities and the device offers only one of them: the result is an intersection, and the app works with the remaining capability. The app asks only for a capability the device lacks: the intersection is empty and the resolution is rejection. The measurement below builds these two cases side by side.
// main.swift — a capability the device lacks empties the intersection
let hardware: Set = ["accelerometer", "camera"]
let cases = [
Meeting("capability-partial",
first: ["camera", "depth-sensor"],
second: hardware,
result: usable(requested: ["camera", "depth-sensor"], hardware: hardware)),
Meeting("capability-none",
first: ["depth-sensor"],
second: hardware,
result: usable(requested: ["depth-sensor"], hardware: hardware)),
]
for item in cases {
item.report()
}
summarize(cases)
capability-partial: first camera,depth-sensor | second accelerometer,camera | result camera -> intersection capability-none: first depth-sensor | second accelerometer,camera | result empty -> rejection precedence: 0 intersection: 1 union: 0 rejection: 1
capability-partial continues with the loss of one capability; capability-none has no result. Both come from the same rule, because both are produced by the same filter, and the difference lies only in how close the requested set is to the device. Rejection here is not an error but information: if the app needs that capability, it is better to declare this before the app is installed on the device than to run into an unexpected gap after installation. Whether a capability is required or optional is a separate decision and was not measured here.
PS8. This measurement is for a device with two capabilities and a request with a single capability. A capability changing with the version or the region was not measured.
What Domain Frameworks Offer and Ask For
Each domain framework offers something different and asks for something different. The summary below is conceptual; it carries no claim about numbers or versions.
The location framework provides the geographic location of the device. Because it touches the user’s data, it asks for a permission, and the permission can be granted in two scopes: only while the app is in use, or always. The map framework shows a location on a map; drawing the map needs no separate permission, but showing the user’s own location depends on the permission of the location framework. The health framework reads and writes health data; for each data type the read and write permissions are asked separately, and the user may grant only some of them. The media framework reaches capture devices such as the camera and microphone, and audio and video playback; capture asks for a permission, while playback in most cases does not. The graphics frameworks take over drawing and, because they do not touch the user’s data, ask for no permission. The augmented reality framework turns the camera image into a model of the surroundings; because it uses the camera, it depends on the camera permission, and the device must also carry hardware able to do this work. The on-device model framework runs a trained model on the device; as long as the model runs locally it asks for no permission, but if the input of the model comes from the camera or the microphone, the permission for that input is needed.
This list shows two things. The first is that a permission is a property not of the framework but of the data the framework touches: two different frameworks can share the same camera permission. The second is that a framework not asking for a permission does not mean it will run on every device; a device capability is a separate condition.
Usage Description
A usage description is the explanatory text, written in the app’s property list, that is shown to the user in the permission dialog. For every data type for which a permission is requested, this description is mandatory, and if it is missing the platform either never shows the permission prompt or terminates the app. The description tells the user what the app wants this data for. A good description is concrete and explains only that feature; a broad and vague description leads the user to deny the permission.
An important distinction: a usage description is not a permission. The description is the precondition for showing the permission prompt. The meeting between what the app asks for and what the user grants happens after the description is written, when the permission prompt is shown. For this reason the earlier measurements bring the requested and the granted sets together and do not count the description as a separate party.
Permission Scope
Permission scope is how broad the granted permission is. In the location example there are two scopes: only while in use, and always. As the scope grows, so does the work the app can do; whether this difference reaches the meeting rule is shown by the limiting measurement at the end of the lesson. The scope is not a bargain; the user can narrow the scope from settings, and the app sees this while it runs. The app should assume that when the scope narrows, the work it can do shrinks, and should ask again at run time for the scope that was granted; a scope read once at start-up can lose its validity while the app runs.
The scope has two consequences. If the app keeps the scope it asks for as narrow as possible, the user grants the permission more readily and the app is responsible for less work. If the app asks for a broad scope, the platform may remind the user of this at certain intervals. In both cases the resolution is the intersection: the common part of what the app asked for and what the user granted.
When to Show the Permission Prompt
The timing of the permission prompt affects the likelihood that the permission is granted. A flow that asks for three permissions as soon as the app opens has not yet shown the user why they are needed; the user denies, and the denial is in most cases permanent. Asking for location permission when the “places near me” button is tapped on the map, and for camera permission when the first photo is taken, shows the user the context of the request and makes the explanation in the usage description concrete.
A denied permission is also a result, and the app must handle it. When a permission is not granted, the rest of the app should keep working, and only that feature should be disabled. If location is denied, the map still opens, but the user is asked to enter an address. If the user wants to change their decision later, the way is to direct them to settings; the platform does not ask the same permission a second time with a dialog. This shows why a permission is a meeting of two sides: the user’s answer determines the app’s next step. The rejection in the capability-none measurement is a similar situation: when no common result emerges, the app should offer a fallback path.
The Bounding Measurement
Location permission can be granted in two scopes: only while the app is in use, and always. The scope matters a great deal to the user, but not to the meeting rule: the rule looks at the common elements of two sets and sees the permission name as the element; it does not see in which scope the permission was granted. The measurement below builds the same request with a location permission in two different scopes.
// main.swift — the scope of a permission does not change the intersection
let requested: Set = ["camera", "location", "health"]
let whenInUse = ["location": "when-in-use", "microphone": "granted", "health": "granted"]
let always = ["location": "always", "microphone": "granted", "health": "granted"]
let scopes = [
Meeting("location-when-in-use",
first: requested,
second: Set(whenInUse.keys),
result: granted(requested: requested, grants: whenInUse)),
Meeting("location-always",
first: requested,
second: Set(always.keys),
result: granted(requested: requested, grants: always)),
]
for item in scopes {
item.report()
}
location-when-in-use: first camera,health,location | second health,location,microphone | result health,location -> intersection location-always: first camera,health,location | second health,location,microphone | result health,location -> intersection
The two lines are the same apart from the label: the two measurements give the same result and the same class. The difference the scope brings, namely that a location permission granted always is enough for more work than one granted while in use, lies in the meaning of the permission and stays outside this measurement. The class of the rule is not affected by the scope; to read the scope, the result has to be examined separately. For this reason a “while in use” permission enters the intersection count but does not say what the permission is enough for; the difference between reading a result as “the permission exists” and reading it as “the permission exists under every condition” stays outside the classification.
PS9. This measurement is only for two scopes and a single permission name. A scope changing a behavior such as receiving location in the background does not exist in this model and was not measured.
Summary
- A domain framework is a system framework dedicated to a single domain: location, health, media, graphics, augmented reality, and the on-device model are examples.
- The app asks for a permission, the user grants it, the device offers a capability; what is usable is the common part of what was requested and what was granted.
- Requested permissions intersect with granted ones: the camera dropped out, and location and health remained.
- A capability the device lacks empties the intersection, and the resolution is rejection.
- The scope of a permission (only while in use, or always) does not change the class of the rule; the rule looks at the set and does not read the scope.
- The resolution of this lesson: intersection.
Next Step
Up to here, the two definitions were two faces of one set. In the next lesson both become delegates that handle the same event: the app delegate and the scene delegate. If both define a handler for the same event, we measure which one runs; the resolution is precedence.
To keep your progress and take notes, Log in
My notes
Log in to take notes.