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!
Related
I have a switch button that will trigger notification alerts. I was able to implement asking for Notification permission (first time the user trigger the switch), I was also able to implement bringing the user to open the app's notification settings when the user click on the switch button again (if the user decline the first request). But I am having problem on how to get that Notification is turn on information back to the app.
App is launch, user click on toggle switch (Notification request dialogue will pop up).
If user decline the initial request and click on toggle switch again, a dialogue will pop up and suggest/bring the user to the app's settings. (the app goes to background, Settings is front)
when the user turn on the Notification Settings, then go back to the app (Settings goes to background, the app comes to foreground).
At this stage, how can I detect Notification setting for the app is turn on and show the switch button as 'on'?
I tried override func viewWillAppear (this doesn't work because the viewcontroller never calls/reload again when you go back to the app from Settings.)
I also tried in AppDelegate, applicationWillEnterForeground and applicationDidBecomeActive, both didn't work either.
I hope this make sense. Thank you for any suggestion.
Import UserNotifications into your ViewController
import UserNotifications
Create a method to check the current notification status
private func hasNotificationPermission(completion: #escaping (Bool) -> Void) {
UNUserNotificationCenter.current().getNotificationSettings { (settings) in
completion(settings.authorizationStatus == .authorized)
}
}
Add observer to detect when app becomes active
override func viewDidLoad() {
super.viewDidLoad()
NotificationCenter.default.addObserver(self, selector: #selector(self.openactivity), name: NSNotification.UIApplicationDidBecomeActive, object: nil)
}
When coming back from background check for Notification status and update your UI
#objc func appBecomeActive() {
if hasNotificationPermission {
// Notifications are enabled
} else {
// Notifications are not enabled
}
}
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 am making an iOS application. Where I have made that I get notification when near certain places. And that works fine. But I then want it to navigate to a certain view when I press the notification.
I am thinking just like when I get a sms. I press that notification for the sms and it then navigate me to the correct sms conversation.
When application receives a push notification, a method in UIApplicationDelegate is called. The notification needs to be handled differently depending on what state your app is in when it’s received:
If app wasn’t running and the user launches it by tapping the push
notification, the push notification is passed to your app in the
launchOptions of
application(_:didFinishLaunchingWithOptions:)
If app was running and in the foreground, the push notification
will not be shown this function will call immediately
application(_:didReceiveRemoteNotification:)
If app was running or suspended in the background and the user brings
it to the foreground by tapping the push notification, this function
will be called.
application(_:didReceiveRemoteNotification:)
So you can change the root view controller on these functions like this :-
if let notification = launchOptions?[UIApplicationLaunchOptionsRemoteNotificationKey] as? [String: AnyObject] {
//Change your root view controller
}
This checks whether the value for UIApplicationLaunchOptionsRemoteNotificationKey exists in launchOptions. If it does, this will be the push notification payload you sent.
I wrote a simple class to navigate to any view controller in the view hierarchy from anywhere in one line of code by just passing the class type, so the code you'll write will be also decoupled from the view hierarchy itself, for instance:
Navigator.find(MyViewController.self)?.doSomethingSync()
Navigator.navigate(to: MyViewController.self)?.doSomethingSync()
..or you can execute methods asynchronously on the main thread also:
Navigator.navigate(to: MyViewController.self) { (MyViewControllerContainer, MyViewControllerInstance) in
MyViewControllerInstance?.doSomethingAsync()
}
Here the GitHub project link: https://github.com/oblq/Navigator
Apologies for the weird headline, can't think of a better way to describe it.
I'm building my app and have managed to get push notifications to work (Yay!).
If I send a push notification to the phone and tap the notification the function didReceiveRemoteNotification: in AppDelegate gets called and works as expected.
If I ignore the notification and load the app as per normally, the UITableView page doesn't refresh and I have to move off the view and then back to get viewDidAppear to refresh the view.
How do I get the app / view to refresh the page when the user navigates back to the app?
Have you tried to get the tableView to reloadData from applicationDidBecomeActive or post a notification to reload your tableview from this method like below?
func applicationDidBecomeActive(application: UIApplication) {
yourVC.tableView.reloadData()
}