Community-maintained Agent Skills for complete Swift and Apple-platform app delivery.
73
92%
Does it follow best practices?
Run evals on this skill
Adds up to 20 points to the overall score
View guide
Medium
Suggest reviewing before use
Use this reference for permission strategy, APNs registration, device tokens, provider payloads, background notifications, and delivery diagnosis. Rich notification extensions are covered separately in rich-notifications.md.
UNUserNotificationCenter authorization controls user-visible interactions such as alerts, sounds, and badges. It is separate from APNs device-token registration.
Request visible authorization in context, after the user understands the benefit. The system records the decision; repeated calls do not recreate the initial prompt. Query notificationSettings() because the user can change individual settings later.
let center = UNUserNotificationCenter.current()
let settings = await center.notificationSettings()
if settings.authorizationStatus == .notDetermined {
_ = try await center.requestAuthorization(options: [.alert, .sound, .badge])
}Provisional authorization is appropriate only when quiet trial delivery matches the product experience. Critical alerts require Apple's entitlement and a qualifying health, safety, or security use case. Do not present a Settings link as another permission prompt; use it only after denial when the user explicitly wants to change notification behavior.
Call UIApplication.shared.registerForRemoteNotifications() whenever the app needs an APNs token for provider binding or background delivery. Do not gate it on .authorized; visible-notification authorization and token registration are different contracts.
Receive registration callbacks through the application delegate, including in SwiftUI apps via UIApplicationDelegateAdaptor.
final class AppDelegate: NSObject, UIApplicationDelegate {
func application(
_ application: UIApplication,
didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data
) {
let token = deviceToken.map { String(format: "%02x", $0) }.joined()
Task { await tokenService.upload(token) }
}
func application(
_ application: UIApplication,
didFailToRegisterForRemoteNotificationsWithError error: Error
) {
logger.error("APNs registration failed: \(error.localizedDescription)")
}
}Treat the token as opaque and variable-length. Upload it on every successful callback; APNs can change it. Do not persist a token locally as the provider's source of truth or skip upload because it matches a cached value. Associate tokens with the correct user/session on the provider and remove invalid tokens in response to APNs feedback.
On supported current Xcode/OS hosts, iOS Simulator can register with the APNs Sandbox and receives a simulator-specific, variable-length token. Provider delivery to that Simulator is useful for sandbox end-to-end checks. .apns and simctl push exercise simulated delivery without the provider path; host/CI support varies. Confirm production registration, signing, entitlements, and hardware-specific behavior on a physical device.
Keep Apple-reserved keys inside aps; place product routing identifiers beside it. Validate and authorize custom identifiers again in the app before navigating or mutating data.
| Delivery | Required payload/headers | Important behavior |
|---|---|---|
| Visible alert | aps.alert; apns-push-type: alert | Authorization and Focus settings affect presentation |
| Background update | aps contains only content-available: 1; apns-push-type: background; apns-priority: 5 | Low priority, throttled, and not guaranteed |
| Service extension | Alert payload plus mutable-content: 1; alert push type | Silent-only payloads don't launch the extension |
Use the correct apns-topic for the target bundle, an expiration appropriate to the content, and a stable collapse identifier only when replacing an older pending notification is intended. Inspect APNs HTTP status and reason values rather than treating every non-200 response as retryable. Authentication failures, bad topics, malformed payloads, and invalid tokens require different recovery.
Avoid sensitive plaintext in alert bodies and custom payload fields. APNs payloads are delivery envelopes, not trusted authorization state or durable storage.
{
"aps": {
"alert": {
"title": "New message",
"body": "Open the app to read it"
},
"sound": "default",
"thread-id": "conversation-42",
"category": "MESSAGE_CATEGORY"
},
"messageID": "42"
}Use title-loc-key, loc-key, and loc-args when the device should localize the alert from app resources.
Enable Background Modes > Remote notifications. The aps dictionary for a background notification contains content-available: 1 without alert, sound, or badge keys.
{
"aps": { "content-available": 1 },
"changeToken": "opaque-server-token"
}The provider must use apns-push-type: background and priority 5. Apple treats these pushes as low priority, may coalesce or throttle them, and doesn't guarantee delivery. Don't promise immediate refresh or schedule them every few minutes; use them as a hint to fetch current server state.
Perform bounded work and return the correct result promptly:
func application(
_ application: UIApplication,
didReceiveRemoteNotification userInfo: [AnyHashable: Any]
) async -> UIBackgroundFetchResult {
do {
let changed = try await syncService.refresh(using: userInfo)
return changed ? .newData : .noData
} catch {
return .failed
}
}Route scheduled or long-running work that needs BGTaskScheduler to background-processing. A push doesn't grant unlimited runtime.
Set UNUserNotificationCenter.current().delegate during app launch, before a notification response can arrive. Implement:
willPresent to decide foreground banner, list, sound, and badge behavior;didReceive to handle body taps, dismissals, and registered actions;Register categories before relevant notifications arrive. Ensure payload category identifiers and app category/action identifiers match exactly.
Work from the first failed boundary:
Do not diagnose a missing banner as proof that APNs delivery failed; foreground policy, authorization, Focus, summary, and extension failure are separate stages.
.tessl-plugin
skills
accessorysetupkit
references
activitykit
adattributionkit
references
alarmkit
references
app-clips
app-intents
app-store-optimization
app-store-review
apple-on-device-ai
appmigrationkit
audioaccessorykit
references
authentication
references
avkit
background-processing
references
browserenginekit
callkit
references
carplay
cloudkit
contacts-framework
references
core-bluetooth
references
core-data
core-motion
references
core-nfc
references
coreml
references
cryptokit
cryptotokenkit
references
debugging-instruments
device-integrity
references
dockkit
energykit
references
eventkit
financekit
references
focus-engine
gamekit
healthkit
references
homekit
references
ios-accessibility
ios-app-workflow
references
ios-ettrace-performance
ios-localization
ios-memgraph-analysis
ios-networking
ios-simulator
references
metrickit
references
musickit
references
natural-language
references
paperkit
references
passkit
references
pdfkit
pencilkit
references
permissionkit
references
photokit
push-notifications
realitykit
references
relevancekit
references
scenekit
sensorkit
speech-recognition
references
spritekit
storekit
swift-api-design-guidelines
swift-architecture
references
swift-charts
swift-codable
references
swift-code-review
swift-concurrency
swift-formatstyle
references
swift-language
swift-security
references
swift-testing
swiftdata
swiftlint
swiftui-animation
swiftui-gestures
references
swiftui-layout-components
swiftui-liquid-glass
references
swiftui-patterns
swiftui-performance
swiftui-responsive-layout
swiftui-uikit-interop
swiftui-webkit
tabletopkit
tipkit
vision-framework
weatherkit
references