I am Newbie to iOS Swift. I have added LocalAuthentication(TouchID) feature in my app. When touch id is enabled I am redirecting user on Dashboard Page, on successful authentication. Now when push notification received if it is of category type blog, i wants to open blog page, if it is of category type payouts, i wants to open payout page and if it is news i wants to open notification page. But due to touch id is enable i am redirecting to Dashboard page and not on specific ViewController on notification received. Don't understand how to deal with this scenario.
What i am doing when received notification :
func navigateToView(userInfo: [AnyHashable: Any]) {
let userInfo = userInfo
if userInfo[AnyHashable("click_action")] != nil{
category = (userInfo[AnyHashable("click_action")] as? String)!
}
let userauthenticate = UserDefaults.standard.bool(forKey: "userauth")
print("USERINFOCOUNT: \(userInfo.count) \n CATEGORY : \(category) \n USERAUTH: \(userauthenticate)")
if(userauthenticate == true && category.isEmpty){
let mainStoryboardIpad : UIStoryboard = UIStoryboard(name: "Main", bundle: nil)
let initialViewControlleripad : UIViewController = mainStoryboardIpad.instantiateViewController(withIdentifier: "Dashboard") as UIViewController
self.window = UIWindow(frame: UIScreen.main.bounds)
self.window?.rootViewController = initialViewControlleripad
self.window?.makeKeyAndVisible()
} else if(userauthenticate == true && category == "notification"){
saveNewNotificationInBackground(userInfo: userInfo)
NotificationCenter.default.post(name: NSNotification.Name(rawValue: "openNotifications"), object: nil)
defaults.set(userInfo, forKey: "userInfo")
defaults.synchronize()
}
}
My authentication function is as below :
import UIKit
import Foundation
import Firebase
import FirebaseMessaging
import UserNotifications
import FirebaseCore
import FirebaseInstanceID
import Alamofire
import SwiftyJSON
import SQLite3
import LocalAuthentication
@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {
var window: UIWindow?
let gcmMessageIDKey = "gcm.message_id"
var userid : String = ""
var type : String = ""
var image : String = ""
var body : String = ""
var title : String = ""
var subtitle : String = ""
var category : String = ""
var badge : Int32 = 0
var badgecount : Int32 = 0
var aps: NSDictionary = NSDictionary()
var alert: NSDictionary = NSDictionary()
var usernamelen : Int = 0
var passwordlen : Int = 0
var notificationLists = [NotificationObj]()
var db: OpaquePointer?
let logevent = LogEvents()
var bridge: RCTBridge!
var forceUpdateCount : Int = 0
var reach: Reachability?
let defaults = UserDefaults.standard
var notificationType : String = ""
var notificationTitle : String = ""
var message : String = ""
var link : String = ""
var image_url : String = ""
var read_status : String = ""
var isfirstitmelaunch : Bool = true
var cemail : String = ""
var cpass : String = ""
var cid : String = ""
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey : Any]?) -> Bool {
if #available(iOS 10.0, *) {
UNUserNotificationCenter.current().delegate = self
let authOptions: UNAuthorizationOptions = [.alert, .badge, .sound]
UNUserNotificationCenter.current().requestAuthorization(
options: authOptions,
completionHandler: {_, _ in })
Messaging.messaging().delegate = self
} else {
let settings: UIUserNotificationSettings =
UIUserNotificationSettings(types: [.alert, .badge, .sound], categories: nil)
application.registerUserNotificationSettings(settings)
}
application.registerForRemoteNotifications()
FirebaseApp.configure()
NotificationCenter.default.addObserver(self,
selector: #selector(self.tokenRefreshNotification),
name: NSNotification.Name(rawValue: "pushNotification"),
object: nil)
UserDefaults.standard.removeObject(forKey: "checkcomingfrom")
UserDefaults.standard.set(false, forKey: "checkcomingfrom")
cemail = defaults.string(forKey: "username") ?? ""
cpass = defaults.string(forKey: "password") ?? ""
cid = defaults.string(forKey: "id") ?? ""
if(cid != nil){
logevent.logFirebaseEvent(name: "app_launch", params: ["userid" : cid])
}
if(launchOptions != nil){
let userInfo = launchOptions?[UIApplicationLaunchOptionsKey.remoteNotification]
if userInfo != nil {
print("USERINFO \(String(describing: userInfo))")
}
}
if(cemail == nil){
usernamelen = 0
}else{
usernamelen = (cemail.count)
}
if(cpass == nil){
passwordlen = 0
}else{
passwordlen = (cpass.count)
}
let firstTime = UserDefaults.standard.object(forKey: "first_time") as? Bool // Here you look if the Bool value exists, if not it means that is the first time the app is opened
if(firstTime == nil){
let mainStoryboardIpad : UIStoryboard = UIStoryboard(name: "NewCustomer", bundle: nil)
let initialViewControlleripad : UIViewController = mainStoryboardIpad.instantiateViewController(withIdentifier: "demoview") as UIViewController
self.window = UIWindow(frame: UIScreen.main.bounds)
self.window?.rootViewController = initialViewControlleripad
self.window?.makeKeyAndVisible()
defaults.set(true, forKey: "first_time")
defaults.synchronize()
}else if(usernamelen > 0 && passwordlen > 0){ // if remeber me set to true open dashboard directly
// IsFirstTimeLaunch = false
let userauthenticate = UserDefaults.standard.bool(forKey: "userauth")
print("userauthenticateforeground \(userauthenticate)")
if(userauthenticate == true){
authenticationWithTouchID()
}
else{
defaults.set(false, forKey: "first_time")
defaults.synchronize()
print("INITIALCONTROLLERISDASHBOARD")
let mainStoryboardIpad : UIStoryboard = UIStoryboard(name: "Main", bundle: nil)
let initialViewControlleripad : UIViewController = mainStoryboardIpad.instantiateViewController(withIdentifier: "Dashboard") as UIViewController
self.window = UIWindow(frame: UIScreen.main.bounds)
self.window?.rootViewController = initialViewControlleripad
self.window?.makeKeyAndVisible()
}
}else{ // else open login viewcontroller as a initial view controller
defaults.set(false, forKey: "first_time")
defaults.synchronize()
print("INITIALCONTROLLERISLOGIN")
let mainStoryboardIpad : UIStoryboard = UIStoryboard(name: "Main", bundle: nil)
let initialViewControlleripad : UIViewController = mainStoryboardIpad.instantiateViewController(withIdentifier: "NewLoginViewController") as UIViewController
self.window = UIWindow(frame: UIScreen.main.bounds)
self.window?.rootViewController = initialViewControlleripad
self.window?.makeKeyAndVisible()
}
return true
}
func application(_ application: UIApplication, didReceiveRemoteNotification userInfo: [AnyHashable: Any]) {
_ = userInfo["aps"] as? NSDictionary
NotificationCenter.default.post(name: NSNotification.Name("pushNotification"), object: nil, userInfo: userInfo)
}
//Called When Silent Push Notification Arrives
func application(_ application: UIApplication, didReceiveRemoteNotification userInfo: [AnyHashable : Any], fetchCompletionHandler completionHandler: @escaping (UIBackgroundFetchResult) -> Void) {
UIApplication.shared.applicationIconBadgeNumber = UIApplication.shared.applicationIconBadgeNumber + 1
_ = userInfo["aps"] as? NSDictionary
NotificationCenter.default.post(name: NSNotification.Name("pushNotification"), object: nil, userInfo: userInfo)
print("Message ID: \(userInfo["gcm.message_id"] ?? "")")
print("\(userInfo)")
let aps:NSDictionary = (userInfo[AnyHashable("aps")] as? NSDictionary)!
badge += 1
print("badgecount \(badge)")
if UIApplication.shared.applicationState == .inactive {
print("APPLICATIONIS INACTIVE")
//navigateToView(userInfo: userInfo)
//completionHandler(.newData)
}else if UIApplication.shared.applicationState == .background {
print("APPLICATIONIS BACKGROUND")
//navigateToView(userInfo: userInfo)
//completionHandler(UIBackgroundFetchResult.newData)
}else {
print("APPLICATIONIS FOREGROUND")
// completionHandler(UIBackgroundFetchResult.newData)
}
completionHandler(UIBackgroundFetchResult.newData)
}
func applicationReceivedRemoteMessage(_ remoteMessage: MessagingRemoteMessage) {
print("REMOTEDATA: \(remoteMessage.appData)")
}
// [END receive_message]
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
super.touchesBegan(touches, with: event)
let statusBarTappedNotification = Notification(name: Notification.Name(rawValue: "statusBarTappedNotification"))
let statusBarRect = UIApplication.shared.statusBarFrame
guard let touchPoint = event?.allTouches?.first?.location(in: self.window) else { return }
if statusBarRect.contains(touchPoint) {
NotificationCenter.default.post(statusBarTappedNotification)
}
}
func application(_ application: UIApplication, didFailToRegisterForRemoteNotificationsWithError error: Error) {
print("Unable to register for remote notifications: \(error.localizedDescription)")
}
// This function is added here only for debugging purposes, and can be removed if swizzling is enabled.
// If swizzling is disabled then this function must be implemented so that the APNs token can be paired to
// the FCM registration token.
func application(_ application: UIApplication, didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data) {
print("APNs token retrieved: \(deviceToken)")
//InstanceID.instanceID().setAPNSToken(deviceToken, type: InstanceIDAPNSTokenType.unknown)
// With swizzling disabled you must set the APNs token here.
Messaging.messaging().apnsToken = deviceToken as Data
}
func applicationWillResignActive(_ application: UIApplication) {
// Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state.
// Use this method to pause ongoing tasks, disable timers, and throttle down OpenGL ES frame rates. Games should use this method to pause the game.
print("DIDRESIGNACTIVE")
}
func applicationDidEnterBackground(_ application: UIApplication) {
print("DIDBACKGROUND")
// Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later.
// If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits.
Messaging.messaging().shouldEstablishDirectChannel = false
}
func applicationWillEnterForeground(_ application: UIApplication) {
print("applicationWillEnterForegroundcalled")
let forcemail : String = defaults.string(forKey: "username") ?? ""
let forcpass : String = defaults.string(forKey: "password") ?? ""
print("CUNAME : \(forcemail) \n CPASS : \(forcpass)")
// let userauthenticate = UserDefaults.standard.bool(forKey: "userauth")
// if(forcemail.count > 0 && forcpass.count > 0){
// if(userauthenticate == true){
// authenticationWithTouchID()
//
// }
// }
}
func applicationDidBecomeActive(_ application: UIApplication) {
print("DIDBECOMEACTIVE")
connectToFcm()
}
func applicationWillTerminate(_ application: UIApplication) {
}
func registerFcmToken(token : String, id : String, logintoken : String){
let systemVersion = UIDevice.current.systemVersion //11.4
let modelName = UIDevice.modelName //iPhone 6s plus
let systemName = UIDevice.current.systemName //iOS
let name = UIDevice.current.name //Sagar's iPhone
print("APPDELEGATEFCM : \(token)")
let url : String = "https://moneyfrog.in/register-token"
let params : [String : String] = ["user_id":id , "device_type":"ios" ,"token_no":token, "device_os_version" : systemVersion, "device_api_level" : "","device_name" : name, "device_model" : modelName, "device_product": UIDevice.current.model, "login_token" : logintoken]
Alamofire.request(url, method: .post, parameters: params).responseString{
response in
if response.result.isSuccess{
print("DEVICEINFO \(params)")
//let data : String = String(response.result.value!)
print("responseis : \(response) \n TOKENDATA ADDED")
}
}
}
// [START refresh_token]
@objc func tokenRefreshNotification(_ notification: Notification) {
if let refreshedToken = InstanceID.instanceID().token() {
print("InstanceID token: \(refreshedToken)")
}
}
// [END refresh_token]
func connectToFcm() {
// Won't connect since there is no token
guard InstanceID.instanceID().token() != nil else {
return;
}
Messaging.messaging().shouldEstablishDirectChannel = true
}
func authenticationWithTouchID() {
let localAuthenticationContext = LAContext()
localAuthenticationContext.localizedFallbackTitle = "Enter Passcode"
var _: AppDelegate? = (UIApplication.shared.delegate as? AppDelegate)
let backgrView = UIView(frame: CGRect(x: CGFloat(0), y: CGFloat(0), width: CGFloat(UIScreen.main.bounds.size.width), height: CGFloat(UIScreen.main.bounds.size.height)))
backgrView.backgroundColor = UIColor.black
//backgrView.alpha = 0.9
window?.addSubview(backgrView)
let blurEffect = UIBlurEffect(style: .light)
let blurVisualEffectView = UIVisualEffectView(effect: blurEffect)
blurVisualEffectView.frame = backgrView.bounds
backgrView.addSubview(blurVisualEffectView)
var authError: NSError?
let reasonString = "Please authenticate to use Moneyfrog"
if localAuthenticationContext.canEvaluatePolicy(.deviceOwnerAuthentication, error: &authError) {
localAuthenticationContext.evaluatePolicy(.deviceOwnerAuthentication, localizedReason: reasonString) { success, evaluateError in
if success {
DispatchQueue.main.async
{
blurVisualEffectView.removeFromSuperview()
self.navigateToView(userInfo: [:])
}
} else {
//TODO: User did not authenticate successfully, look at error and take appropriate action
guard let error = evaluateError else {
return
}
print(self.evaluateAuthenticationPolicyMessageForLA(errorCode: error._code))
//TODO: If you have choosen the 'Fallback authentication mechanism selected' (LAError.userFallback). Handle gracefully
}
}
} else {
guard let error = authError else {
return
}
//TODO: Show appropriate alert if biometry/TouchID/FaceID is lockout or not enrolled
print("APP LOCKED : \(self.evaluatePolicyFailErrorMessageForLA(errorCode: error.code))")
}
}
func evaluatePolicyFailErrorMessageForLA(errorCode: Int) -> String {
var message = ""
if #available(iOS 11.0, macOS 10.13, *) {
switch errorCode {
case LAError.biometryNotAvailable.rawValue:
message = "Authentication could not start because the device does not support biometric authentication."
case LAError.biometryLockout.rawValue:
message = "Authentication could not continue because the user has been locked out of biometric authentication, due to failing authentication too many times."
case LAError.biometryNotEnrolled.rawValue:
message = "Authentication could not start because the user has not enrolled in biometric authentication."
default:
message = "Did not find error code on LAError object"
}
} else {
switch errorCode {
case LAError.touchIDLockout.rawValue:
message = "Too many failed attempts."
case LAError.touchIDNotAvailable.rawValue:
message = "TouchID is not available on the device"
case LAError.touchIDNotEnrolled.rawValue:
message = "TouchID is not enrolled on the device"
default:
message = "Did not find error code on LAError object"
}
}
return message;
}
func evaluateAuthenticationPolicyMessageForLA(errorCode: Int) -> String {
var message = ""
switch errorCode {
case LAError.authenticationFailed.rawValue:
message = "The user failed to provide valid credentials"
case LAError.appCancel.rawValue:
message = "Authentication was cancelled by application"
case LAError.invalidContext.rawValue:
message = "The context is invalid"
case LAError.notInteractive.rawValue:
message = "Not interactive"
case LAError.passcodeNotSet.rawValue:
message = "Passcode is not set on the device"
case LAError.systemCancel.rawValue:
message = "Authentication was cancelled by the system"
case LAError.userCancel.rawValue:
message = "The user did cancel"
exit(0);
case LAError.userFallback.rawValue:
message = "The user chose to use the fallback"
default:
message = evaluatePolicyFailErrorMessageForLA(errorCode: errorCode)
}
return message
}
}
// [START ios_10_message_handling]
@available(iOS 10, *)
extension AppDelegate : UNUserNotificationCenterDelegate {
func userNotificationCenter(_ center: UNUserNotificationCenter,
willPresent notification: UNNotification,
withCompletionHandler completionHandler: @escaping (UNNotificationPresentationOptions) -> Void) {
_ = notification.request.content.userInfo
//let userInfo = notification.request.content.userInfo
// navigateToView(userInfo: userInfo)
completionHandler([.alert, .badge, .sound])
print("NotificationCenter1 called")
}//userNotificationCenter
func userNotificationCenter(_ center: UNUserNotificationCenter,
didReceive response: UNNotificationResponse,
withCompletionHandler completionHandler: @escaping () -> Void) {
print("NotificationReceived \(response.notification.request.content.userInfo)")
print("STEP NOTIFICATIONCALLED")
let userInfo = response.notification.request.content.userInfo
navigateToView(userInfo: userInfo)
completionHandler()
}//userNotificationCenter2
/* navigate to specific view controller on notification received */
func navigateToView(userInfo: [AnyHashable: Any]) {
let userInfo = userInfo
if userInfo[AnyHashable("click_action")] != nil{
category = (userInfo[AnyHashable("click_action")] as? String)!
}
let userauthenticate = UserDefaults.standard.bool(forKey: "userauth")
print("USERINFOCOUNT: \(userInfo.count) \n CATEGORY : \(category) \n USERAUTH: \(userauthenticate)")
if(userauthenticate == true && category.isEmpty){
let mainStoryboardIpad : UIStoryboard = UIStoryboard(name: "Main", bundle: nil)
let initialViewControlleripad : UIViewController = mainStoryboardIpad.instantiateViewController(withIdentifier: "Dashboard") as UIViewController
self.window = UIWindow(frame: UIScreen.main.bounds)
self.window?.rootViewController = initialViewControlleripad
self.window?.makeKeyAndVisible()
}
// else if(userauthenticate == true && category == "payout_alerts"){ // if notification is payout alert open payout alerts controller
//
//
// // post a notification
//
// NotificationCenter.default.post(name: NSNotification.Name(rawValue: "openPayoutAlerts"), object: nil)
//
// self.logevent.logFirebaseEvent(name: "notification_payout", params: ["userid": self.userid])
// /*this logs database events in the system*/
// let params : [String : String] = ["action" : "notification_payout" , "page" : "payout_alerts_screen" , "user_id" : self.userid, "source" : "iOS"] //post params
// self.logevent.logDatabaseEvents(parameters: params)
//
//
//
// }else if(userauthenticate == true && category == "blog"){ // if notification is blogs alert open blogs controller
//
// print("OPENING BLOGS")
//
// NotificationCenter.default.post(name: NSNotification.Name(rawValue: "OpenBlogs"), object: nil)
//
// self.logevent.logFirebaseEvent(name: "notification_blog", params: ["userid": self.userid])
// /*this logs database events in the system*/
// let params : [String : String] = ["action" : "notification_blog" , "page" : "blogs_screen" , "user_id" : self.userid, "source" : "iOS"] //post params
// self.logevent.logDatabaseEvents(parameters: params)
//
// }else if(userauthenticate == true && category == "chat"){ // if notification is mypost alert open My post controller
//
// NotificationCenter.default.post(name: NSNotification.Name(rawValue: "openMypostContoller"), object: nil)
//
// self.logevent.logFirebaseEvent(name: "notification_mypost", params: ["userid": self.userid])
// /*this logs database events in the system*/
// let params : [String : String] = ["action" : "notification_mypost" , "page" : "mypost_screen" , "user_id" : self.userid, "source" : "iOS"] //post params
// self.logevent.logDatabaseEvents(parameters: params)
//
// }
else if(userInfo.count > 0 && category == "notification"){ // if notification is ingeneral notification open notification controller
// saveNewNotificationInBackground(userInfo: userInfo)
//
// NotificationCenter.default.post(name: NSNotification.Name(rawValue: "openNotifications"), object: nil)
//
//
// defaults.set(userInfo, forKey: "userInfo")
// defaults.synchronize()
if let controller = UIStoryboard(name: "Main", bundle: nil).instantiateViewController(withIdentifier: "Notification") as? Notifications {
if let navigator = self.window?.rootViewController as? UINavigationController {
navigator.pushViewController(controller, animated: true)
}
}
self.logevent.logFirebaseEvent(name: "notification_received", params: ["userid": self.userid])
/*this logs database events in the system*/
let params : [String : String] = ["action" : "notification_received" , "page" : "notification_screen" , "user_id" : self.userid, "source" : "iOS"] //post params
self.logevent.logDatabaseEvents(parameters: params)
}
}
/*this function save data in NotificationDatabase.sqlite database and Notifications table*/
func saveNewNotificationInBackground(userInfo: [AnyHashable: Any]) -> Void {
//save notification using sqlite data
}
}
// [END ios_10_message_handling]
extension AppDelegate : MessagingDelegate {
// [START refresh_token]
func messaging(_ messaging: Messaging, didReceiveRegistrationToken fcmToken: String) {
print("Firebasetoken: \(fcmToken)")
Messaging.messaging().subscribe(toTopic: "blog") { error in
print("subscribed to blog topic")
}
Messaging.messaging().subscribe(toTopic: "news") { error in
print("subscribed to news topic")
}
let defaults = UserDefaults.standard
defaults.set(fcmToken, forKey: "token")
defaults.synchronize()
let dataDict:[String: String] = ["token": fcmToken]
NotificationCenter.default.post(name: Notification.Name("FCMToken"), object: nil, userInfo: dataDict)
let id : String? = defaults.string(forKey: "id")
let logintoken : String? = defaults.string(forKey: "login_token")
if(id != nil && logintoken != nil){
registerFcmToken(token: fcmToken, id: id!, logintoken: logintoken!)
}
}
func messaging(_ messaging: Messaging, didReceive remoteMessage: MessagingRemoteMessage) {
Messaging.messaging().shouldEstablishDirectChannel = true
print("ReceivedDataMessage: \(remoteMessage.appData)")
}
// [END ios_10_data_message]
}