My app has one view controller and one view. When I run my app on the simulator from xcode, the app loads and both viewDidLoad, viewDidAppear are called as expected.
When I go to the simulator home screen and then come back to the app,
I expect viewDidLoad to be called, but it is not.
When I QUIT the
app by following these directions, and restart the app fresh, I
expect both methods to be called, but neither are called.
If these events don't trigger those calls, then what will trigger those calls?
It's hard to believe that on a real device, viewDidLoad is only called once - the first time the app is loaded.
It says "load". It literally means that it is called when the view controller is loaded(instantiated). Also the "appear" is called when the view is appeared like when you push it or dismiss other view controller above it.
The thing you want is register the following notifications
static let UIApplicationWillEnterForeground: NSNotification.Name
static let UIApplicationDidEnterBackground: NSNotification.Name
static let UIApplicationDidBecomeActive: NSNotification.Name
In your view controller's viewDidLoad() add notification.
NotificationCenter.default.addObserver(self, selector: #selector(ViewController.applicationDidBecomeActive), name: NSNotification.Name.UIApplicationDidBecomeActive, object: nil)
Then the following method will be called when the app becomes active again form the background.
func applicationDidBecomeActive() {
// Update your view controller
}
Case 2:
If you quit the app like the instruction, the debug session is terminated so the break point and log is not working. If you quit your app and want to check the break points or logs you need to hit run again in your Xcode.
Related
Background
Out app has a feature to launch background using Significant-Change Location Service(https://developer.apple.com/documentation/corelocation/getting_the_user_s_location/using_the_significant-change_location_service).
This an iOS feature that when the app detect significant change in location, the app launched on background. It launches directly to background state so the user won't notice that app has launched(unless writing code to notify to users. ie. sending a local push or etc.) however app is fully launched so didFinishLaunchingWithOptions method on App Delegate is called upon launch.
Situation
When our app is launched by Significant-Change Location, we send local push notification to the user notifying that how much distance the user has walked recently(we have implemented pedometer to our app). At this point, our app is launched on background.
A user may tap the local notification just received and then our app's lifecycle state will be changed to foreground and app's home screen will be displayed to the user(from user point of view app is LAUNCHED at this point).
On home view controller, we observe UIApplication.didBecomeActiveNotification and when detecting this notification, we make an API request. API response may have an award field(nullable) and when the award is not null, we show the award received modal.
// Observe app state changes to active
let center = NotificationCenter.default
center.addObserver(
self,
selector: #selector(callRewardAPI), // calls api when detecting notification
name: UIApplication.didBecomeActiveNotification, // <- didBecomeActiveNotification
object: nil
)
func callRewardAPI {
usecase.callRewardAPI)()
}
// Show modal when received API response
extension: HomeViewController, HomeUseCaseOutput {
func didReceiveReward() {
let vc = RewardReceivedViewController()
vc.modalTransitionStyle = .crossDissolve
vc.modalPresentationStyle = .overFullScreen
self.present(vc, animated: true, completion: nil)
}
}
Problem
Expected
We expect to see the modal animation of RewardReceivedViewController.
Actual
When the app has finished launching(from user point of view, from application point of view it has been launched on background and moving to foreground), the modal had already been displayed.
We are quite sure that modal had finished displayed upon launch since view displayed on the zoom animation of our app launch(the iOS animation by apple that app's view zooms in to fill the device screen on app launch) shows the modal being displayed.
Something we are confused is that this does not happen all the time but rathe happens several times and then stopped happening(behaving as is should be) for several times. We checked this on same git commit.
Something we tried
We tried to simulate the problem by triggering modal display right after receiving UIApplication.didBecomeActiveNotification.
func callRewardAPI {
// Call didReceiveReward right away to show modal.
self.ouptut.didReceiveReward()
/*
self.rewardAPI.request { [weak self] result in
switch result {
case .success(let reward):
self.ouptut.didReceiveReward()
case .failure:
// error case
}
}
*/
}
Since it seems we cannot use Xcode debug breakpoint when simulating app launch from not running to background status, we set local push notification to each point of code to observer what is happening.
What we found out is that code to show modal is called after viewDidAppear. viewDidAppear is the last lifecycle method of the view controller when launching a view controller so we're stuck finding out the solution.
I used NSNotification.Name.UIApplicationDidBecomeActive in many places in my app. but I want to disable all of them in just a ViewController for example named vc. in vc I called NotificationCenter.default.removeObserver(self) in viewWillDisappear method. but when I opened a URL in Safari and use the back-to-app button to come back to my app all the notifications were triggered again.
is there a way to disable all of the notifications in my whole application and enable them again?
I implemented push notifications into my app. The user gets a push notification from the server whenever there is a new message for him.
When the user clicks on the push notification, it opens the app. I want my app to reload a tableview when this happens, showing the user the recent newsfeed.
Is it possible in Swift 3?
As I understand, you are interesting in case of app that was opened on your recent newsfeed ViewController, then is closed to background by the user. Later on while receiving push, user clicks on it and opens the same newsfeed controller.
You have to subscribe to UIApplicationWillEnterForeground notification in your UIViewController like
NotificationCenter.default.addObserver(self, selector: #selector(yourMethod), name: NSNotification.Name.UIApplicationWillEnterForeground, object: nil)
and implement yourMethod like
func yourMethod() {
// send request to update newsfeed
// then update tableView
// tableView.reloadData()
}
Don't forget unsubscribe
deinit {
NotificationCenter.default.removeObserver(self)
}
It's the simplest solution and I described the simplest case. There are some more complicated cases I didn't describe, for example
The app is closed and you have to make hierarchy based on the push received in willFinishLaunchingWithOptions.
The app is opened on the other page, in this case you have to reconfigure viewControllers hierarchy to show required ViewController.
In this case you have to pass some data to required ViewController from the pressed Push Notification.
Hope it helps!
I'm working on an iOS project and would like to secure the app. I have been able to create and passcode view controller and it works fine but I want to present the passcode view whenever the app is about to enter the background or before it enters foreground. I know Appdelegate provides useful methods but how do I implement it to show the passcode view controller. I tried implementing the rootviewcontroller to present the passcode viewcontroller in the Appdelegate but it doesn't work and gives debugger message "attempt to present a viewcontroller that's not in the hierachy".
add this line to viewDidLoad() func:
NSNotificationCenter.defaultCenter().addObserver(self, selector: "showPasscodeView:", name: UIApplicationWillEnterForegroundNotification, object: nil)
Then present passcode view in showPasscodeView() func.
I have been writing an app that involves updating several values depending on the time the user is in - I update these values using viewDidAppear but unfortunately this function is not being called when the app is loaded again after being sent to the background.
Is there any way to prevent the app from being sent to the background? Or to force the app to open on a certain page after being opened from the background? Or is there a function that is like viewDidAppear, but is always whenever the app is loaded from the background?
Thanks!
Note:
I tried this:
func applicationDidBecomeActive() {
viewDidAppear(false)
}
inside the viewController class, but it didn't work.
You may be looking for applicationWillEnterForeground(application: UIApplication). This method has to be implemented in AppDelegate, not in a controller