How do I open a specific view controller from local notification? - ios

On pressing a button on the local notification, I want to be able to open a specific view controller depending on some information in the notification. How can this be done?
In AppDelegate.swift
let notificationSettings = UIUserNotificationSettings(forTypes: [.Alert, .Badge, .Sound], categories: nil)
UIApplication.sharedApplication().registerUserNotificationSettings(notificationSettings)
if let options = launchOptions {
if let notification = options[UIApplicationLaunchOptionsLocalNotificationKey] as? UILocalNotification {
if let userInfo = notification.userInfo {
let type = userInfo["TYPE"] as! String
// do something neat here
if (type == "SLEEP") {
} else if (type == "STRESS") {
} else {
}
}
}
}
return true
}
Setting the notifications in one of the view controllers
let sleepInPrefs = prefs.valueForKey(key) as? NSDate
if sleepInPrefs != nil {
print(sleepInPrefs)
print("Setting stress notif")
for notification in (UIApplication.sharedApplication().scheduledLocalNotifications )! {
if (notification.userInfo!["TYPE"]) != nil {
if (notification.userInfo!["TYPE"] as! String == key) {
UIApplication.sharedApplication().cancelLocalNotification(notification)
print("deleting notif")
break
}
}
}
let notification = UILocalNotification()
notification.fireDate = sleepInPrefs
notification.repeatInterval = NSCalendarUnit.Day
notification.timeZone = NSCalendar.currentCalendar().timeZone
notification.alertBody = ""
notification.hasAction = true
notification.alertAction = "open"
notification.soundName = UILocalNotificationDefaultSoundName
notification.userInfo = ["TYPE": key ]
notification.category = "PROMPT"
UIApplication.sharedApplication().scheduleLocalNotification(notification)
}

You can pass redirection params and additional info in userInfo of UILocalNotification.
// In iOS 8.0 and later, your application must register for user notifications using -[UIApplication registerUserNotificationSettings:] before being able to schedule and present UILocalNotifications
func registerForLocaleNotifications()
{
let settings = UIUserNotificationSettings(forTypes: [.Alert, .Badge , .Sound], categories: nil)
UIApplication.sharedApplication().registerUserNotificationSettings(settings)
}
func scheduleLocalNotification()
{
let notification = UILocalNotification()
notification.fireDate = NSDate(timeIntervalSinceNow: 20)//will be fired in 20 seconds
notification.timeZone = NSTimeZone.defaultTimeZone()
notification.soundName = UILocalNotificationDefaultSoundName
notification.alertBody = "Test UILocalNotification"
notification.userInfo = ["TYPE":"Page1"]
UIApplication.sharedApplication().scheduleLocalNotification(notification)
}
func application(application: UIApplication, didReceiveLocalNotification notification: UILocalNotification)
{
if ( application.applicationState == UIApplicationState.Active)
{
print("Active")
// App is foreground and notification is recieved,
// Show a alert.
}
else if( application.applicationState == UIApplicationState.Background)
{
print("Background")
// App is in background and notification is received,
// You can fetch required data here don't do anything with UI.
}
else if( application.applicationState == UIApplicationState.Inactive)
{
print("Inactive")
// App came in foreground by used clicking on notification,
// Use userinfo for redirecting to specific view controller.
self.redirectToPage(notification.userInfo)
}
}
func redirectToPage(userInfo:[NSObject : AnyObject]!)
{
var viewControllerToBrRedirectedTo:UIViewController!
if userInfo != nil
{
if let pageType = userInfo["TYPE"]
{
if pageType as! String == "Page1"
{
viewControllerToBrRedirectedTo = UIViewController() // creater specific view controller
}
}
}
if viewControllerToBrRedirectedTo != nil
{
if self.window != nil && self.window?.rootViewController != nil
{
let rootVC = self.window?.rootViewController!
if rootVC is UINavigationController
{
(rootVC as! UINavigationController).pushViewController(viewControllerToBrRedirectedTo, animated: true)
}
else
{
rootVC?.presentViewController(viewControllerToBrRedirectedTo, animated: true, completion: { () -> Void in
})
}
}
}
}

Related

Thread Error in Xcode w/ Swift When Launching New Screen

Once the user logs into my app, I set the window, set the rootViewController and makeKeyAndVisible and when I do I get a Thread 9: signal SIGABRT error. FWIW, I get a purple warning -[UIWindow initWithFrame:] must be used from main thread only when setting self.window = UIWindow(frame: UIScreen.main.bounds). See code below to see my setup.
The code dies with that error right here - self.window!.makeKeyAndVisible() in the launchWindow(aWindow: UIWindow?) function below in AppController.swift.
AppDelegate.swift
//
// AppDelegate.swift
import UIKit
import AWSCognitoAuth
import AWSSNS
import AWSCognitoIdentityProvider
import UserNotifications
import ESTabBarController_swift
import AWSMobileClient
#UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterDelegate {
var window: UIWindow?
var navigationController: UINavigationController?
var storyboard: UIStoryboard?
var loginViewController: LoginViewController?
var pool = AWSCognitoIdentityUserPool.init(forKey: "UserPool")
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
AppController.sharedInstance.enableCognitoClientWithAuthentication()
// setup logging
// pool.delegate = self
// AWSDDLog.sharedInstance.logLevel = .verbose
// let credentialsProvider = AWSCognitoCredentialsProvider(regionType: .USEast1,
// identityPoolId: Constants.APIKeys.AWSIdentityPoolID)
//
// // setup service configuration
// let serviceConfiguration = AWSServiceConfiguration(region: AWSRegionType.USEast1, credentialsProvider: credentialsProvider)
//
// // create pool configuration
// let poolConfiguration = AWSCognitoIdentityUserPoolConfiguration(clientId: Constants.APIKeys.AWSClientID,
// clientSecret: Constants.APIKeys.AWSSecret,
// poolId: Constants.APIKeys.AWSPoolID)
// AWSServiceManager.default().defaultServiceConfiguration = serviceConfiguration
// // initialize user pool client
// AWSCognitoIdentityUserPool.register(with: serviceConfiguration, userPoolConfiguration: poolConfiguration, forKey: "UserPool")
//
// pool.currentUser()?.getSession()
// fetch the user pool client we initialized in above step
//let pool = AWSCognitoIdentityUserPool(forKey: "UserPool")
let signedIn = AWSMobileClient.sharedInstance().isSignedIn
self.navigationController = UINavigationController()
if !signedIn {
navigationInit()
}
// } else {
// AppController.sharedInstance.showLoggedInStateAndReturn(true)
// }
//pool.delegate = self
self.window = UIWindow(frame: UIScreen.main.bounds)
AppController.sharedInstance.launchInWindow(aWindow: self.window)
return true
}
func application(_ app: UIApplication, open url: URL, options: [UIApplication.OpenURLOptionsKey : Any] = [:]) -> Bool {
navigationInit()
return true
}
func navigationInit() {
let loginViewController = LoginViewController()
self.navigationController!.pushViewController(loginViewController, animated: false)
}
//MARK: Push Notification
func application(_ application: UIApplication, didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data) {
/// Attach the device token to the user defaults
var token = ""
for i in 0..<deviceToken.count {
token = token + String(format: "%02.2hhx", arguments: [deviceToken[i]])
}
print(token)
UserDefaults.standard.set(token, forKey: "deviceTokenForSNS")
/// Create a platform endpoint. In this case, the endpoint is a
/// device endpoint ARN
let sns = AWSSNS.default()
let request = AWSSNSCreatePlatformEndpointInput()
request?.token = token
request?.platformApplicationArn = Constants.APIKeys.AWSSSNSARN
sns.createPlatformEndpoint(request!).continueWith(executor: AWSExecutor.mainThread(), block: { (task: AWSTask!) -> AnyObject? in
if task.error != nil {
print("Error: \(String(describing: task.error))")
} else {
let createEndpointResponse = task.result! as AWSSNSCreateEndpointResponse
if let endpointArnForSNS = createEndpointResponse.endpointArn {
print("endpointArn: \(endpointArnForSNS)")
Settings.setPushArn(endpointArnForSNS)
NotificationCenter.default.post(name: NSNotification.Name(rawValue: "RegisteredForPush"), object: nil)
}
}
return nil
})
}
func application(_ application: UIApplication, didReceiveRemoteNotification userInfo: [AnyHashable : Any], fetchCompletionHandler completionHandler: #escaping (UIBackgroundFetchResult) -> Void) {
let visible = window?.visibleViewController()
if let data = userInfo["aps"] as? [AnyHashable: Any] {
if let route = data["route"] as? String {
switch route {
case "matchRequested":
var projectId = ""
if let project = data["projectId"] as? String {
projectId = project
}
var matchId = ""
if let match = data["matchId"] as? String {
matchId = match
}
let projectMatches = MatchRequestedViewController(withNotificationPayload: projectId, matchId: matchId)
visible?.navigationController?.pushViewController(projectMatches, animated: true)
case "projectDetails":
var projectId = ""
if let project = data["projectId"] as? String {
projectId = project
}
let projectMatches = ProjectDetailsViewController(withProject: TERMyProject(), orProjectId: projectId)
visible?.navigationController?.pushViewController(projectMatches, animated: true)
case "matched":
var projectId = ""
if let project = data["projectId"] as? String {
projectId = project
}
var matchId = ""
if let match = data["matchId"] as? String {
matchId = match
}
var originProject: TERMyProject = TERMyProject()
var matchedProject: TERMatchedProject = TERMatchedProject()
AppController.sharedInstance.AWSClient?.projectsGet(id:projectId).continueWith(block: { (task: AWSTask) -> Any? in
if let error = task.error {
print("Error: \(error)")
} else if let result = task.result {
if result is NSDictionary {
DispatchQueue.main.async {
let array = [result]
let parsedProject: [TERMyProject] = TERMyProject.parseFromAPI(array:array as! [NSDictionary])
for project in parsedProject {
originProject = project
}
// self.initialSetup()
}
AppController.sharedInstance.AWSClient?.projectsGet(id:matchId).continueWith(block: { (task: AWSTask) -> Any? in
if let error = task.error {
print("Error: \(error)")
} else if let result = task.result {
if result is NSDictionary {
DispatchQueue.main.async {
let array = [result]
let parsedProject: [TERMatchedProject] = TERMatchedProject.parseFromAPI(array:array as! [NSDictionary])
for project in parsedProject {
matchedProject = project
}
let projectMatches = MatchedViewController(withProject: originProject, match: matchedProject, isComplete: false)
visible?.navigationController?.pushViewController(projectMatches, animated: true)
}
}
}
return nil
})
}
}
return nil
})
default:
break
}
}
}
}
func application(_ application: UIApplication, didFailToRegisterForRemoteNotificationsWithError error: Error) {
print(error.localizedDescription)
}
// Called when a notification is delivered to a foreground app.
#available(iOS 10.0, *)
func userNotificationCenter(_ center: UNUserNotificationCenter, willPresent notification: UNNotification, withCompletionHandler completionHandler: #escaping (UNNotificationPresentationOptions) -> Void) {
print("User Info = ",notification.request.content.userInfo)
completionHandler([.alert, .badge, .sound])
}
//MARK: Boiler-plate methods
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 invalidate graphics rendering callbacks. Games should use this method to pause the game.
}
func applicationDidEnterBackground(_ application: UIApplication) {
// 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.
}
func applicationWillEnterForeground(_ application: UIApplication) {
// Called as part of the transition from the background to the active state; here you can undo many of the changes made on entering the background.
}
func applicationDidBecomeActive(_ application: UIApplication) {
}
func applicationWillTerminate(_ application: UIApplication) {
// Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:.
}
}
extension AppDelegate: AWSCognitoIdentityInteractiveAuthenticationDelegate {
// func startPasswordAuthentication() -> AWSCognitoIdentityPasswordAuthentication {
// if (self.navigationController == nil) {
//
// self.navigationController = self.storyboard?.instantiateViewController(withIdentifier: "LoginViewController") as? UINavigationController
// }
//
// if (self.loginViewController == nil) {
// self.loginViewController = self.navigationController?.viewControllers[0] as? LoginViewController
// }
//
// DispatchQueue.main.async {
// self.navigationController!.popToRootViewController(animated: true)
// if (!self.navigationController!.isViewLoaded
// || self.navigationController!.view.window == nil) {
// self.window?.rootViewController?.present(self.navigationController!,
// animated: true,
// completion: nil)
// }
//
// }
// return self.loginViewController!
// }
}
// MARK:- AWSCognitoIdentityRememberDevice protocol delegate
extension AppDelegate: AWSCognitoIdentityRememberDevice {
func didCompleteStepWithError(_ error: Error?) {
}
func getRememberDevice(_ rememberDeviceCompletionSource: AWSTaskCompletionSource<NSNumber>) {
}
}
extension UIWindow {
func visibleViewController() -> UIViewController? {
if let rootViewController: UIViewController = self.rootViewController {
return UIWindow.getVisibleViewControllerFrom(vc: rootViewController)
}
return nil
}
class func getVisibleViewControllerFrom(vc:UIViewController) -> UIViewController {
switch(vc){
case is UINavigationController:
let navigationController = vc as! UINavigationController
return UIWindow.getVisibleViewControllerFrom( vc: navigationController.visibleViewController!)
break;
case is UITabBarController:
let tabBarController = vc as! UITabBarController
return UIWindow.getVisibleViewControllerFrom(vc: tabBarController.selectedViewController!)
break;
default:
if let presentedViewController = vc.presentedViewController {
//print(presentedViewController)
if let presentedViewController2 = presentedViewController.presentedViewController {
return UIWindow.getVisibleViewControllerFrom(vc: presentedViewController2)
}
else{
return vc;
}
}
else{
return vc;
}
break;
}
}
}
AppController.swift
func launchInWindow(aWindow: UIWindow?){
self.window = aWindow
self.initializeSDKs()
self.globalCustomization()
self.AWSUnAuthedClient.apiKey = Constants.APIKeys.AWSAPIKey
self.window!.rootViewController = self.showLoggedInStateAndReturn(true)
self.window!.makeKeyAndVisible()
}
func initializeSDKs() {
// Google places
GMSPlacesClient.provideAPIKey(Constants.APIKeys.GooglePlaces)
}
func globalCustomization() {
self.styleNavigationBar()
}
#discardableResult func showLoggedInStateAndReturn(_ shouldReturn: Bool) -> UIViewController? {
//AppController.sharedInstance.enableCognitoClientWithAuthentication()
//self.registerForPush()
self.tabBarController = ESTabBarController()
//tabBarController.delegate = delegate
self.tabBarController?.title = "Irregularity"
self.tabBarController?.tabBar.shadowImage = UIImage.image(with: UIColor("FFFFFF", alpha: 0.0)!)
self.tabBarController?.tabBar.backgroundImage = UIImage.image(with: UIColor("2A2A27")!)
self.tabBarController?.shouldHijackHandler = {
tabbarController, viewController, index in
if index == 1 {
return true
}
return false
}
self.tabBarController?.didHijackHandler = {
[weak tabBarController] tabbarController, viewController, index in
DispatchQueue.main.asyncAfter(deadline: .now() + 0.2) {
let newProjectNavCon = UINavigationController(rootViewController: NewProjectViewController())
newProjectNavCon.hero.isEnabled = true
newProjectNavCon.setNavigationBarHidden(true, animated: false)
newProjectNavCon.hero.navigationAnimationType = .fade
tabBarController?.present(newProjectNavCon, animated: true, completion: nil)
}
}
let centerVC = UINavigationController(rootViewController: HomeViewController())
let v1 = centerVC
let v2 = BaseViewController()
let v3 = UINavigationController(rootViewController: ProfileViewController())
v1.tabBarItem = ESTabBarItem.init(TabBarBouncesContentView(), title: "Projects", image: UIImage(named: "tabBar"), selectedImage: UIImage(named: "tabBar"))
v2.tabBarItem = ESTabBarItem.init(TabBarIrregularityContentView(), title: nil, image: UIImage(named: "tabBarPlusButton"), selectedImage: UIImage(named: "tabBarPlusButton"))
v3.tabBarItem = ESTabBarItem.init(TabBarBouncesContentView(), title: "Profile", image: UIImage(named: "tabBarProfile"), selectedImage: UIImage(named: "tabBarProfile"))
self.tabBarController?.setViewControllers([v1, v2, v3], animated: true)
if shouldReturn {
return self.tabBarController
} else {
self.window?.rootViewController = self.tabBarController
return nil
}
}
You should try to execute the piece of code in the main thread:
func launchInWindow(aWindow: UIWindow?){
self.window = aWindow
self.initializeSDKs()
self.globalCustomization()
self.AWSUnAuthedClient.apiKey = Constants.APIKeys.AWSAPIKey
DispatchQueue.main.async {
self.window!.rootViewController = self.showLoggedInStateAndReturn(true)
self.window!.makeKeyAndVisible()
}
}
Also, your question codebase looks a little bit weird in this part in AppDelegate.swift:
self.window = UIWindow(frame: UIScreen.main.bounds) //-- Purple warning here
AppController.sharedInstance.launchInWindow(aWindow: self.window)
return true
Looks like those three lines are not in a function but in class scope, which is weird and you shouldn't be able to compile it. Probably you pasted your code wrong?

How to listen to the changes in notification authorization Status

In my application, there is a setting to controller if receive notification or not. When user try to turn on receive notification but notification is turn off in system setting, it pop up a dialog to redirect user to system setting to turn on it firstly. I want to know whether user is turn on/ off notification setting after redirect and then I can do some additional task.
At viewDidLoad you can add observer to listen to notification settings status. Then do something with the latest status. Refer code below:
override func viewDidLoad() {
super.viewDidLoad()
NotificationCenter.default.addObserver(self, selector: #selector(checkNotificationStatus), name: UIApplication.didBecomeActiveNotification, object: nil)
}
#objc private func checkNotificationStatus() {
UNUserNotificationCenter.current().getNotificationSettings { (settings) in
switch settings.authorizationStatus {
case .authorized, .denied, .provisional, .notDetermined:
print("Do something according to status")
}
}
}
You can use following code for check whether app's notification setting is On/off.
func setPushPermission(){
if #available(iOS 10.0, *) {
let center = UNUserNotificationCenter.current()
center.getNotificationSettings { (settings) in
if(settings.authorizationStatus == .authorized) {
self.pushPermission = .Allowed
} else if(settings.authorizationStatus == .denied) {
self.pushPermission = .Disallowed
} else {
self.pushPermission = .UnDefined
}
}
}else{
let notificationSettings = UIApplication.shared.currentUserNotificationSettings
let status = notificationSettings?.types.contains(.alert)
if status == true {
self.pushPermission = .Allowed
}
}
}
func registerForUserNotification(){
if #available(iOS 10.0, *) {
UNUserNotificationCenter.current().requestAuthorization(options: [UNAuthorizationOptions.alert, UNAuthorizationOptions.sound, UNAuthorizationOptions.badge]) { (willAllow: Bool, error: Error?) in
if willAllow == true
{
self.pushPermission = .Allowed
//[[UIApplication sharedApplication] registerForRemoteNotifications];
// UIApplication.shared.registerForRemoteNotifications()
}else{
self.pushPermission = .Disallowed
}
NotificationCenter.default.post(name: NSNotification.Name("PushPermissionChaged"), object: nil)
}
UNUserNotificationCenter.current().delegate = self
}else{
let userNotificationSettings = UIUserNotificationSettings(types: [.alert, .badge, .sound], categories: nil)
// Register User Notification Settings
UIApplication.shared.registerUserNotificationSettings(userNotificationSettings)
self.pushPermission = .Allowed
}
}
iOS10+ RxSwift solution to listen auth status.
It works for manual changes at settings and accept/decline when access was asked
extension Reactive where Base: UNUserNotificationCenter {
var isAuthorized: Observable<Bool> {
UIApplication.shared.rx.applicationDidBecomeActive
.flatMap { [base] _ -> Observable<Bool> in
Observable.create { observer in
base.getNotificationSettings(completionHandler: { (settings: UNNotificationSettings) in
guard settings.authorizationStatus == .authorized else {
observer.onNext(false)
return
}
observer.onNext(true)
})
return Disposables.create()
}
}
}
}
upd: need to use RxAppState github.com/pixeldock/RxAppState library.

Unable to navigate to specific viewController after clicking push notification in foreground using FCM below iOS 10

I am trying to get the notification in foreground as well as in background, i am getting notification in both states, but i'm unable to navigate it to desired view controller after tapping the notification when app is in foreground, Any help will be highly appreciated, thanks in advance
i am using third party for custom banner, running on iphone 4s device, ios 9
func application(_ application: UIApplication, didReceiveRemoteNotification userInfo: [AnyHashable: Any]) {
// If you are receiving a notification message while your app is in the background,
// this callback will not be fired till the user taps on the notification launching the application.
// TODO: Handle data of notification
// With swizzling disabled you must let Messaging know about the message, for Analytics
Messaging.messaging().appDidReceiveMessage(userInfo)
print("In did Recieve Notification")
// Print message ID.
print("userInfoNotification=\(userInfo)")
if let contactID = userInfo["contactID"] as? String {
self.contactID = contactID
print(contactID)
}
let state = UIApplication.shared.applicationState
if state == .active {
print("App in Foreground")
if self.contactID != AppDelegate.openedChatContactId {
print("openedID=\(AppDelegate.openedChatContactId)")
if let aps = userInfo["aps"] as? NSDictionary {
if let alert = aps["alert"] as? NSDictionary {
let body = alert["body"] as! String
let title = alert["title"] as! String
let banner = Banner(title: title, subtitle: body, image: UIImage(named: "AppIcon"), backgroundColor: UIColor(red:31.00/255.0, green:136.0/255.0, blue:254.5/255.0, alpha:1.000))
banner.dismissesOnTap = true
banner.show(duration: 3.0)
// let storyboard = UIStoryboard(name: "Main", bundle: nil)
// let viewController = storyboard.instantiateViewController(withIdentifier: "chatMessageVC") as! ChatMessagesVC
// UIApplication.shared.keyWindow?.rootViewController = viewController;
} else if let alert = aps["alert"] as? NSString {
}
}
}
}
if state == .inactive || state == .background {
let storyboard = UIStoryboard(name: "Main", bundle: nil)
var destinationViewController = storyboard.instantiateViewController(withIdentifier: "chatMessageVC") as! ChatMessagesVC
UserDefaults.standard.set(contactID, forKey: "contactID")
UserDefaults.standard.synchronize()
destinationViewController.contactID = self.contactID
let navigationController = self.window?.rootViewController as! UINavigationController
navigationController.pushViewController(destinationViewController, animated: false)
}
}
Implement below extension of UIApplication
extension UIApplication {
class func topViewController(controller: UIViewController? = UIApplication.shared.keyWindow?.rootViewController) -> UIViewController? {
if let navigationController = controller as? UINavigationController {
return topViewController(controller: navigationController.visibleViewController)
}
if let tabController = controller as? UITabBarController {
if let selected = tabController.selectedViewController {
return topViewController(controller: selected)
}
}
if let presented = controller?.presentedViewController {
return topViewController(controller: presented)
}
return controller
}
}
Please change your code to navigate to specific screen while app is in foreground.
let storyboard = UIStoryboard(name: "Main", bundle: nil)
let viewController = storyboard.instantiateViewController(withIdentifier: "chatMessageVC") as! ChatMessagesVC
UIApplication.topViewController()?.navigationController?.pushViewController(viewController, animated: false)
Hope this finds you well and let me know in case of any queries.
UPDATE
If the application is running in the foreground, iOS won't show a notification banner/alert. That's by design. But we can achieve it by using UILocalNotification as follows
if application.applicationState == .active {
var localNotification = UILocalNotification()
localNotification.userInfo = userInfo
localNotification.soundName = UILocalNotificationDefaultSoundName
localNotification.alertBody = message
localNotification.fireDate = Date()
UIApplication.shared.scheduleLocalNotification(localNotification)
}

Swift: Unable to refresh rootViewController with pushNotification's info when the application is running in the background

I am implementing a news App. Once I receive a notification with a breaking news id and message, i am supposed to open the app and refresh the rootViewController with the id received in the push notifications.
Things work fine when the application is not running in the background while an issue arises when the application is running in the background.
In the AppDelegate.swift i have set the didReceiveRemoteNotifications as follows
func application(application: UIApplication, didReceiveRemoteNotification userInfo: [NSObject : AnyObject], fetchCompletionHandler completionHandler: (UIBackgroundFetchResult) -> Void) {
strNotificationId = (userInfo["id"] as? String)!
}
In my applicationWillEnterForeground i have called the rootViewController method activeController.request() that reloads the page with the id received as shown below
func applicationWillEnterForeground(application: UIApplication) {
print("Push notifications are not supported in the iOS Simulator.")
if let navigationController = window?.rootViewController as?UINavigationController {
navigationController.popToRootViewControllerAnimated(false)
}
let navigationController = window?.rootViewController as? UINavigationController
let activeController = navigationController!.visibleViewController as! FeedTableViewController
activeController.request("http://example.net/rsssite.xml")
}
My rootViewController implements the request("http://example.net/rsssite.xml") method. The request method reads the notification id as shown below
delegate.notificationId = [NSObject: AnyObject]()
notificationId = delegate.strNotificationId as String
My issue is showing that whenever i click on my notification received in the IOS notifications list, and when the app happens to be running in the backend, the request("http://example.net/rsssite.xml") that is implemented in the rootViewController is neither being triggered in my rootViewController viewDidLoad nor in applicationWillEnterForeground
Below is a full the full code of my request(url) method in my rootViewController which might be useless for your feedback
//Method to request and parser the rss feeds
func request(urlLink: String){
forceError = false
let alert = UIAlertController(title: nil, message: Util.getMessage("loading"), preferredStyle: .Alert)
alert.view.tintColor = UIColor.blackColor()
let loadingIndicator: UIActivityIndicatorView = UIActivityIndicatorView(frame: CGRectMake(10, 5, 50, 50)) as UIActivityIndicatorView
loadingIndicator.hidesWhenStopped = true
loadingIndicator.activityIndicatorViewStyle = UIActivityIndicatorViewStyle.Gray
loadingIndicator.startAnimating();
alert.view.addSubview(loadingIndicator)
self.view.addSubview(alert.view)
self.view.bringSubviewToFront(loadingIndicator)
let viewsDictionary = ["alert":alert.view]
let loadingMetrics = ["xDimension":(self.view.bounds.size.width - alert.view.frame.width ) / 2, "yDimension": (self.view.bounds.size.height - alert.view.frame.height ) / 2]
let horizontalLoadingConstraint = NSLayoutConstraint.constraintsWithVisualFormat("H:|-50-[alert]", options: NSLayoutFormatOptions.AlignAllCenterX, metrics:loadingMetrics, views: viewsDictionary)
let verticalLoadingConstraint = NSLayoutConstraint.constraintsWithVisualFormat("V:|-100-[alert]", options: NSLayoutFormatOptions.AlignAllCenterX, metrics: loadingMetrics, views: viewsDictionary)
self.view.addConstraints(horizontalLoadingConstraint)
self.view.addConstraints(verticalLoadingConstraint)
presentViewController(alert, animated: true, completion: nil)
let delegate = UIApplication.sharedApplication().delegate as! AppDelegate
let notificationsData = delegate.notificationId
for aButton in notificationsData {
let key = aButton.0
let value = aButton.1
if(key == "id"){
notificationId = value as! String
}
}
delegate.notificationId = [NSObject: AnyObject]()
notificationId = delegate.strNotificationId as String
//notificationId = "17579"
if (notificationId != "" && self.directLoad==false) {
//var notificationURLLink = defaults.stringForKey("notificationId")
let diceRoll = Int(arc4random_uniform(9999) + 1) as NSNumber
let notificationURLLink = "http://example.net/"+notificationId+"-rss.xml?_=" + diceRoll.stringValue
let queue = NSOperationQueue()
queue.addOperationWithBlock() {
let url = NSURL(string: notificationURLLink)
let feedParser = MWFeedParser(feedURL: url)
feedParser.delegate = self
feedParser.parse()
NSOperationQueue.mainQueue().addOperationWithBlock() {
let item = self.feedItems[0] as MWFeedItem
self.slectedItems = item
self.sideBar.showSidebar(false)
self.performSegueWithIdentifier("DetailsViewControllerSegue", sender: self)
self.dismissViewControllerAnimated(false, completion: nil)
self.notificationId = ""
self.directLoad = true
}
}
}else{
let queue = NSOperationQueue()
queue.addOperationWithBlock() {
let url = NSURL(string: urlLink)
let feedParser = MWFeedParser(feedURL: url)
feedParser.delegate = self
feedParser.parse()
NSOperationQueue.mainQueue().addOperationWithBlock() {
self.tableView.reloadData()
self.dismissViewControllerAnimated(false, completion: nil)
}
}
}
delegate.strNotificationId = ""
}
Thank you
Try move your code in applicationWillEnterForeground to didReceiveRemoteNotification. The process should be done right after you receive the notification.
func application(application: UIApplication, didReceiveRemoteNotification userInfo: [NSObject : AnyObject], fetchCompletionHandler completionHandler: (UIBackgroundFetchResult) -> Void) {
strNotificationId = (userInfo["id"] as? String)!
print("Push notifications are not supported in the iOS Simulator.")
if let navigationController = window?.rootViewController as?UINavigationController {
navigationController.popToRootViewControllerAnimated(false)
}
let navigationController = window?.rootViewController as? UINavigationController
let activeController = navigationController!.visibleViewController as! FeedTableViewController
activeController.request("http://example.net/rsssite.xml")
}
Also please check which function execute first when you open the notification while the apps in background, applicationWillEnterForeground or didReceiveRemoteNotification ?

Swift: how to schedule local notification after some seconds

I want my app to schedule a local notification after some seconds it has entered in the background or inactive mode. This is whatIhave done in my view controller, when app enters background mode from it:
class HomeViewController: UITableViewController {
override func viewDidLoad() {
...
let notificationCenter:NSNotificationCenter = NSNotificationCenter.defaultCenter()
notificationCenter.addObserver(self, selector: #selector(self.appMovedToBackground), name: UIApplicationWillResignActiveNotification, object: nil)
}
func appMovedToBackground() {
let appDelegate = UIApplication.sharedApplication().delegate as! AppDelegate
for userInfo in appDelegate.userInfoNotificationPending {
let localNotification = UILocalNotification()
localNotification.userInfo = userInfo
localNotification.soundName = UILocalNotificationDefaultSoundName
localNotification.alertBody = userInfo["aps"]!["alert"] as? String
localNotification.fireDate = nil
UIApplication.sharedApplication().scheduleLocalNotification(localNotification)
let time = dispatch_time(dispatch_time_t(DISPATCH_TIME_NOW), 2 * Int64(NSEC_PER_SEC))
dispatch_after(time, dispatch_get_main_queue()) {
UIApplication.sharedApplication().scheduleLocalNotification(localNotification)
UIApplication.sharedApplication().applicationIconBadgeNumber += 1
}
}
}
}
why doesn't it work?
The didReceiveLocalNotification method in app delegate is called at once as soon as the app enters background. Where am I making the mistake?

Resources