Calling Functions From AppDelegate in swift3 - ios

I'm using OneSignal to process notifications in my app. This is initiated in AppDelegate.swift where there is a function that handles received apps:
let notificationReceivedBlock: OSHandleNotificationReceivedBlock = { notification in
print("Received Notification: \(notification!.payload.body)")
}
I then have a TabViewController with 5 tabs and each tab's navigationItem has a UINavigationBarButton that sends you to a messages view. I want to change the badge number in that button whenever a notification comes in to show that the user is getting new messages. I have a function inside every viewController (each of the 5 tabs) that will update this.
The problem is that the AppDelegate needs to call this function updateBadgeNumber and I don't know how to do this. Plus, some of the tabs may not even have been initialised yet. Does anyone know how to call functions in ViewControllers from the AppDelegate?
Thank you.
EDIT:
Found a solution thanks to #paulvs below. He linked me to an answer where I found this: Find Top View Controller in Swift

The answer depends on whether your UITabBarController is the root view controller of your app.
If your UITabBarController is the root view controller, you likely have a reference to it in your app delegate and can use UITabBarController's selectedViewController property to get the current view controller and then set the badge value. (If you don't have a reference, you can probably cast window.rootViewController to UITabBarController to access it.)
If it's not, you can use code like this to find the currently visible view controller. (Then to access the UITabBarController, you can probably cast parentViewController to a UITabBarController, and from there access the other view controllers and set their badge values, too.)

Related

How to access previous view controller from a dismiss command

Throughout my app I use a navigation controller to push and pop my view controllers. When I pop from one ViewController, I check to see if I can reload the data in the previous one with this:
_ = self.navigationController?.popViewController(animated: true)
if let previousViewController = self.navigationController?.viewControllers.last as? AnimalsVC {
previousViewController.tableView.reloadData()
}
This works every time, but now I need to do the same with another view, but it's not apart of the navigation controller because I modally present (instead of pushing it to the navigation controller). Now there's no way I can access the previous ViewController like before and I can not figure out how to access the previous ViewController.
How can I access the previous ViewController to reload the tableview like the example code without accessing the navigation controller?
I know this is possible with notifications, but I prefer not to use that method if possible!
First of all, It's not necessary to access the previous ViewController to reload tableview(or any other func)
I recommend you to use Delegate to achieve the same feature.
Holding a reference to the previous viewController in the way you mentioned will make your app very hard to maintain when your app gets more complicated.
You can call tableview.reloadData() in viewWillAppear method in the controller that you present modally

iMessage Extensions: Root Navigation Controller results in all delegate methods to not get called in my MSMessagesAppViewController

I have a new iMessage Extension project where I tried 2 ways of structuring the navigation stack:
In my storyboard I set the entry point to a UINavigationController that has my MSMessagesAppViewController as the root controller.
Or I set my MSMessagesAppViewController directly as the Entry point in my storyboard. (No UINavigationController that owns it).
For scenario #1 above, the navigation controller works fine, and I can push new screens on the stack. (with the exception of the whole nav bar being hidden in Expanded view, which is a separate issue that I still have to figure out). However, NONE of the delegate methods of my MSMessagesAppViewController get called with this configuration. Such as:
willTransitionToPresentationStyle
didTransitionToPresentationStyle,
willBecomeActiveWithConversation,
didSelectMessage
(none of these get called)
For scenario #2 above, the MSMessagesAppViewController methods DO get called. (because the UINavigationController is not the entry point in the Storyboard).
So my question is: How can I have a UINavigationController be at the root of my iMessage Extension application, so I can perform Push navigation, but at the same time have the methods of MSMessagesAppViewController get called, as described by the Apple API?
Although it doesn't seem to be documented, it looks like message extensions expect the entry point to be a subclass of MSMessagesAppViewController. Those methods aren't delegate methods, they're superclass overrides, so there's no way to arrange for them to go anywhere else. The message extension system could handle the case you describe but-- aparently-- does not.
What I'd try is:
Make the entry point a subclass of MSMessagesAppViewController.
Early in that object's life cycle (maybe in viewDidLoad) create a UINavigationController and add it as a child view controller of your MSMessagesAppViewController subclass. Make it fill the entire screen.
Now-- in effect-- your navigation controller is the root of the extension. It's not really the root since message events like willTransitionToPresentationStyle will still go through the MSMessagesAppViewController subclass. But everything else starts there. It's the root of the UI and the navigation.
In the meantime it might be good to file an enhancement request with Apple. It's reasonable to think that the message extension system would check the root VC of a navigation controller to see if it's the right class, and maybe they'll add that in the future.

NavigationController StoryboardSegue not showing navigationBar and navigation delegate not called

I recently changed my app structure to include a UINavigationController as base for my hierarchy and I had its root viewController implement the UINavigationControllerDelegate protocol in order to implement custom segues.
My viewController implements navigationController:animationControllerForOperation:fromViewController:toViewController:.
My problem is two-fold:
The navigationController.delegate methods are not being called.
The navigationBar is not called in the views being pushed via storyboardSegues of type show.
The prepareForSegue:sender: function is being called.
This is my UI:
Turns out that UIStoryboardSegues I added before I added the UINavigationController to my hierarchy are still interpreted as modal segues. Probably this is set during creation.
The problem was solved by deleting and re-adding the segues in question, with the relevant information (identifier, class...) transferred to the new instance.
If you have the same problem, when you set Top Par to inferred in your segued viewController you will see no navigationBar showing.
After replacing the segues the Top Bar showed again as normal.
Edit:
I posted the question together with this answer, since there was no post on SO covering this issue. self-answer

how to get currently viewing viewcontroller?

When a push notification arrives, i need to get currently viewing ViewController from the method didReceiveRemoteNotification: of AppDelegate. I have been using both TabbarController and NavigationController in my App. When i try to get it from navigation stack (top item), i get my CustomNavigationController. But i need to get the viewing ViewController (might be an item in tabbar). Would you please help. Thanks in advance.
Just make a variable in viewDidLoad or viewDidAppear for tabBar and set it in appdelegate.
You can check using this current viewController being viewed.

iOS Check if Coming from Particular View Controller

I'm working on an iPhone app where I move through push through several view controllers. On the last on I [self.navigationController popToRootViewControllerAnimated:YES]
I want to ask is there a way to detect that I just came from ViewController7 when i return to the ViewController1?
The reason being i'd like the viewDidAppear to behave in a certain way if it is.
Otherwise is it possible to rerun the ViewDidLoad? (I'm presuming its not).
Thanks.
You could have your viewController1 conform to the UINavigationControllerDelegate protocol and become the UINavigationController's delegate. Then in navigationController:willShowViewController:animated: check if the controller to be shown is viewController1, check your UINavigationController's visibleViewController and set some variable in viewController1. Then in viewDidAppear you can animate appropriately.
I'd use the delegation design pattern to set a protocol method to send information back regarding what view controller you are in.

Resources