macOS integration

Request notification permission in context

Ask for notification permission when the user enables a feature that needs it, then handle denial without breaking the app.

12 minute lesson

~~~

Notifications interrupt people. macOS gates them behind a permission prompt, and users have trained themselves to click “Don’t Allow” on prompts they do not understand. The single biggest thing you control is when the question appears.

Ask at first launch, out of nowhere, and you get denied. Ask at the moment the user turns on a feature that clearly needs notifications — “Remind me about this note” — and the prompt explains itself.

The API lives in the UserNotifications framework:

import UserNotifications

func enableReminders() async throws -> Bool {
  let center = UNUserNotificationCenter.current()
  return try await center.requestAuthorization(
    options: [.alert, .sound]
  )
}

The first call shows the system prompt. Every later call returns the stored decision without any UI, so calling it from the feature toggle is safe.

Sending a notification is a separate step — content, trigger, request:

func scheduleReminder(for note: Note, in seconds: TimeInterval) async throws {
  let content = UNMutableNotificationContent()
  content.title = "Reminder"
  content.body = note.title

  let trigger = UNTimeIntervalNotificationTrigger(
    timeInterval: seconds,
    repeats: false
  )
  let request = UNNotificationRequest(
    identifier: note.id.uuidString,
    content: content,
    trigger: trigger
  )
  try await UNUserNotificationCenter.current().add(request)
}

Using the note’s ID as the request identifier is deliberate: scheduling again with the same identifier replaces the old reminder instead of stacking a duplicate.

Denial is a state, not an error. The user can refuse now and change their mind in System Settings later, or the reverse. Check the current status before scheduling and keep the feature honest:

let settings = await UNUserNotificationCenter.current()
  .notificationSettings()
if settings.authorizationStatus == .denied {
  // show a “turn on notifications in System Settings” hint
}

Now the part that trips everyone during development: schedule a reminder for five seconds, keep the app in front, and nothing appears. That is not a bug. By default macOS suppresses banners while the sending app is frontmost — the assumption is the user can already see whatever you would tell them. Switch to another app and the banner arrives. If you want banners while frontmost, implement the UNUserNotificationCenterDelegate method userNotificationCenter(_:willPresent:) and return .banner.

Verify the full flow: toggle the reminder feature, see the permission prompt appear at that moment, accept, schedule, switch to Safari, and watch the banner arrive. Then deny permission in System Settings and confirm your app shows its hint instead of pretending the reminder was scheduled.

Lesson completed

Take this course offline

Get every free book and course as PDF and EPUB files.

Get the download library →