Skip to content

Deep Linking for Local Notifications (SwiftUI)

A SwiftUI guide for coding agents. Also covers onopenurl notification tap, notification opens screen, url scheme deep link ios, unusernotificationcenter delegate, uiapplicationdelegateadaptor, navigate from push tap.

Make a notification tap navigate somewhere specific by storing a URL in the notification's userInfo, opening it from the notification delegate, and handling it with onOpenURL. One URL pipeline then serves links from Safari and from notifications alike.

Handle the URL in SwiftUI

Register a URL scheme in the target's URL types settings first, then parse incoming URLs with onOpenURL. Test by opening myapp://offer in Safari.

ContentView()
  .onOpenURL { url in
    guard let host = url.host(), host == "offer" else { return }
    offerShown = true
  }
  .sheet(isPresented: $offerShown) {
    OfferView()
  }

Put the URL in the Notification

Store the deep link as a string in userInfo when scheduling.

extension UNUserNotificationCenter {
  func addOfferNotification() {
    let content = UNMutableNotificationContent()
    content.title = String(localized: "offerTitle")
    content.body = String(localized: "offerBody")
    content.userInfo = ["url": "myapp://offer"]

    let request = UNNotificationRequest(
      identifier: "offer",
      content: content,
      trigger: UNTimeIntervalNotificationTrigger(timeInterval: 1800, repeats: false)
    )
    add(request)
  }
}

Open It From the Delegate

The notification center delegate must be set in willFinishLaunchingWithOptions, which means a real AppDelegate, bridged into SwiftUI with @UIApplicationDelegateAdaptor. didReceive fires on tap; read the URL and open it, ignoring notifications without one.

final class AppDelegate: NSObject, UIApplicationDelegate {
  func application(
    _ application: UIApplication,
    willFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]? = nil
  ) -> Bool {
    UNUserNotificationCenter.current().delegate = self
    return true
  }
}

extension AppDelegate: UNUserNotificationCenterDelegate {
  func userNotificationCenter(
    _ center: UNUserNotificationCenter,
    didReceive response: UNNotificationResponse
  ) async {
    guard
      let urlString = response.notification.request.content.userInfo["url"] as? String,
      let url = URL(string: urlString)
    else { return }
    await UIApplication.shared.open(url)
  }
}

Wire It Into the App

@main
struct MyApp: App {
  @UIApplicationDelegateAdaptor(AppDelegate.self) private var delegate
  @Environment(\.scenePhase) private var scenePhase

  var body: some Scene {
    WindowGroup { ContentView() /* onOpenURL as above */ }
      .onChange(of: scenePhase) {
        if scenePhase == .background {
          UNUserNotificationCenter.current().addOfferNotification()
        }
      }
  }
}

UIApplication.shared.open routes the tap through the same onOpenURL handler as an external link, so navigation logic lives in exactly one place.

Use this guidance in your coding agent

Install the Better Design MCP once. Your agent then loads this page with one call.

get-swiftui-guide({ topic: "swiftui-deep-linking-for-local-notifications" })
claude mcp add --scope user better-design --transport http https://better-design.com/api/mcp --header "Authorization: Bearer <YOUR_API_KEY>"
Browse related design systems