Using deinit to make a save to database - swift

Very junior dev, so excuse the question but I'm just curious. I have a local database and I'm considering using a ViewModel to store the information which will only be persisted if the user hits the save button.
But if they send the app to background for example and forgot about the app, can I use the deinit to make the saves the database?
Now not considering the user experience in this case, I just would like to know if this method is ever used.

The app lifecycle is unpredictable enough that you can't trust deinit will be called.
When your app is sent to the background it has a few seconds to run before it's frozen, if the user doesn't use it in a while it will be killed silently, and deinit won't be called.
If the user kills the app, I don't think deinit is called either.
Like it was suggested in the comments, you should listen to life cycle events and react to those. It will change depending on how your app is setup:
UIKit + UIApplication Delegate: implement applicationDidEnterBackground(_:)
UIKit + Scene Delegate: implement sceneDidEnterBackground(_:)
Full SwiftUI: Observe changes of Scene Phase

Related

When to call activateSession() on WCSession object

I wonder at what point one would call activateSession() on a WCSession object on the watch and on the iOS device.
In the documentation it says:
Always assign a delegate and activate your session before calling any session-related methods. The session must be configured and activated before sending messages or obtaining information about the state of the connection.
At first thought I put my code to initialise the session:
if (WCSession.isSupported()) {
session = WCSession.defaultSession()
session.delegate = self
session.activateSession()
}
in viewDidLoad on the iOS device and in willActivate on the watch side.
It works... but I don't think it's a good solution.
I'm not too familiar with the app-lifecycle yet but as far as I understand those are called every time the app's are opened.
Does that result in a "reconnect" every time one of the apps is opened?
Where would be a good place to place that code?
When you put the WCSession code in viewDidLoad and willActivate it is not only called when the app is opened but every time the view controller that contains the code is shown. So that is not an ideal place.
The best place to put this code is in application:didFinishLaunchingWithOptions in your app's AppDelegate and in applicationDidFinishLaunching in your watch extensions's ExtensionDelegate
You can put all the session handling into a singleton class, as suggested in this great tutorial by #NatashaTheRobot.
That way the session is only created once for the time the app in being held in memory.
EDIT
As ccjensen pointed out in his comment, if you are using the connection for a Complication, Notification or Glance update you have to activate the session in the ExtensionDelegate's init method. applicationDidFinishLaunching will not be called in those cases.

The right place to call .removeObserver for NSNotificationCenter = Swift deinit()?

I've read a lot of suggestions for the right place to call .removeObserver for NSNotificationCenter since viewDidUnload is not an option.
I was just wondering if the new deinit() in Swift would be a good choice?
-nick
It really depends on the role of the class where you subscribe to NSNotificationCenter notifications. If you are subscribing in:
UIView
Then you should unsubscribe as soon as view gets invisible to the user. To save CPU cycles and not consume resources while user does not see the view.
UIViewController
Here it also depends on kind of action that you are going to perform in response to notification. If it is just a UI adjustment that you should unsubscribe as soon as view controller disappears from the screen.
You App Service layer
Here it is OK to have .removeObserver inside deinit(). however even here I tend to suggest you to be more explicit about when you subscribe and unsubscribe from NSNotificationCenternotifications and put them in start and stop methods of your service.
If you were previously calling removeObserver in viewDidUnload/dealloc/deinit, then starting with iOS 9.0 and macOS 10.11, you don't need to call it anymore:
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.
source: https://developer.apple.com/documentation/foundation/notificationcenter/1413994-removeobserver

Where to register for applicationWillResignActive using UIStoryboard

Looking at the example in Beginning iOS 5 for persisting your application state, in the first viewController that is shown for the app, they register for applicationWillResignActive in viewDidLoad:. So that makes sense to me in that you register for that notification when your first view is shown.
I'm confused on whether you always do this, or where you typically register for this notification. Q1) Like do they register for this notification in this viewController so they can recreate this view? Q2) If so, do I do this for each viewController?
Q3) I'm using UIStoryboard and my first viewController is a UITabBarController. So do I register for the notification in the first tab's viewController?
I also have a singleton DataManager object that holds the data for the app if that helps anyone guide me in the right direction of where I should save my data. Thanks!
These methods are in the AppDelegate.m
- (void)applicationWillResignActive:(UIApplication *)application
{
/*
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 throttle down OpenGL ES frame rates. Games should use this method to pause the game.
*/
}
You can perform your actions here. You can however register to "listen" to that event in another view controller (like your example) but that is just to make it easier to send that event notification into that viewcontroller.
1) no, only to ensure that whenver this VC is loaded it will be able to listen to this event.
2) no, only for the ones you want to pass the even easily like this. However using the appdelegate.m and each's vc view did appear is better.
3) depends on the kind of data you want to save, but typically you create your own file and just save it to disk like in any other OS. ios gives you access to the "documents" folder of your app and each folder is used for something specific, read the documentation.

iPhone 4: when to save data?

I have an app (a game) which saves data: game state, high scores, achievements, etc. Currently the app delegate does this on applicationWillTerminate:. After playing around with iPhone 4 for a bit, it seems that applications pretty much never terminate: they just run in the background forever, unless the user goes out of their way to quit them, or restart the phone.
So my question is, should I find another place to save my data, and if so, when?
To minimize the amount of time spent in the delegate method call, you should find a place that makes sense to save during the game (level completion, checkpoints, etc). You can also add a new delegate method to your application delegate which will be called when your application transitions to the background where you can duplicate some of the things you may have done previously in applicationWillTerminate:. The new delegate method to implement is -applicationDidEnterBackground:.
You will also receive a notification that the user switched back to your app as applicationWillEnterForeground:.
you can do so in the views diddisappear delegate method
-(void)viewDidDisappear:(BOOL)animated
{
//CODE FOR SAVING
}
There are 2 App delegate methods you can use
applicationDidResignActive: //pausing the app, used when a msg comes up. if multitasking this wont help
applicationDidEnterBackground: // called in iOS 4 for when the app is in the background
you can see when it loads into the foreground using
applicationWillEnterForeground:
check out the reference for more info
You should save in applicationDidEnterBackground. Make sure to wrap your saving code with – beginBackgroundTaskWithExpirationHandler: and endBackgroundTask, since without that, you have less than a second (or something like that) before execution suspends.

awakeFromNib opposite?

Is there a reverse of awakeFromNib, a method called when the nib is closed? I know an application delegates receive a notification the the application will terminate, but was wondering if there was to save some state information on a simple NSObject.
Not really, but mostly because it generally doesn't make sense. By the time an individual object is being deallocated, it is generally too late to do anything meaningful.
For saving state, you would generally want to do so periodically, often triggered by some user action (user just entered a bunch of data.... good time to save, user transitioned to a new part of the app.... save). Only saving as the app is terminated or as a window is closed (or, in your case, as a particular screen is exited) is a recipe for data loss.
For an NSObject instance its dealloc routine will get called as the object is going away -- you should have a chance there to save off any state you need to before destroying self and super.