Call function on app termination in Swift - swift

How can I call a function that is within an SKScene class when my app is terminated by the user?
I need to modify a value and save it to NSUserDefauts when the app is terminated.

You can register to receive a notification when your app is about to terminate. To do this, add an observer to the default notification center by
Swift 5:
NotificationCenter.default.addObserver(self, selector: #selector(saveData), name: UIApplication.willTerminateNotification, object: nil)
Swift 3/4:
// Add this to didMoveToView in your SKScene subclass
NotificationCenter.default.addObserver(self, selector: #selector(saveData), name: NSNotification.Name.UIApplicationWillTerminate, object: nil)
Add the following method to your SKScene subclass. The method will be called before the app terminates. It must be "exposed" to Objective-C by adding #objc so the notifier can use #selector().
#objc func saveData(notification:NSNotification) {
// Save your data here
print("Saving data...")
}

In Swift 3 and 4 you have something like that:
in your viewDidLoad
NotificationCenter.default.addObserver(self, selector: #selector(toDoSomething), name: NSNotification.Name.UIApplicationWillTerminate, object: nil)
and than you have that method to be called
func suspending () {
print("toDoSomething...")
}

There are a few methods in UIAppDelegate that will help you. Take a look at applicationWillTerminate(_:) and applicationWillResignActive(_:). From there you see what state your app is in and do perform the appropriate actions.

Related

Swift selector function with optional closure?

What am I doing wrong with the #selector assignment when I add the notification observer?
NotificationCenter.default.addObserver(self, selector: #selector(reloadData), name: NSNotification.Name(rawValue: "reloadCollectionData"), object: nil)
func reloadData(completionHandler: ((Bool) -> Void)? = nil ) {
mainCollectionView.reloadData()
completionHandler?(true)
}
The app crashes whenever I post the notification:
NotificationCenter.default.post(name: NSNotification.Name(rawValue: "reloadCollectionData"), object: nil)
I have already tried things like #selector(reloadData(completionHandler: nil)
EDIT:
I have tried the selector #selector(reloadData(completionHandler:)) but it still crashes n the line where I post a notification, with the error message:
Thread 1: EXC_BAD_ACCESS (code=1, address=0x8)
When I change the code as follows everything works fine but it is not really nice to create a function just to call another function
NotificationCenter.default.addObserver(self, selector: #selector(testCall), name: NSNotification.Name(rawValue: "reloadCollectionData"), object: nil)
func testCall() {
self.reloadData()
}
A completion handler in a notification selector method is nonsensical (and illegal). What do you expect is the object which is supposed to be the receiver of the completion handler ... ?
The syntax of a notification selector is very similar to a target/action selector. The passed parameter must be the type of the affected object (here Notification).
func reloadData(_ notification : Notification) { ...
The alternative is the block based API of Notification which is able to capture values of the enclosing function / method.
The only way to pass custom data in a notification object with specified selector is the (optional) userInfo dictionary.

NSWorkspace notification not received

My function isn't being executed when I switch apps:
public class ItemObserver: NSObject {
#objc public func recievedNotification(notification: NSNotification) {
print(notification.name)
print("s")
}
}
let observer = ItemObserver()
NSWorkspace.shared().notificationCenter.addObserver(observer, selector: #selector(ItemObserver.recievedNotification(notification:)), name: .NSWorkspaceDidActivateApplication, object: nil)
NSNotificationCenter doesn't retain the observer: it uses a zeroing weak reference, where possible. If you log its init and deinit, you'll see that it's being released right away.
Keep a strong reference to the observer.
So it turns out that NSNotifications don't run concurrently. I had a while loop further down.

WatchOS: getting applicationDidBecomeActive notifications

I'm making a framework that needs to do stuff when my apple watch is entering background and foreground.
I'm looking for an equivalent of this iOS code for the Apple watch since UIApplication is not present in UIKit anymore :
let notificationCenter = NSNotificationCenter.defaultCenter()
notificationCenter.addObserver(self, selector: "applicationDidEnterBackground", name: UIApplicationDidEnterBackgroundNotification, object: nil)
any help would be nice
As of watchOS 7 the following have been added:
WKExtension.applicationDidBecomeActiveNotification
WKExtension.applicationDidEnterBackgroundNotification
WKExtension.applicationDidFinishLaunchingNotification
WKExtension.applicationWillEnterForegroundNotification
WKExtension.applicationWillResignActiveNotification
Source: https://developer.apple.com/documentation/WatchKit/wkextension
Appears that the WatchOS equivalent of
let notificationCenter = NSNotificationCenter.defaultCenter()
notificationCenter.addObserver(self, selector: "applicationDidEnterBackground", name: UIApplicationDidEnterBackgroundNotification, object: nil)
is simply
let notificationCenter = NSNotificationCenter.defaultCenter()
notificationCenter.addObserver(self, selector: "applicationDidEnterBackground", name: "UIApplicationDidEnterBackgroundNotification", object: nil)
One just need to replace the emum by its string equivalent
Closest you get is applicationDidBecomeActive and applicationWillResignActive
class ExtensionDelegate: NSObject, WKExtensionDelegate {
func applicationDidFinishLaunching() {
// Perform any final initialization of your application.
}
func applicationDidBecomeActive() {
// Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface.
}
func applicationWillResignActive() {
// 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, etc.
}
}

Doubts in NSNotification in Swift

I am developing my very first app for iOS and not understanding notifications.
I am sending notifications as:
DefaultCenter.postNotificationName("evRodadaAtualizei", object: nil)
In another class, I have a method that observes this notification:
DefaultCenter.addObserver(self, selector: Selector("Atualizar"),
name: "evRodadaAtualizei", object: nil)
My question is:
This observer will listen any notification with that name? It is not important the class where notification was declared? In other words, is possible to have a place to put all the notifications (like a notification library), because all of them are independent of the class?
If I am understanding correctly, this is very different of the concept of events in C# or VB.Net where events belongs to classes.
Notifications in Cocoa work inter-class. It doesn't matter where the notification is created or observed.
However, note the object parameter on the postNotificationName method. If set, this should correspond to the object posting the notification. If you only want to observe notifications for a given object, set the object parameter to that object when you add the observer. e.g.
class MyObjectClass {
func doSomething() {
// Do something and then notify
DefaultCenter.postNotificationName("evRodadaAtualizei", object: self)
}
}
class MyObserverClass {
func startProcess() {
var myObject = MyObjectClass()
DefaultCenter.addObserver(self, selector: Selector("Atualizar"), name: "evRodadaAtualizei", object: myObject)
}
func Atualizar() {
}
}

NSNotification never being sent

I'm trying to use the NSNotificationCenter with Swift and I'm running into a problem. I'm trying the following:
class MyClass {
init() {
NSNotificationCenter.defaultCenter().addObserver(self, selector: "stopTimer", name: UIApplicationDidEnterBackgroundNotification, object: [])
}
func stopTimer() {
println("Entered background!")
}
}
When hitting the home button, the observer is never calling the selector, therefore my message is never being printed out.
Notice that I send an empty object at the end, the method signature for addObserver is expecting an implicitly unwrapped object and setting it to nil results in an EXC_BAD_ACCESS error when the app enters background.
Any ideas?
EDIT
I forgot to mention that this code is being executed from a framework included in my project.
Executing the code gives me a bad access abort signal.
You should be able to pass nil for an optional argument. Can you elaborate more on what happens if you do? This is working for me:
func init() {
super.init()
NSNotificationCenter.defaultCenter().addObserver(self, selector: "stopTimer", name: UIApplicationDidEnterBackgroundNotification, object: nil)
}
func stopTimer() {
println("Entered background!")
}
deinit {
NSNotificationCenter.defaultCenter().removeObserver(self)
}
Alright, I figured this out. 2 problems:
There was no strong reference to the instance of my class in my app
My class wasn't extending NSObject
Now, I can set object: nil and everything works fine.
Thanks for all your help! :)
Perhaps you should try implementing applicationDidEnterBackground(application: UIApplication) instead?
To further troubleshoot, you may also want to add a log statement to your app delegate's deinit to see if the delegate gets deallocated after entering the background.