Triggering a specific action when the app enters foreground from a local notification in iOS? (using swift)

30,178

Solution 1

If I want a view controller to be notified when the app is brought back to the foreground, I might just register for the UIApplication.willEnterForegroundNotification notification (bypassing the app delegate method entirely):

class ViewController: UIViewController {

    private var observer: NSObjectProtocol?

    override func viewDidLoad() {
        super.viewDidLoad()

        observer = NotificationCenter.default.addObserver(forName: UIApplication.willEnterForegroundNotification, object: nil, queue: .main) { [unowned self] notification in
            // do whatever you want when the app is brought back to the foreground
        }
    }

    deinit {
        if let observer = observer {
            NotificationCenter.default.removeObserver(observer)
        }
    }
}

Note, in the completion closure, I include [unowned self] to avoid strong reference cycle that prevents the view controller from being deallocated if you happen to reference self inside the block (which you presumably will need to do if you're going to be updating a class variable or do practically anything interesting).

Also note that I remove the observer even though a casual reading of the removeObserver documentation might lead one to conclude is unnecessary:

If your app targets iOS 9.0 and later or macOS 10.11 and later, you don't need to unregister an observer in its dealloc method.

But, when using this block-based rendition, you really do need to remove the notification center observer. As the documentation for addObserver(forName:object:queue:using:) says:

To unregister observations, you pass the object returned by this method to removeObserver(_:). You must invoke removeObserver(_:) or removeObserver(_:name:object:) before any object specified by addObserver(forName:object:queue:using:) is deallocated.

Solution 2

I like to use the Publisher initializer of NotificationCenter. Using that you can subscribe to any NSNotification using Combine.


import UIKit
import Combine

class MyFunkyViewController: UIViewController {

    /// The cancel bag containing all the subscriptions.
    private var cancelBag: Set<AnyCancellable> = []

    override func viewDidLoad() {
        super.viewDidLoad()
        addSubscribers()
    }

    /// Adds all the subscribers.
    private func addSubscribers() {
        NotificationCenter
            .Publisher(center: .default,
                       name: UIApplication.willEnterForegroundNotification)
            .sink { [weak self] _ in
                self?.doSomething()
            }
            .store(in: &cancelBag)
    }

    /// Called when entering foreground.
    private func doSomething() {
        print("Hello foreground!")
    }
}

Solution 3

Add Below Code in ViewController

override func viewDidLoad() {
super.viewDidLoad()
let notificationCenter = NotificationCenter.default
notificationCenter.addObserver(self, selector:#selector(appMovedToForeground), name: UIApplication.willEnterForegroundNotification, object: nil)
   }

  @objc func appMovedToForeground() {
  print("App moved to foreground!")
  }
Share:
30,178
cptdanko
Author by

cptdanko

Updated on November 27, 2021

Comments

  • cptdanko
    cptdanko over 2 years

    I am building an iOS app using the new language Swift. Now it is an HTML5 app, that displays HTML content using the UIWebView. The app has local notifications, and what i want to do is trigger a specific javascript method in the UIWebView when the app enters foreground by clicking (touching) the local notification.

    I have had a look at this question, but it does not seem to solve my problem. I have also come across this question which tells me about using UIApplicationState, which is good as that would help me know the the app enters foreground from a notification. But when the app resumes and how do i invoke a method in the viewController of the view that gets displayed when the app resumes?

    What i would like to do is get an instance of my ViewController and set a property in it to true. Something as follows

    class FirstViewController: UIViewController,UIWebViewDelegate { 
      var execute:Bool = false;
      @IBOutlet var tasksView: UIWebView!
    }
    

    And in my AppDelegate i have the method

    func applicationWillEnterForeground(application: UIApplication!) {
        let viewController = self.window!.rootViewController;
        let mainStoryboard: UIStoryboard = UIStoryboard(name: "Main", bundle: nil)
    
        var setViewController = mainStoryboard.instantiateViewControllerWithIdentifier("FirstView") as FirstViewController
        setViewController.execute = true;
    
    
    }
    

    so what i would like to do is when the app enters foreground again, i want to look at the execute variable and run the method as follows,

    if execute{
     tasksView.stringByEvaluatingJavaScriptFromString("document.getElementById('sample').click()");
    }
    

    Where should i put the code for the logic to trigger the javascript from the webview? would it be on viewDidLoad method, or one of the webView delegate methods? i have tried to put that code in the viewDidLoad method but the value of the boolean execute is set to its initial value and not the value set in the delegate when the app enters foreground.

  • cptdanko
    cptdanko almost 10 years
    I appreciate the detail in the response. Now as you may have guessed, i am very new to ios programming and have no experience publishing on the app store. So if i bypass the app delegate method for notifications, will that increase the chances of my app not clearing the app store submission approval process?
  • Rob
    Rob almost 10 years
    No, this notification is a documented, well-established public interface, so it is approved mechanism. It is fine.
  • confile
    confile about 9 years
    What if you have registered more than one observer and you want all of them to be removed on deinit?
  • TruMan1
    TruMan1 over 8 years
    Unregister each one or do: NSNotificationCenter.defaultCenter().removeObserver(self)
  • Lance Samaria
    Lance Samaria over 4 years
    @rob hi, i'm having a weird situation where my foreground notification doesn't fire after going to the background when an avcapturesession is removed. The only way the fg notification works is if I put a breakpoint anywhere in the bg notification. I tried the code you added above and when coming from the bg to the fg nothing registers. I changed from debug mode to release mode and back and still nothing. Only works when a breakpoint is put anywhere in the bg notification code
  • Zorayr
    Zorayr almost 4 years
    Does the observer need to be removed at some point?
  • paky
    paky over 3 years
    Yes it is a good practice to remove the observer when the viewController deinit. Add something like deinit { NotificationCenter.default.removeObserver(self) } if you added any NotificationCenter.
  • Andrew Stoddart
    Andrew Stoddart over 2 years
    Quoted from above - If your app targets iOS 9.0 and later or macOS 10.11 and later, you don't need to unregister an observer in its dealloc method.