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.

Notifications interrupt people. macOS gates them behind a permission prompt, and users have trained themselves to click “Don’t Allow” on prompts they don’t 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, like “Remind me about this note”, and the prompt explains itself.

Request permission

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.

Schedule a notification

Sending one is a separate step. You build content, a trigger, and a 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
}

Don’t pretend the reminder was scheduled when it wasn’t. Tell the user where to flip the switch.

Why nothing appears during development

Here’s the part that trips everyone. Schedule a reminder for five seconds, keep the app in front, and nothing shows up. That’s not a bug.

By default macOS suppresses banners while the sending app is frontmost. The assumption is that 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 and 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 failing silently.

Lesson completed