My app allows remote push notifications to a user. How do I enable it to be opened in a specific view controller when the user taps on the push notification? I want the app to open and navigate to a specific view controller depending on the push notification received.
you have to check when your app is on close state using launch option in " func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool{ :
if let option = launchOptions {
let info = option[UIApplication.LaunchOptionsKey.remoteNotification]
if (info != nil) {
self.goAnotherVC()
}
}
and in view did load in your landing VC set the observer
NotificationCenter.default.addObserver(self, selector: #selector(self.goToVc(notification:)), name:NSNotification.Name(rawValue:identifier), object: nil)
Selector Method :
#objc func goToVc(notification:Notification) {
let storyboard = UIStoryboard(name: "Main", bundle: nil)
let vc = storyboard.instantiateViewController(withIdentifier:"landingVC") as! landingVC
self.navigationController?.pushViewController(vc, animated: true)
}
In app delegate :
func application(_ application: UIApplication,didReceiveRemoteNotification userInfo: [AnyHashable: Any]) {
NotificationCenter.default.post(name:NSNotification.Name(identifier), object: userInfo)
}
This answer is for iOS 13+
In AppDelegae :
func userNotificationCenter(_ center: UNUserNotificationCenter, didReceive response: UNNotificationResponse, withCompletionHandler completionHandler: #escaping () -> Void) {
NotificationCenter.default.post(name: NSNotification.Name(identifier), object: nil)
completionHandler()
}
In viewDidLoad of Landing ViewController :
override func viewDidLoad() {
super.viewDidLoad()
NotificationCenter.default.addObserver(self, selector: #selector(self.showChatController(notification:)), name:NSNotification.Name(rawValue: "noti"), object: nil)
}
In your landing viewController add following selector method :
#objc
func showChatController(notification: Notification) {
print("DEBUG: show chat controller here..")
}
You can trigger a notification once you receive and the user clicks on the notification.
Along with the notification, you can pass the value which will later used to identify to which view controller you need to navigate.
Create a class which will be responsible for all the push notification navigation handling. You can name it like PushNotificationHandler. Let the PushNotificationHandler handler take care of all the logic to navigate to the view controllers.
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
}
Get the value from the deep link using the above delegate method
Trigger a notification with the value
Handle the notification inside PushNotificationHandler class
Is it possible to know if the app was launched/opened from a push notification?
I guess the launching event can be caught here:
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
if (launchOptions != nil) {
// Launched from push notification
NSDictionary *notification = [launchOptions objectForKey:UIApplicationLaunchOptionsRemoteNotificationKey];
}
}
However, how can I detect it was opened from a push notification when the app was in background?
See This code :
- (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo
{
if ( application.applicationState == UIApplicationStateInactive || application.applicationState == UIApplicationStateBackground )
{
//opened from a push notification when the app was on background
}
}
same as
-(void)application:(UIApplication *)application didReceiveLocalNotification (UILocalNotification *)notification
late but maybe useful
When app is not running
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
is called ..
where u need to check for push notification
NSDictionary *notification = [launchOptions objectForKey:UIApplicationLaunchOptionsRemoteNotificationKey];
if (notification) {
NSLog(#"app recieved notification from remote%#",notification);
[self application:application didReceiveRemoteNotification:notification];
} else {
NSLog(#"app did not recieve notification");
}
The issue we had was in correctly updating the view after the app is launched. There are complicated sequences of lifecycle methods here that get confusing.
Lifecycle Methods
Our testing for iOS 10 revealed the following sequences of lifecycle methods for the various cases:
DELEGATE METHODS CALLED WHEN OPENING APP
Opening app when system killed or user killed
didFinishLaunchingWithOptions
applicationDidBecomeActive
Opening app when backgrounded
applicationWillEnterForeground
applicationDidBecomeActive
DELEGATE METHODS CALLED WHEN OPENING PUSH
Opening push when system killed
[receiving push causes didFinishLaunchingWithOptions (with options) and didReceiveRemoteNotification:background]
applicationWillEnterForeground
didReceiveRemoteNotification:inactive
applicationDidBecomeActive
Opening push when user killed
didFinishLaunchingWithOptions (with options)
didReceiveRemoteNotification:inactive [only completionHandler version]
applicationDidBecomeActive
Opening push when backgrounded
[receiving push causes didReceiveRemoteNotification:background]
applicationWillEnterForeground
didReceiveRemoteNotification:inactive
applicationDidBecomeActive
The problem
Ok, so now we need to:
Determine if the user is opening the app from a push
Update the view based on the push state
Clear the state so that subsequent opens don't return the user to the same position.
The tricky bit is that updating the view has to happen when the application actually becomes active, which is the same lifecycle method in all cases.
Sketch of our solution
Here are the main components of our solution:
Store a notificationUserInfo instance variable on the AppDelegate.
Set notificationUserInfo = nil in both applicationWillEnterForeground and didFinishLaunchingWithOptions.
Set notificationUserInfo = userInfo in didReceiveRemoteNotification:inactive
From applicationDidBecomeActive always call a custom method openViewFromNotification and pass self.notificationUserInfo. If self.notificationUserInfo is nil then return early, otherwise open the view based on the notification state found in self.notificationUserInfo.
Explanation
When opening from a push didFinishLaunchingWithOptions or applicationWillEnterForeground is always called immediately before didReceiveRemoteNotification:inactive, so we first reset notificationUserInfo in these methods so there's no stale state. Then, if didReceiveRemoteNotification:inactive is called we know we're opening from a push so we set self.notificationUserInfo which is then picked up by applicationDidBecomeActive to forward the user to the right view.
There is one final case which is if the user has the app open within the app switcher (i.e. by double tapping the home button while the app is in the foreground) and then receives a push notification. In this case only didReceiveRemoteNotification:inactive is called, and neither WillEnterForeground nor didFinishLaunching gets called so you need some special state to handle that case.
Hope this helps.
This is a well worn post... but it is still missing an actual solution to the problem (as is pointed out in the various comments).
The original question is about detecting when the app was launched
/ opened from a push notification, e.g. a user taps on the
notification. None of the answers actually cover this case.
The reason can be seen in the call flow when a notification arrives, application:didReceiveRemoteNotification...
gets called when the notification is received AND again when the notification is tapped by the user. Because of this, you can't tell by just looking at UIApplicationState wether the user tapped it.
Additionally, you no longer need to handle the situation of a 'cold start' of the app in application:didFinishLaunchingWithOptions... as application:didReceiveRemoteNotification... is called again after launching in iOS 9+ (maybe 8 as well).
So, how can you tell if the user tap started the chain of events? My solution is to mark the time at which the app begins to come out of the background or cold start and then check that time in application:didReceiveRemoteNotification.... If it is less than 0.1s, then you can be pretty sure the tap triggered the startup.
Swift 2.x
class AppDelegate: UIResponder, UIApplicationDelegate {
var wakeTime : NSDate = NSDate() // when did our application wake up most recently?
func applicationWillEnterForeground(application: UIApplication) {
// time stamp the entering of foreground so we can tell how we got here
wakeTime = NSDate()
}
func application(application: UIApplication, didReceiveRemoteNotification userInfo: [NSObject : AnyObject], fetchCompletionHandler completionHandler: (UIBackgroundFetchResult) -> Void) {
// ensure the userInfo dictionary has the data you expect
if let type = userInfo["type"] as? String where type == "status" {
// IF the wakeTime is less than 1/10 of a second, then we got here by tapping a notification
if application.applicationState != UIApplicationState.Background && NSDate().timeIntervalSinceDate(wakeTime) < 0.1 {
// User Tap on notification Started the App
}
else {
// DO stuff here if you ONLY want it to happen when the push arrives
}
completionHandler(.NewData)
}
else {
completionHandler(.NoData)
}
}
}
Swift 3
class AppDelegate: UIResponder, UIApplicationDelegate {
var wakeTime : Date = Date() // when did our application wake up most recently?
func applicationWillEnterForeground(_ application: UIApplication) {
// time stamp the entering of foreground so we can tell how we got here
wakeTime = Date()
}
func application(_ application: UIApplication, didReceiveRemoteNotification userInfo: [AnyHashable : Any], fetchCompletionHandler completionHandler: #escaping (UIBackgroundFetchResult) -> Void) {
// ensure the userInfo dictionary has the data you expect
if let type = userInfo["type"] as? String, type == "status" {
// IF the wakeTime is less than 1/10 of a second, then we got here by tapping a notification
if application.applicationState != UIApplicationState.background && Date().timeIntervalSince(wakeTime) < 0.1 {
// User Tap on notification Started the App
}
else {
// DO stuff here if you ONLY want it to happen when the push arrives
}
completionHandler(.newData)
}
else {
completionHandler(.noData)
}
}
}
I have tested this for both cases (app in background, app not running) on iOS 9+ and it works like a charm. 0.1s is pretty conservative too, the actual value is ~0.002s so 0.01 is fine as well.
When app is terminated, and user taps on push notification
public func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
if launchOptions?[UIApplicationLaunchOptionsKey.remoteNotification] != nil {
print("from push")
}
}
When app is in background, and user taps on push notificaion
If the user opens your app from the system-displayed alert, the system may call this method again when your app is about to enter the foreground so that you can update your user interface and display information pertaining to the notification.
public func application(application: UIApplication, didReceiveRemoteNotification userInfo: [NSObject : AnyObject], fetchCompletionHandler completionHandler: (UIBackgroundFetchResult) -> Void) {
if application.applicationState == .inactive {
print("from push")
}
}
Depending on your app, it can also send you silent push with content-available inside aps, so be aware of this as well :) See https://stackoverflow.com/a/33778990/1418457
Swift 2.0 For 'Not Running' State (Local & Remote Notification)
func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
// Handle notification
if (launchOptions != nil) {
// For local Notification
if let localNotificationInfo = launchOptions?[UIApplicationLaunchOptionsLocalNotificationKey] as? UILocalNotification {
if let something = localNotificationInfo.userInfo!["yourKey"] as? String {
self.window!.rootViewController = UINavigationController(rootViewController: YourController(yourMember: something))
}
} else
// For remote Notification
if let remoteNotification = launchOptions?[UIApplicationLaunchOptionsRemoteNotificationKey] as! [NSObject : AnyObject]? {
if let something = remoteNotification["yourKey"] as? String {
self.window!.rootViewController = UINavigationController(rootViewController: YourController(yourMember: something))
}
}
}
return true
}
In application:didReceiveRemoteNotification: check whether you have received the notification when your app is in the foreground or background.
If it was received in the background, launch the app from the notification.
-(void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo {
if ([UIApplication sharedApplication].applicationState == UIApplicationStateActive) {
NSLog(#"Notification received by running app");
} else {
NSLog(#"App opened from Notification");
}
}
For swift:
func application(application: UIApplication, didReceiveRemoteNotification userInfo: [NSObject : AnyObject]) {
PFPush.handlePush(userInfo)
if application.applicationState == UIApplicationState.Inactive || application.applicationState == UIApplicationState.Background {
//opened from a push notification when the app was in the background
}
}
Posting this for Xamarin users.
The key to detecting if the app was launched via a push notification is the AppDelegate.FinishedLaunching(UIApplication app, NSDictionary options) method, and the options dictionary that's passed in.
The options dictionary will have this key in it if it's a local notification: UIApplication.LaunchOptionsLocalNotificationKey.
If it's a remote notification, it will be UIApplication.LaunchOptionsRemoteNotificationKey.
When the key is LaunchOptionsLocalNotificationKey, the object is of type UILocalNotification.
You can then look at the notification and determine which specific notification it is.
Pro-tip: UILocalNotification doesn't have an identifier in it, the same way UNNotificationRequest does. Put a dictionary key in the UserInfo containing a requestId so that when testing the UILocalNotification, you'll have a specific requestId available to base some logic on.
I found that even on iOS 10+ devices that when creating location notifications using the UNUserNotificationCenter's AddNotificationRequest & UNMutableNotificationContent, that when the app is not running(I killed it), and is launched by tapping the notification in the notification center, that the dictionary still contains the UILocalNotificaiton object.
This means that my code that checks for notification based launch will work on iOS8 and iOS 10+ devices
public override bool FinishedLaunching (UIApplication app, NSDictionary options)
{
_logger.InfoFormat("FinishedLaunching");
if(options != null)
{
if (options.ContainsKey(UIApplication.LaunchOptionsLocalNotificationKey))
{
//was started by tapping a local notification when app wasn't previously running.
//works if using UNUserNotificationCenter.Current.AddNotificationRequest OR UIApplication.SharedApplication.PresentLocalNotificationNow);
var localNotification = options[UIApplication.LaunchOptionsLocalNotificationKey] as UILocalNotification;
//I would recommended a key such as this :
var requestId = localNotification.UserInfo["RequestId"].ToString();
}
}
return true;
}
Yes, you can detect by this method in appDelegate:
- (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo
{
/* your Code*/
}
For local Notification:
- (void)application:(UIApplication *)application
didReceiveLocalNotification:(UILocalNotification *)notification
{
/* your Code*/
}
if somebody wants the answer in swift 3
func application(_ application: UIApplication, didReceiveRemoteNotification userInfo: [AnyHashable: Any]) {
switch application.applicationState {
case .active:
//app is currently active, can update badges count here
break
case .inactive:
//app is transitioning from background to foreground (user taps notification), do what you need when user taps here
break
case .background:
//app is in background, if content-available key of your notification is set to 1, poll to your backend to retrieve data and update your interface here
break
default:
break
}
}
If you are running iOS 13 or above use this code in your SceneDelegate:
func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
guard let notificationResponse = connectionOptions.notificationResponse else { return }
let pushTitle = notificationResponse.notification.request.content.title
let pushSubtitle = notificationResponse.notification.request.content.subtitle
let pushBody = notificationResponse.notification.request.content.body
// do your staff here
}
If you have SceneDelegate in your app then you should use below code to manage local/remote notification, when your application is killed/terminated and you open application from tapping on notification
func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
//Handle Notification Response
guard let notifiResponse = connectionOptions.notificationResponse else { return }
if notifiResponse.notification.request.trigger is UNTimeIntervalNotificationTrigger { //Local Notification
Messaging.messaging().appDidReceiveMessage(notifiResponse.notification.request.content.userInfo)
print("Receive Local Notifications")
}
else if notifiResponse.notification.request.trigger is UNPushNotificationTrigger{ //Remote Notification
print("Receive Remote Notifications")
}
}
Use your AppDelegate to manage local/remote notification when your application is in background/foreground state.
extension AppDelegate : UNUserNotificationCenterDelegate {
func userNotificationCenter(_ center: UNUserNotificationCenter, didReceive response: UNNotificationResponse, withCompletionHandler completionHandler: #escaping () -> Void) {
if response.notification.request.trigger is UNTimeIntervalNotificationTrigger{
print("Receive Local Notifications")
}
else if response.notification.request.trigger is UNPushNotificationTrigger{
print("Receive Remote Notifications")
}
let userInfo = response.notification.request.content.userInfo
completionHandler()
}
func userNotificationCenter(_ center: UNUserNotificationCenter, willPresent notification: UNNotification, withCompletionHandler completionHandler: #escaping (UNNotificationPresentationOptions) -> Void) {
print("willPresent Notifications")
if notification.request.trigger is UNTimeIntervalNotificationTrigger{
print("Receive Local Notifications")
}
else {
print("Receive Remote Notifications")
}
completionHandler([.banner, .list, .sound])
}
}
Straight from the documentation for
- (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo:nil
If the app is running and receives a remote notification, the app calls this method to process the notification.
Your implementation of this method should use the notification to take an appropriate course of action.
And a little bit later
If the app is not running when a push notification arrives, the method launches the app and provides the appropriate information in the launch options dictionary.
The app does not call this method to handle that push notification.
Instead, your implementation of the
application:willFinishLaunchingWithOptions:
or
application:didFinishLaunchingWithOptions:
method needs to get the push notification payload data and respond appropriately.
I'll start with a state chart that I created for my own use to visualize it more accurately and to consider all other states:
https://docs.google.com/spreadsheets/d/e/2PACX-1vSdKOgo_F1TZwGJBAED4C_7cml0bEATqeL3P9UKpBwASlT6ZkU3iLdZnOZoevkMzOeng7gs31IFhD-L/pubhtml?gid=0&single=true
Using this chart, we can see what is actually required in order to develop a robust notification handling system that works in almost all possible use cases.
Complete solution ↓
Store notification payload in didReceiveRemoteNotification
Clear stored notification in applicationWillEnterForeground and didFinishLaunchingWithOptions
To tackle cases where control Center/ Notification center pulled, you can use a flag willResignActiveCalled and set it to false initially,
Set this to true in applicationWillResignActive method,
In didReceiveRemoteNotification method, save notifications(userInfo) only when willResignActiveCalled is false.
Reset willResignActiveCalled to false in applicationDidEnterBackground and applicationDidBecomeActive method.
Note: A Similar answer is suggested in comments on Eric's answer, however, the state sheet helps in finding all possible scenarios as I did in my app.
Please find the complete code below and comment below if any specific case is not handled:
AppDelegate
class AppDelegate: UIResponder, UIApplicationDelegate {
private var willResignActiveCalled = false
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
NotificationUtils.shared.notification = nil
return true
}
func applicationWillResignActive(_ application: UIApplication) {
willResignActiveCalled = true
}
func applicationDidEnterBackground(_ application: UIApplication) {
willResignActiveCalled = false
}
func applicationWillEnterForeground(_ application: UIApplication) {
NotificationUtils.shared.notification = nil
}
func applicationDidBecomeActive(_ application: UIApplication) {
willResignActiveCalled = false
NotificationUtils.shared.performActionOnNotification()
}
func application(_ application: UIApplication, didReceiveRemoteNotification userInfo: [AnyHashable : Any], fetchCompletionHandler completionHandler: #escaping (UIBackgroundFetchResult) -> Void) {
if !willResignActiveCalled { // Check if app is in inactive by app switcher, control center, or notification center
NotificationUtils.shared.handleNotification(userInfo: userInfo)
}
}
}
NotificationUtils : This is where you can write all your code to navigating to different parts of the application, handling Databases(CoreData/Realm) and do all other stuff that needs to be done when a notification is received.
class NotificationUtils {
static let shared = NotificationUtils()
private init() {}
var notification : [AnyHashable: Any]?
func handleNotification(userInfo : [AnyHashable: Any]){
if UIApplication.shared.applicationState == UIApplicationState.active {
self.notification = userInfo //Save Payload
//Show inApp Alert/Banner/Action etc
// perform immediate action on notification
}
else if UIApplication.shared.applicationState == UIApplicationState.inactive{
self.notification = userInfo
}
else if UIApplication.shared.applicationState == UIApplicationState.background{
//Process notification in background,
// Update badges, save some data received from notification payload in Databases (CoreData/Realm)
}
}
func performActionOnNotification(){
// Do all the stuffs like navigating to ViewControllers, updating Badges etc
defer {
notification = nil
}
}
}
M.Othman's answer is correct for apps that don't contain scene delegate
For Scene Delegate Apps This worked for me on iOS 13
Here is the code for that should be written in will connect scene
if connectionOptions.notificationResponse == nil {
//Not opened from push notification
} else {
//Opened from push notification
}
Code for app delegate to support earlier versions
didFinishLaunchingWithOptions
let notification = launchOptions?[UIApplication.LaunchOptionsKey.remoteNotification]
if (notification != nil) {
//Launched from push notification
} else {
//Launch from other source
}
func application(_ application: UIApplication, didReceiveRemoteNotification data: [AnyHashable : Any]) {
print("Push notification received: \(data)")
if let info = data["aps"] as? Dictionary<String, AnyObject> {
let alertMsg = info["alert"] as! String
print(alertMsg)
switch application.applicationState {
case .active:
print("do stuff in case App is active")
case .background:
print("do stuff in case App is in background")
// navigateToChatDetailViewControler(pushdata: data)
case .inactive:
print("do stuff in case App is inactive")
// navigateToChatDetailViewControler(pushdata: data)
}
}
}
There is only one reliable way, and it works only for iOS 10+ :
Using UNUserNotificationCenter implement UNUserNotificationCenterDelegate method:
- (void) userNotificationCenter:(UNUserNotificationCenter *)center didReceiveNotificationResponse:(UNNotificationResponse *)response withCompletionHandler:(void (^)(void))completionHandler {
//Here you can get your original push if you need to
NSDictionary* pusDict = response.notification.request.content.userInfo;
if ([response.actionIdentifier isEqualToString: UNNotificationDefaultActionIdentifier]) {
//User tapped the notification
} else if ([response.actionIdentifier isEqualToString: UNNotificationDismissActionIdentifier]) {
//User dismissed the notification
} else if ([response.actionIdentifier isEqualToString: MYCustomActionId]) {
//User chose my custom defined action
}
...
}
2021, Swift 5, Local notifiactions only :
UNUserNotificationCenter.current().delegate = self
extension YourClass: UNUserNotificationCenterDelegate {
func userNotificationCenter(_ center: UNUserNotificationCenter, didReceive response: UNNotificationResponse, withCompletionHandler completionHandler: #escaping () -> Void) {
let notificationIdentifier = response.notification.request.identifier
// If this is called, then your app was opened from a local notification with this identifier
}
}
// shanegao's code in Swift 2.0
func application(application: UIApplication, didReceiveRemoteNotification userInfo: [NSObject : AnyObject])
{
if ( application.applicationState == UIApplicationState.Inactive || application.applicationState == UIApplicationState.Background ){
print("opened from a push notification when the app was on background")
}else{
print("opened from a push notification when the app was on foreground")
}
}
The problem with this question is that "opening" the app isn't well-defined. An app is either cold-launched from a not-running state, or it's reactivated from an inactive state (e.g. from switching back to it from another app). Here's my solution to distinguish all of these possible states:
typedef NS_ENUM(NSInteger, MXAppState) {
MXAppStateActive = 0,
MXAppStateReactivated = 1,
MXAppStateLaunched = 2
};
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
// ... your custom launch stuff
[[MXDefaults instance] setDateOfLastLaunch:[NSDate date]];
// ... more custom launch stuff
}
- (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo fetchCompletionHandler:(void (^)(UIBackgroundFetchResult))completionHandler {
// Through a lot of trial and error (by showing alerts), I can confirm that on iOS 10
// this method is only called when the app has been launched from a push notification
// or when the app is already in the Active state. When you receive a push
// and then launch the app from the icon or apps view, this method is _not_ called.
// So with 99% confidence, it means this method is called in one of the 3 mutually exclusive cases
// 1) we are active in the foreground, no action was taken by the user
// 2) we were 'launched' from an inactive state (so we may already be in the main section) by a tap
// on a push notification
// 3) we were truly launched from a not running state by a tap on a push notification
// Beware that cases (2) and (3) may both show UIApplicationStateInactive and cant be easily distinguished.
// We check the last launch date to distinguish (2) and (3).
MXAppState appState = [self mxAppStateFromApplicationState:[application applicationState]];
//... your app's logic
}
- (MXAppState)mxAppStateFromApplicationState:(UIApplicationState)state {
if (state == UIApplicationStateActive) {
return MXAppStateActive;
} else {
NSDate* lastLaunchDate = [[MXDefaults instance] dateOfLastLaunch];
if (lastLaunchDate && [[NSDate date] timeIntervalSinceDate:lastLaunchDate] < 0.5f) {
return MXAppStateLaunched;
} else {
return MXAppStateReactivated;
}
}
return MXAppStateActive;
}
And MXDefaults is just a little wrapper for NSUserDefaults.
Xcode 10 Swift 4.2
func application(application: UIApplication, didReceiveRemoteNotification userInfo: [NSObject : AnyObject]) {
let state : UIApplicationState = application.applicationState
if (state == .Inactive || state == .Background) {
// coming from background
} else {
// App is running in foreground
}
}
M.Othman's answer for Swift 5. (Although using NSLog is not recommended anymore)
Add the following after you set everything needed to display your RootViewController. In your application(_:didReceiveRemoteNotification), you should add the logic that can differentiate between a first launch and come to background type of launch.
if let launchOptions = launchOptions,
let notification = launchOptions[UIApplicationLaunchOptionsKey.remoteNotification]
as? [AnyHashable : Any] {
NSLog("app recieved notification from remote \(notification)")
self.application(application, didReceiveRemoteNotification: notification)
} else {
NSLog("app did not recieve notification")
}
Some other Swift specific answers to tackle this can be found at: How to handle launch options in Swift 3 when a notification is tapped? Getting syntax problems
You can use:
-(void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo
to handle the remote push notifications.
Check here the documentation
I haven't tried it yet but maybe you could send yourself a notification? http://nshipster.com/nsnotification-and-nsnotificationcenter/
For Swift Users:
If you want to launch a different page on opening from push or something like that, you need to check it in didFinishLaunchingWithOptions like:
let directVc: directVC! = directVC(nibName:"directVC", bundle: nil)
let pushVc: pushVC! = pushVC(nibName:"pushVC", bundle: nil)
if let remoteNotification = launchOptions?[UIApplicationLaunchOptionsRemoteNotificationKey] as? NSDictionary {
self.navigationController = UINavigationController(rootViewController: pushVc!)
} else {
self.navigationController = UINavigationController(rootViewController: directVc!)
}
self.window!.rootViewController = self.navigationController
For swift
func application(application: UIApplication, didReceiveRemoteNotification userInfo: [NSObject : AnyObject]){
++notificationNumber
application.applicationIconBadgeNumber = notificationNumber;
if let aps = userInfo["aps"] as? NSDictionary {
var message = aps["alert"]
println("my messages : \(message)")
}
}
When app is in background as shanegao you can use
- (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo
{
if ( application.applicationState == UIApplicationStateInactive || application.applicationState == UIApplicationStateBackground )
{
//opened from a push notification when the app was on background
}
}
But if you want to launch the application and when app is closed and you want to debug your application you can go to Edit Scheme and in left menu select Run and then in launch select Wait for executable to be launched and then you application launch when you click on push notification
Edit Scheme > Run > Wait for executable to be launched
IN SWIFT:
I'm running Push Notifications (with background fetching). When my app is in the background and I receive a push notification, I found that didReceiveRemoteNotification in appDelegate would be called twice; once for when notification is received and another when user clicks on the notification alert.
To detect if notification alert was clicked, just check if applicationState raw value == 1 inside didReceiveRemoteNotification in appDelegate.
func application(application: UIApplication, didReceiveRemoteNotification userInfo: [NSObject: AnyObject]) {
// If not from alert click applicationState(1)
if (application.applicationState.rawValue != 1) {
// Run your code here
}
}
I hope this helps.