Search code examples
iospush-notificationswift3xcode8remote-notifications

Facing trouble with viewing push notification in iOS 10 Swift 3


My app has 2 types of initial state.

  1. Login
  2. After Login which contains Dashboard & other stuff.

My app working fine when switching view from Appdelegate like that

 func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
        let userDataExist = CommonUserFunction.isUserDataExist() as Bool

        if userDataExist == true {

            let homeViewController = self.window?.rootViewController?.storyboard?.instantiateViewController(withIdentifier: "HomeView") as? HomeViewController
            let navigationController = UINavigationController()
            navigationController.viewControllers = [homeViewController!]

            self.window!.rootViewController = navigationController
        }
        else{

            let loginController = self.window?.rootViewController?.storyboard?.instantiateViewController(withIdentifier: "LoginView") as? LoginController
            let navigationController = UINavigationController()
            navigationController.viewControllers = [loginController!]

            self.window!.rootViewController = navigationController
        }

        self.window?.makeKeyAndVisible()

        return true
 }

I am facing problem when i have to handle push notification. I received push notification in iOS 10 in these methods

@available(iOS 10.0, *)
func userNotificationCenter(_ center: UNUserNotificationCenter,  willPresent notification: UNNotification, withCompletionHandler   completionHandler: @escaping (_ options:   UNNotificationPresentationOptions) -> Void) {
    print("Handle push from foreground")
    // custom code to handle push while app is in the foreground

    print(notification.request.content.userInfo)
}

@available(iOS 10.0, *)
func userNotificationCenter(_ center: UNUserNotificationCenter, didReceive response: UNNotificationResponse, withCompletionHandler completionHandler: @escaping () -> Void) {

   // custom code to handle push while app is in the foreground
    print(response.notification.request.content.userInfo)
}

When i tapped notification from notification tray these two methods are fired whereas app is foreground or background. After tapping the notification i must show a details page. I tried to set the details page like that which i did before in didFinishLaunchingWithOptions method

@available(iOS 10.0, *)
func userNotificationCenter(_ center: UNUserNotificationCenter, didReceive response: UNNotificationResponse, withCompletionHandler completionHandler: @escaping () -> Void) {

    let pushResponse = response.notification.request.content.userInfo as NSDictionary

    let rtype = pushResponse.object(forKey: "rtype") as? String
    let rid = pushResponse.object(forKey: "rid") as? String

    if rtype != nil {

        if rtype == "5" {

            if rid != nil {
                 let noticeDetailsViewController = self.storyboard?.instantiateViewController(withIdentifier: "NoticeDetailsView") as? NoticeDetailsController
                 noticeDetailsViewController!.id = rtype!
                 noticeDetailsViewController?.fromPush = true
                 let navigationController = UINavigationController()
                 navigationController.viewControllers = [noticeDetailsViewController!]
                 self.window!.rootViewController = navigationController
            }
        }
    }
}

Every time i do that app crashes. How to overcome this. Please help. Thanks in advance.


Solution

  • Finally solved this using Observer design pattern.

    When app is in Background & tapped the the notification in notification tray then post the notification from here like that

    @available(iOS 10.0, *)
    func userNotificationCenter(_ center: UNUserNotificationCenter, didReceive response: UNNotificationResponse, withCompletionHandler completionHandler: @escaping () -> Void) {
    
        let pushResponse = response.notification.request.content.userInfo as NSDictionary
    
        let rtype = pushResponse.object(forKey: "rtype") as? String
        let rid = pushResponse.object(forKey: "rid") as? String
    
        if rtype != nil {
    
            if rtype == "5" {
    
                if rid != nil {
    
                    NotificationCenter.default.post(name: Notification.Name(rawValue: "getPush"), object: pushResponse)
                }
            }
        }
    }
    

    To receive this notification init below code to your viewController

    NotificationCenter.default.addObserver(self, selector: #selector(self.getPushResponse), name: NSNotification.Name(rawValue: "getPush"), object: nil)
    

    Get response from the below code

    func getPushResponse(_ notification: Notification){
    
        if let info = notification.object as? NSDictionary {
            let rid = info.object(forKey: "rid") as? String
            let noticeDetailsViewController = self.storyboard?.instantiateViewController(withIdentifier: "NoticeDetailsView") as? NoticeDetailsController
            noticeDetailsViewController?.id = rid!
            noticeDetailsViewController?.fromPush = true
            self.navigationController?.pushViewController(noticeDetailsViewController!, animated: true)
        }
    }
    

    Don't forget to Remove Observer from the viewController like that

    deinit {
        NotificationCenter.default.removeObserver(self)
    }
    

    If app has no instance in background & tapped the notification in tray then how to show the notification which has no instance. This can be handle like that in AppDelegate

    let prefs: UserDefaults = UserDefaults.standard
        if let remoteNotification = launchOptions?[UIApplicationLaunchOptionsKey.remoteNotification] as? NSDictionary {
            prefs.set(remoteNotification as! [AnyHashable: Any], forKey: "startUpPush")
            prefs.synchronize()
        }
    

    Check your initial viewController is this key exist or not like that

    let prefs:UserDefaults = UserDefaults.standard
        if prefs.value(forKey: "startUpPush") != nil {
    
            let pushResponse = prefs.object(forKey: "startUpPush") as! NSDictionary
            NotificationCenter.default.post(name: Notification.Name(rawValue: "getPush"), object: pushResponse)
        }
    

    Don't forget to remove this key after showing the detailsController like that

    if fromPush == true {
            let prefs: UserDefaults = UserDefaults.standard
            if prefs.value(forKey: "startUpPush") != nil {
                prefs.removeObject(forKey: "startUpPush")
                prefs.synchronize()
            }
        }