Navigation Stack and Dependancies (Part 2) Push Notification Handler

mohamed ahmed
2 min readFeb 7, 2024

--

Article Follow-Up: Utilizing the Navigator for Push Notification Navigation in SwiftUI

This article serves as a continuation of the previous installment discussing the Navigation Stack and Dependencies. If you haven’t had the chance to review Part 1, you can find it here.

Now, let’s delve into the utilization of the Navigator to handle push notification navigation in SwiftUI.

1- App Delagate :-

First, we need to create the AppDelegate and obtain an instance from NavigatorApp within it to continue our flow.

class AppDelegate: NSObject, UIApplicationDelegate, ObservableObject {

var app: NavigatorApp?

func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey : Any]? = nil) -> Bool {
initialize(application: application, launchOptions: launchOptions)
return true
}

private func initialize(application: UIApplication, launchOptions: [UIApplication.LaunchOptionsKey: Any]?) {
self.registerForNotifications(application: application)
}

func registerForNotifications(application: UIApplication){
UNUserNotificationCenter.current().delegate = self
application.registerForRemoteNotifications()
}
}

2- NavigatorApp( @main ) :-

Then, upon the onAppear event of the main view, we assign this instance to self, which represents the NavigatorApp.

@main
struct NavigatorApp: App, MainDependancyContainer {
@UIApplicationDelegateAdaptor private var appDelegate: AppDelegate
@StateObject private var navigator = Navigator.shared
var body: some Scene {
WindowGroup {
NavigationStack(path: $navigationState.mainRoutes) {
dependancyCreator(view: .firstScreen)
.navigationsConfig()
}
.onAppear {
appDelegate.app = self
}
}
}

func handelPushNotification(data: [String:Any]) async {

}

}

The line @UIApplicationDelegateAdaptor private var appDelegate: AppDelegate in Swift represents a property wrapper used to initialize and manage the AppDelegate instance in a SwiftUI app.

The function handlePushNotification(data: [String:Any]) serves as the push notification router. It will be accessed by the AppDelegate through the app instance to handle incoming push notifications effectively.

extension AppDelegate: UNUserNotificationCenterDelegate{
func userNotificationCenter(_ center: UNUserNotificationCenter, didReceive response: UNNotificationResponse) async {
let userInfo = response.notification.request.content.userInfo
guard let userInfo = userInfo as?[String:Any] else { return }
Task {
await app?.handelPushNotification(data:userInfo)
}
}

func userNotificationCenter(_ center: UNUserNotificationCenter, willPresent notification: UNNotification, withCompletionHandler completionHandler: @escaping (UNNotificationPresentationOptions) -> Void) {

return completionHandler([.list, .badge, .sound])
}
}

3- func handelPushNotification(data: [String:Any]) async :-

func handelPushNotification(data: [String:Any]) async {
let routeFinder = RouteFinder()
}

The RouteFinder() will determine the screen to navigate to based on the data fetched from the push notification.

enum PushNotificationTypes: String {
case productDetails
}
@MainActor
struct RouteFinder {
func findPushNotifications(data:[String:Any]) async -> Route? {
guard let type = data["NotificationType"] as? String else { return nil }
switch PushNotificationTypes(rawValue: type) {
case .productDetails:
guard let productID = data["Id"] as? String else { return nil }
return await ProductPushNotification.openProductDetaisl(id: productID)
}
}
}

ProductPushNotification class :-

@MainActor
class ProductPushNotification {
static func openProductDetaisl(id: String) async -> Route? {
let product = try! await getProductDetails(id: id)
guard product != nil else { return nil }
return .secondScreen(info: product)
}
}

The openProductDetails function fetches all necessary information from the backend and returns the route of the required screen.

func handelPushNotification(data: [String:Any]) async {
let routeFinder = RouteFinder()
Task {
if let route = await routeFinder.findPushNotifications(data: data) {
navigator.goTo(route)
}
}
}

the Next Article Will Show How To handle DeepLinking Using Navigator 🙂 Soon…

--

--

mohamed ahmed

Senior iOS Developer with 9 years if experience in building & Architecting Large scale mobile Applications