Greetings, traveler!
Handling app navigation triggered by system events like local notifications is a common requirement. In SwiftUI, achieving this in a clean and reliable way requires a slightly different approach compared to UIKit. This article presents a minimal solution for switching to a specific tab in a TabView
when a user taps on a local notification.
Use Case
You want to deliver a local notification with context—perhaps prompting the user to review their profile, check new settings, or return to the home tab. When the user taps on the notification, the app should open and focus the appropriate tab.
Using UNUserNotificationCenterDelegate in SwiftUI
To handle notification taps, we need to use UNUserNotificationCenterDelegate
. Although SwiftUI does not directly expose this API, we can bridge it using UIApplicationDelegateAdaptor
:
final class AppDelegate: NSObject, UIApplicationDelegate, UNUserNotificationCenterDelegate {
func application(
_ application: UIApplication,
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]? = nil
) -> Bool {
UNUserNotificationCenter.current().delegate = self
return true
}
func userNotificationCenter(
_ center: UNUserNotificationCenter,
didReceive response: UNNotificationResponse,
withCompletionHandler completionHandler: @escaping () -> Void
) {
if let tab = response.notification.request.content.userInfo["tab"] as? String {
NotificationCenter.default.post(name: .navigateToTab, object: tab)
}
completionHandler()
}
}
The delegate extracts the "tab"
value from the notification payload and publishes it using NotificationCenter
.
In your @main
app declaration, attach the delegate:
@main
struct MyApp: App {
@UIApplicationDelegateAdaptor(AppDelegate.self) var appDelegate
var body: some Scene {
WindowGroup {
MainTabView()
}
}
}
Switching Tabs in SwiftUI
Instead of introducing a dedicated router or dependency injection, this solution keeps things simple. The tab selection is handled via a @State
property and a standard .onReceive
modifier.
enum TabKind: String {
case home, settings, profile
}
struct MainTabView: View {
@State private var selectedTab: TabKind = .home
var body: some View {
TabView(selection: $selectedTab) {
HomeView()
.tabItem { Label("Home", systemImage: "house") }
.tag(TabKind.home)
SettingsView()
.tabItem { Label("Settings", systemImage: "gear") }
.tag(TabKind.settings)
ProfileView()
.tabItem { Label("Profile", systemImage: "person") }
.tag(TabKind.profile)
}
.onReceive(NotificationCenter.default.publisher(for: .navigateToTab)) { notification in
if let tabString = notification.object as? String,
let tab = TabKind(rawValue: tabString) {
selectedTab = tab
}
}
}
}
To complete the setup, define the notification name used:
extension Notification.Name {
static let navigateToTab = Notification.Name("navigateToTab")
}
Attaching Metadata to the Notification
When scheduling the local notification, include the target tab in the userInfo
dictionary:
let content = UNMutableNotificationContent()
content.title = "Go to Profile"
content.body = "Tap to view your profile"
content.userInfo = ["tab": "profile"]
let trigger = UNTimeIntervalNotificationTrigger(timeInterval: 10, repeats: false)
let request = UNNotificationRequest(identifier: UUID().uuidString, content: content, trigger: trigger)
UNUserNotificationCenter.current().add(request)
This ensures the tab identifier is passed through when the notification is received.
Beyond This Example: Coordinators
For more advanced navigation flows, this approach may eventually hit its limits. When dealing with stacked views, deep-linking, or shared navigation logic across modules, adopting a Coordinator pattern is a better long-term strategy. In that setup, the coordinator layer subscribes to app-level events (like notifications) and coordinates navigation accordingly.
Note
I’ve been documenting various techniques around Coordinator-based navigation in SwiftUI. If you’re building a medium or large application, you may find this series useful: https://livsycode.com/tag/swiftui-coordinator/
Conclusion
With a few lines of code, SwiftUI apps can respond to local notification taps by updating tab selection. This lightweight solution works well for apps with straightforward navigation needs. For projects with more complexity, consider using a dedicated navigation layer such as a SwiftUI Coordinator.
If you enjoyed this article, please feel free to follow me on my social media: