I need code to run every time the app opens, including if it was only suspended (user hit home button, then returned to app).
I already know that viewDidLoad only loads on the initial VC load, so that doesn't do what I need. And viewWillAppear / viewDidAppear don't do it either. This SO thread has an answer but it's from six years ago and I don't love the answer. Essentially, it says create an observer. However, that seems like a waste of resources and observers create those reference loops that keep things in memory.
If no one gives me a better solution I might try "applicationDidBecomeActive" in the AppDelegate, but I try not to load my appDelegate up with ViewController code.
My question is, in the six years since this SO thread was answered, do Swift/iOS10 now allow for a cleaner solution?
If you don't want to use NSNotificationCenter Than make global object of your Viewcontroller in AppDelegate
and put code in applicationDidBecomeActive with your viewController object
Because applicationDidBecomeActive calls everyTime when you start your application and when you press home button and came from background so you don't need to write your code in viewWillAppear.
As far as I can tell after additional research, there's no new way since that post six years ago. However, the applicationDidBecomeActive approach does work well:
func applicationDidBecomeActive(_ application: UIApplication) {
guard let rvc = self.window?.rootViewController as? firstVCName else { return }
rvc.nameOfMethodYouWantToCall()
}
The above gets a reference to the existing VC (instead of creating a whole new instance). Then it calls the method, without having to load up the appDelegate with code. Very simple and efficient.
I tested this and it's exactly what I needed, code that runs every time the app is opened. It's very consistent.
Related
This question already has answers here:
What's the best way to detect when the app is entering the background for my view?
(10 answers)
Closed 3 years ago.
I've a ViewController which is named mapViewController. Inside that controller I've a button which is a play button. If the button is clicked a boolean named "Play" will change from false to true.
Let's jump to the point of this thread:
Once I press the button, I want the boolean to be changed to true. If the user leaves the application. (Entering the background) I want to print out a text which displays that. (I'll use this for other purposes as soon as I get an idea how this works.)
So, let's take a look at my AppDelegate
func applicationDidEnterBackground(_ application: UIApplication)
{
// Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later.
// If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits.
let mapVC = UIApplication.shared.delegate as! MapViewController
if (mapVC.play == true)
{
print("The play-bool is true, and application did enter the background")
}
}
Once I press the button, and leaves the application I'm getting an error which looks like this:
Could not cast value of type 'MyDogwalk.AppDelegate' (0x28093e230) to 'MyDogwalk.MapViewController' (0x104f99d98).
warning: could not execute support code to read Objective-C class data
in the process. This may reduce the quality of type information
available.
As you can see on my code, I'm trying to get a boolean from a ViewController, and check it's statement inside my AppDelegate.
I'll use this information to track locations once the phone is in the background, but as this is my first time. I need/want to understand how this actually works, and how I can do this.
Please don't try to access controllers from the AppDelegate, that is not its intended purpose.
You can check whether an application will enter into the background from anywhere, if you register your current ViewController to the correct NSNotification. Then you will define a function that is executed when the App enters the background state, in which you will be able to print out your text.
Please take a look at https://stackoverflow.com/a/9012734/4442731 for how to do this. A swift solution is in the answer below the linked answer. This code needs to be in your MapViewController.
Maybe you should rethink your application:
One way is adding observer for certain Notification name like UIApplication.didEnterBackgroundNotification. So you'll be noticed in your UIViewController class that your app goes to background
Or if play is something which should be stored, you can use UserDefaults, FileManager or some database for storing this value, and then you can retrieve this value in applicationDidEnterBackground(_:)
I want to extract some data from the server or database, now I'm confusing if I have to put the extracting code in didFinishLaunchingWithOptions() function or put the code in viewdidload() function in the first viewcontroller. What's the execution efficiency of both methods?
It depends on your requirement. If you want the data before actually going to a viewController then you can extract it in didFinishLaunching WithOptions. But if you want the data for only viewController then its good practise to extract it when you are on that viewController i.e in viewDidLoad of that viewController.
If you fetch data over the network, I'd definitely recommend not to use didFinishLaunchingWithOptions. Any blocking code in this method will prevent the app's UI from loading, so you can't even display a "Please wait" message to the user. Also, iOS will kill your app if didFinishLaunchingWithOptions blocks for too long (I think the timeout is about 10 seconds).
So either perform the loading in your main view controller, or run it asynchronously in a background queue — in this case, you can of course also put the code in didFinishLaunchingWithOptions.
I want to update the the code inside viewDidLoad() every new day - even if the app is not running - closed or in background.
let Date = NSDate()
let Calendar = NSCalendar.currentCalendar()
var Days = 20
var Hours = 5
var Difference = 0
override func viewDidLoad() {
let FireDate = userCalendar.components(NSCalendarUnit.Hour, fromDate: Date)
if (FireDate.hour == 7) {
days--
Difference = (Days / Hours)
}
}
This code does not work how I want it to.
If that code is run at 7 then the check will succeed. That is, 7 on the device it's running on, considering the users timezone settings.
This code won't automatically run each day. You don't show where the code is but nothing will automatically run an iOS app at a set time each day. Possibly you could send a silent push as a trigger but you should really plan for the app to work when the user opens it instead of autonomously. Any time sensitive offline processing should be done by a server...
You could also look at using func applicationSignificantTimeChange(_ application: UIApplication) so that the application is told when the day has changed, in combination with your general processing in application:didFinishLaunchingWithOptions:.
viewDidLoad is called once and once only: When your view controller gets loaded. Trying to call this multiple times is going to get you into trouble.
Usually you set up some things in viewDidLoad (things that should be set up exactly once), and others in viewDidAppear or viewWillAppear (things that might be set up in different ways if your view appears multiple times).
Create a new method that does your setup that would usually be in viewDidAppear or viewWillAppear, making sure there are no problems if this code is called multiple times. Change viewDidAppear or viewWillAppear to call this method. Then in viewDidLoad create a timer that will call this at the right time, and in dealloc invalidate the timer.
If your app is in the background, even if the view is not loaded, there is no need for this to happen. Nobody will notice anyway. The timer will be called immediately when your app comes to the foreground (if it was due to be called), and if your app was closed, viewDidAppear is going to be called.
Ok, so in my AppDelegate, I initialise various stuff in didFinishLaunchingWithOptions, however I create my views in applicationDidBecomeActive.
My reasoning behind this was that when the app is executing stuff in the background (push notifications), the didFinishLaunching... will be called so I don't want to create UI stuff there, if the application will not be visible.
This has worked for me, however now with quick actions, say i want to have a quick action that goes to one of my tabs. I can't do it in didFinishLaunchingWithOptions, since the views won't be created until didBecomeActive.
So, my questions are:
Have I misunderstood something regarding the lifecycle and timing of UI and didFinishLaunching.../didBecomeActive? That is, is it good practice to do what i do, create the views to be used in didBecomeActive and not didFinishLaunching...?
I haven't really found any good examples regarding this. I suppose i could store away the UIApplicationShortcutItem in didFinishLaunching... and then use it in didBecomeActive. It seems a bit 'hacky', but i haven't figured out anything else.
Pointers much appreciated.
What I'm looking for is a very straightforward solution to the following in my XCode project:
When applicationWillEnterForeground is called, I want my main (and only) viewcontroller to reload as if the app is launching from scratch, the reason being I want it to recalculate a number of things at that point which might have changed while the app was in the background, in response to temporal changes detected when the app is launched. I want this to happen rather than the view just to reappear in its last seen state
The viewcontroller is named TMViewController
I have found answers that tell me simply that I have to reload the viewcontroller in the applicationWillEnterForeground section of the AppDelegate, but not how to do this
I have found answers that explain how to reload various elements, labels, webviews, and data from the net. These are much more complex than I need, and I can't really see the needle for the haystack
All I need to happen is the applicationWillEnterForeground to reload the viewcontroller as if its viewDidLoad had been called (I understand that this in itself is not a good solution, not that I know how to do even that!)
I suppose in a nutshell I'm struggling to understand what needs to go in the h & m files of both the AppDelegate and the viewcontroller files to accomplish this
any help would be greatly appreciated
self.window.rootViewController = [[TMViewController alloc]intWithNibName:#"myNib" bundle:nil];