Pausing timer when app is in background state Swift - ios

My ViewController.swift
func startTimer() {
timer = NSTimer().scheduleTimerWithTimerInvterval(1.0,target: self,selctor: Selector("couting"),userinfo: nil, repeats: true)
}
func pauseTimer() {
timer.invalidate()
println("pausing timer")
}
and this is appDelegate.swift
func applicateWillResignActive(application: UIApplication) {
viewController().pauseTimer()
println("closing app")
}
It is printing pausing timer and closing app but when I open again I see it never paused. How do I do it correctly?

You have to set an observer listening to when the application did enter background. Add the below line in your ViewController's viewDidLoad() method.
NSNotificationCenter.defaultCenter().addObserver(self, selector: Selector("myObserverMethod:"), name:UIApplicationDidEnterBackgroundNotification, object: nil)
Add the below function to receive the notification.
func myObserverMethod(notification : NSNotification) {
println("Observer method called")
//You may call your action method here, when the application did enter background.
//ie., self.pauseTimer() in your case.
}
Happy Coding !!!

Updated Answer for Swift 5:
NotificationCenter.default.addObserver
(self,
selector: #selector(myObserverMethod),
name:UIApplication.didEnterBackgroundNotification, object: nil)
#objc func myObserverMethod() {
print("Write Your Code Here")
}

Accepted answer by #Suresh in Swift 3
Set an observer listening to when the application did enter background in your ViewController's viewDidLoad() method.
NotificationCenter.default.addObserver(self, selector: #selector(myObserverMethod), name:NSNotification.Name.UIApplicationDidEnterBackground, object: nil)
Add the below function to receive the notification.
func myObserverMethod(notification : NSNotification) {
print("Observer method called")
//You may call your action method here, when the application did enter background.
//ie., self.pauseTimer() in your case.
}

Updated Answer for Swift 4:
override func viewDidLoad() {
super.viewDidLoad()
NotificationCenter.default.addObserver(self, selector: #selector(myObserverMethod), name:NSNotification.Name.UIApplicationDidEnterBackground, object: nil)
}
#objc func myObserverMethod() {
// Call your action method here, when the application did enter background.
}

You are creating a new object and calling pauseTimer() on it:
viewController().pauseTimer() // This line causes issue viewController(), creates a new instance
Instead of creating new object, either you should pass that instance to AppDelegate and call pauseTimer() on existing object or Listen for UIApplicationDidEnterBackgroundNotification notification in your view controller class and pause timer from there.

Related

Update variables when day changes Swift [duplicate]

I want my app to act when there is a change to another day.
So, in my appDelegate, I put
func applicationSignificantTimeChange(_ application: UIApplication){
//this one fires
}
and in the ViewController that should update its content I do:
override func viewDidLoad() {
NotificationCenter.default.addObserver(self, selector: #selector(self.dayChanged(notification:)), name: Notification.Name("significantTimeChangeNotification"), object: nil)
}
and
#objc func dayChanged(notification: NSNotification){
//this one doesn't fire
}
somehow, while the func in AppDelegate is called, the observer seems to be blind for that event.
Is this syntax, or just plain misunderstanding of the mechanism?
You need to add an observer for "UIApplicationSignificantTimeChangeNotification":
NotificationCenter.default.addObserver(self, selector: #selector(dayChanged), name: UIApplicationSignificantTimeChangeNotification, object: nil)
For Swift 4.2 or later
NotificationCenter.default.addObserver(self, selector: #selector(dayChanged), name: UIApplication.significantTimeChangeNotification, object: nil)
Note: If your intent is to be notified when the day changes you can use .NSCalendarDayChanged ("NSCalendarDayChangedNotification") instead of UIApplication.significantTimeChangeNotification.
NotificationCenter.default.addObserver(self, selector: #selector(dayChanged), name: .NSCalendarDayChanged, object: nil)
And add the selector method to the view controller where you would like to monitor the day changes:
#objc func dayChanged(_ notification: Notification) {
}

App Delegate : Service call coming back from background

I want to call a web service whenever application coming back in the foreground. I am calling it from didBecomeActive().
What's the best way to handle it and pass data to Root view controller?
Since the data you want to pass is always going to the same view controller you should instead set the observer in that view controller instead of app delegate. This way you won't need to pass any data in the first place.
class YourViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
NotificationCenter.default
.addObserver(self, selector: #selector(activityHandler(_:)),
name: UIApplication.didBecomeActiveNotification, object: nil)
}
#objc func activityHandler(_ notification: Notification) {
//Call your web service here
}
}
You have two choices. Get rootViewController and pass the data, handle it.
func applicationDidBecomeActive(_ application: UIApplication) {
// 1
let rootVC1 = self.window?.rootViewController
// 2
let rooVC2 = application.windows.first?.rootViewController
...
/*
pass data to rootVC1 or rootVC2
*/
}

Notification Center Observer Selector Method does not get called

I am adding an observer on A view controller and view controller B is presenting on A.
While dismissing Controller B , I have posted the notification but It does not called the selector method added in A.
Also notification get registered first and the post method is get called. I have already checked that.
Here is the sample code:
NotificationCenter.default.addObserver(self, selector: #selector(closButtonPressed(notification:)) ,name: Notification.Name("CloseButtonPressed"), object: nil)
#objc func closButtonPressed(notification: Notification){
}
NotificationCenter.default.post(name: Notification.Name("CloseButtonPressed"), object: self)
Any help would be appreciated.
Make sure posting notification in completion handler
self?.dismiss(animated: true, completion: {
NotificationCenter.default.post(name: Notification.Name("CloseButtonPressed"),
object: self)
}
Make sure you implement Notification correctly. I recommend you to create an extension for Notification Name & make it static.
extension Notification.Name {
static let didTapCloseButton = Notification.Name("CloseButtonPressed")
}
NotificationCenter.default.addObserver(self, selector: #selector(didTapCloseButton(_:)), name: .didTapCloseButton, object: nil)
#objc func didTapCloseButton(_ sender: Notification?) {
}
NotificationCenter.default.post(name: .didTapCloseButton, object: nil)
I am not sure what's wrong with your project. To solve your problem, I create a test project and write some code like this:
//ControllerA.swift
override func viewDidLoad() {
NotificationCenter.default.addObserver(self, selector: #selector(getNotification(notification:)), name: NSNotification.Name("CloseButtonPressed"), object: nil)
}
getNotification(notification: Notification) {
print(notification)
}
#objc func buttonAClick() {
navigationController?.present(ViewControllerB(), animated: true, completion: {
})
}
//ViewControllerB.swift
#objc func buttonClick() {
NotificationCenter.default.post(name: Notification.Name("CloseButtonPressed"), object: self)
self.dismiss(animated: true) {
}
}
As you said, I add notification in ControllerA, and present ControllerB, When ControllerB close, post notification and dismiss, console can print the notification object, so may I miss something?

iOS unable to remove Notification observer. Deinit not getting called

I have a UIView similar to the one you can see below:
class ViewTaskViewController: UIViewController, UITableViewDataSource, UITableViewDelegate {
override func viewDidLoad() {
super.viewDidLoad()
subscribeToNotifications()
}
func subscribeToNotifications() {
let notification = NotificationCenter.default
notification.addObserver(forName: Notification.Name(rawValue: "TimerUpdated"), object: nil, queue: nil, using: handleUpdateTimer)
print("Subscribed to NotificationCenter in ViewTaskViewController")
}
override func viewWillDisappear(_ animated: Bool) {
super.viewWillDisappear(animated)
print("TUFU TUFU TUFU")
NotificationCenter.default.removeObserver(self)
}
deinit {
print("DENINT")
}
#objc func handleUpdateTimer(notification: Notification) {
if let userInfo = notification.userInfo, let timeInSeconds = userInfo["timeInSeconds"] as? Int {
withUnsafePointer(to: &self.view) {
print("We got timeeeeee \(timeInSeconds) \($0)")
}
//do something here....
}
}
}
The issue I am having is that I am unable to remove the observers from this particular UIView when the user hits the back button and returns to another viewController.
ViewWillDisppear is called but deinit is not called. The strange thing is that if we remove subscribeToNotifications() from viewDidLoad() then the deinit is called.
The other issue is related to a memory leak. As you can see in the screenshot below, when the view does subscribe to notifications and the user leaves/re-enters the view, the memory usage increase.
Now compare that to when the subscribeToNotifications() is commented out, there is no increase in memory usage and only one instance of the viewController.
The conclusion is that there seems to be a correlation between the notification subscription creation of a new instance of the UIView hence the deinit is not being called.
I'd like to find out if there is a way we can deinitialize the view and unsubscribe from the notification.
Please let me know if you need further information. :)
I've found the removeObserver() only works if you use this version of addObserver()
notification.addObserver(self, selector:#selector(self.handleUpdateTimer), name: Notification.Name(rawValue: "TimerUpdated"), object: nil)
I'm guessing with the original version you aren't actually indicating who the observer is.
As #Spads said you can use
NotificationCenter.default.addObserver(self, selector: #selector(subscribeToNotifications), name: NSNotification.Name(rawValue: "TimerUpdate"), object: nil)
or the one you already have.
you can remove your notification by it's name or it's reference
NotificationCenter.default.removeObserver(self, name: "TimerUpdate", object: nil)
if you declared your notification at the top of your class then you can directly pass the reference of your notification to be removed in your case notification
NotificationCenter.default.removeObserver(notification)
You should store your newly added observer in a opaque object (NSObjectProtocol) and then call NotificationCenter.default.removeObserver(self.nameOfObserver)

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

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.
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.
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!")
}
}
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!")
}
In Swift 3, it replaces and generates the following.
override func viewDidLoad() {
super.viewDidLoad()
foregroundNotification = NotificationCenter.default.addObserver(forName:
NSNotification.Name.UIApplicationWillEnterForeground, object: nil, queue: OperationQueue.main) {
[unowned self] notification in
// do whatever you want when the app is brought back to the foreground
}

Resources