ViewController deinit after performing segue using Notification - ios

I'm basically receiving a remote notification and I want to redirect my user to the correct VC as soon as he clicks the notification.
I'm doing it using NSNotificationCenter to perform a segue from my rootVC, leading the user to the correct VC.
NSNotificationCenter.defaultCenter().addObserver(self, selector:
"chooseCorrectVC:", name:chatNotificationKey, object: nil)
Since the observer was previously loaded, my chooseCorrectVC function is called first, so this is my "Init/Deinit" Log. I consider Init whenever viewDidLoad() is called.
rootVC INIT
SecondVC DEINIT
rootVC DEINIT
func chooseCorrectVC(notification:NSNotification){
self.performSegueWithIdentifier("chatSegue", sender: notification)
NSNotificationCenter.defaultCenter().removeObserver(self)
}
The issue is: the VC that is called with chatSegue does not get initialized and goes straight to deinit. I'm not sure why it's happening, maybe I'm not removing the observer correctly.
Any suggestions?

If you are receiving the remote notification, I suggest you to just handle the notification at AppDelegate.swift at method:
func application(application: UIApplication, didReceiveRemoteNotification userInfo: [NSObject : AnyObject]){
// Here you can define view controller and manage it.
println("Received: \(userInfo)")
// Make root view controller first as per your need as HomeViewController in following
let mainStoryboard: UIStoryboard = UIStoryboard(name: "Main", bundle: nil)
var mainTabBarController = mainStoryboard.instantiateViewControllerWithIdentifier("MainTabBarController") as! MainTabBarController
var notificationNavController : UINavigationController = mainTabBarController.viewControllers?.first as! UINavigationController
var homeViewController : HomeViewController = notificationNavController.viewControllers.first as! HomeViewController
homeViewController.isNotified = true
let nav = UINavigationController(rootViewController: mainTabBarController)
self.window!.rootViewController = nav
nav.setNavigationBarHidden(true, animated: false)
self.window?.makeKeyAndVisible()
}
You can manage another view controller to be push on viewdidload of homeviewcontroller via setting flag here. On view did load
if isNotified == true {
// Push another view controller
}

Related

iOS Swift Navigate to certain ViewController programmatically from push notification

I use push notifications to inform the user about different types of events happening in the app. A certain type of push notification should open a special viewcontroller (f.e a notification about a new chat message does navigate to the chat on click)
To achieve this, i tried the following code inside my app delegate:
func applicationDidBecomeActive(_ application: UIApplication) {
if var topController = UIApplication.shared.keyWindow?.rootViewController {
while let presentedViewController = topController.presentedViewController {
topController = presentedViewController
}
topController.tabBarController?.selectedIndex = 3
}
}
It does not move anywhere. What am I missing here?
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's the GitHub project link: https://github.com/oblq/Navigator
This is what you need.
Use your navigation controller to push your notification view controller
let mainStoryBoard : UIStoryboard = UIStoryboard(name: "Main", bundle: nil)
let navigationController : UINavigationController = mainStoryBoard.instantiateViewController(withIdentifier: "MainNavigationController") as! UINavigationController
let notifcontrol : UIViewController = (mainStoryBoard.instantiateViewController(withIdentifier: "NotificationViewController") as? NotificationViewController)!
navigationController.pushViewController(notifcontrol, animated: true)
self.window = UIWindow(frame: UIScreen.main.bounds)
self.window?.rootViewController = navigationController
self.window?.makeKeyAndVisible()
The topController was already my TabBarController in this case!

how to launch different view controllers for different local notifications in ios 10

I am receiving multiple local notifications in my app at different times. I want to launch different view controllers for my app depending on the local notification received. I know about the launch options in didfinishlaunching and didreceive notifications But I dont know how to detect which notification is received and take action according to the notification received.Even when I managed to do that, the problem is that I am able to open a view controller from the app delegate but it is no longer attached to the navigation controller that it is otherwise attached to the storyboard. How to do that? Heres my code:
func application(_ application: UIApplication, didReceive notification: UILocalNotification) {
let storyboard = UIStoryboard(name: "Main", bundle: nil)
let customViewController:WebViewController = storyboard.instantiateViewController(withIdentifier: "webVC") as! WebViewController
let index=Constants.instructionsData.count - 1
customViewController.url=Constants.instructionsData[index].weblink!
self.window = UIWindow(frame: UIScreen.main.bounds)
self.window?.rootViewController = customViewController
self.window?.makeKeyAndVisible()
}
You want to use notification in iOS 10, so I understand you want to use UNNotification. When you use UNNotificationRequest there is argument called identifier. You can use it to distinguish, which notification send this request. If you use the "old" notifications, you just give selector which will be executed when notification is received.
Edited:
In your code you are changing root view controller, so how do you want the navigation controller to last? You are not using segues.
try:
let storyboard = UIStoryboard(name: "Main", bundle: nil)
let customViewController:WebViewController = storyboard.instantiateViewController(withIdentifier: "webVC") as! WebViewController
self.window?.rootViewController?.navigationController?.pushViewController(customViewController, animated: true)

Checking if ViewController is already in background or not

I have an app which starts with different VCs depending whether the user is already logged in or not.
func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
window = UIWindow.init(frame: UIScreen.mainScreen().bounds)
let storyboard = UIStoryboard.init(name: "Main", bundle: nil)
let initialViewController: UIViewController
if DataManager.getInstance().getUserInfo() == nil {
initialViewController = storyboard.instantiateViewControllerWithIdentifier("authenticationViewController")
} else {
initialViewController = storyboard.instantiateViewControllerWithIdentifier("locationsNavigationViewController")
}
window!.rootViewController = initialViewController;
window!.makeKeyAndVisible();
return true
}
If the user is not logged in, the app starts with AuthenticationViewController, otherwise it starts with LocationsNavigationViewController, which is a NavigationViewController
In the latter VC, the is a button for logout. The problem is when the user taps on that button, I don't know if I have to dismiss the LocationsNavigationViewController (because AuthenticationViewController is in background) or if I have to dismiss LocationsNavigationViewController and perform a segue for opening the AuthenticationViewController.
So far, I have just covered the first use case. So in LocationsNavigationViewController I call this function
func showAuthentication() {
dismissViewControllerAnimated(true, completion: nil)
}
But when the app starts with LocationsNavigationViewController dismiss the VC is not enough of course, because the the AuthenticationViewController has never been instantiated.
How can I solve this please?
self.navigationController?.viewControllers
This is an array which will contain all your previous view controller. You can enumerate it and check whether your view controller is exist or not.
If you are using UINavigationController then you can check any UIViewController present or not!
let rootViewController = application.windows[0].rootViewController as! UINavigationController
let mainStoryboard: UIStoryboard = UIStoryboard(name: "Main", bundle: nil)
if !rootViewController.viewControllers.contains(UIViewController_Class()){
let notificationVC = mainStoryboard.instantiateViewControllerWithIdentifier(constInstance.notificationsIdentifier) as! UIViewController_Class_Name
rootViewController.pushViewController(notificationVC, animated: false)
}
Hope this helps!
Thanks to Sohil's answer, I change the showAuthentication function in this way
func showAuthentication() {
//Since the app can start with different VC, I have to check which is the window root VC
if UIApplication.sharedApplication().windows[0].rootViewController is AuthenticationViewController {
dismissViewControllerAnimated(true, completion: nil)
} else {
performSegueWithIdentifier("authenticationSegue", sender: self)
}
}
And added a segue from the NavigationViewController to the AuthenticationViewController, called authenticationSegue
Pretty simple solution.
You can create one function in appdelegate for logout
and in logout function chanege rootviewcontroller of window like
func logoutUser()
{
var login: UIViewController?
login = LoginViewController(nibName : "LoginViewController", bundle : nil)
let nav = UINavigationController(rootViewController: login!)
self.window?.rootViewController = nav
}

How can you create an instructional screen, that only appears the first time (in Swift)

I there a simple way to route a user to a view controller only one time? This ViewController could then be an instructional screen that the user only sees once at startup.
I have this now:
let skipScreen = NSUserDefaults.standardUserDefaults()
#IBAction func skipButton(sender: AnyObject) {
let skipNow = true
skipScreen.setObject(skipNow, forKey: "skip")
skipScreen.synchronize()
}
func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject :AnyObject]?) -> Bool {
self.window = UIWindow(frame: UIScreen.mainScreen().bounds)
var storyboard = UIStoryboard(name: "Main", bundle: nil)
var initialViewController = storyboard.instantiateViewControllerWithIdentifier("ViewController") as UIViewController
self.window?.rootViewController = initialViewController
self.window?.makeKeyAndVisible()
}
override func viewDidLoad() {
super.viewDidLoad()
var skipNow: Bool? = skipScreen.objectForKey("skip") as Bool?
if skipScreen.boolForKey("skip") == true{
var nav = self.view?.rootViewController as UINavigationController
var storyBoard = UIStoryboard(name: "Main", bundle: nil)
nav.pushViewController(storyBoard.instantiateViewControllerWithIdentifier("BookingViewController") as BookingViewController, animated: false)
}
}
But it gives errors with the self.windowparts and says that the viewController does not have a member named 'window'
How could I make this work?
Thanks!
My suggestion for creating tutorial/insturctional screens would be to create images(full screen) with opacity background color and text full visible. Handle showing with NSUserDefaults(after you show one screen, save that to user defaults so you know next time that you shouldn't show that particular image).
Don't forget to create pictures for all screens (for iphone: 4, 5, 6, 6+)
You need to check in your appdelegate's didFinishLaunchingWithOptions method for, lets say a boolean value in your userdefaults, which indicates wether you should start with a "instructional" viewcontroller or the normal viewcontroller.
So you can set the instrucitonal viewcontroller as the default viewcontroller in your storyboard, and you can push the "non-instructional" viewcontroller if it has been shown.
if userDefaults.boolForKey("HasBeenShownInstructions") {
var nav = self.window?.rootViewController as UINavigationController
var storyBoard = UIStoryboard(name: "Main", bundle: nil)
nav.pushViewController(storyBoard.instantiateViewControllerWithIdentifier("BookingViewController") as BookingViewController, animated: false)
}
Dont forget to save the boolean value to userdefaults when the user has dismissed/ended the instructions.
This is just one of many ways to solve the issue. You could also do it the other way around. Depends on what you need.

Check for user in session in AppDelegate and trigger UIViewController if not

I have a Swift application, and what I'd like to do is that every time the app becomes active, I'd like to check for a user in session. If not found, I'd like to show a login view controller that I designed in my Storyboard. If found, I need everything to just resume as usual.
Where is the best way to trigger this check? Is AppDelegate's applicationDidBecomeActive the right place for this?
If yes, how do I actually instantiate the login view controller and show it? I have another home view controller, which is set to be my initial view controller. How do I manage this controller if and when I do end up successfully pushing the login view controller from the app delegate in case there is no user found in session? I don't want the home view controller to show up if a user is not found.
Any advise is greatly appreciated!
Hope this answer can help!
Put this in your AppDelegate.swift file. With the if you can check the value saved in local memory.
func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
var userLoggedIn: Bool = NSUserDefaults.standardUserDefaults().boolForKey("isUserLoggedIn")
if (userLoggedIn){
let mainStoryboard: UIStoryboard = UIStoryboard(name: "Main", bundle: nil)
var homeViewController = mainStoryboard.instantiateViewControllerWithIdentifier("HomeViewController") as!
HomeViewController
window!.rootViewController = homeViewController
}
return true
}
If let's say you store a token in your usersession, we go look if there is a token set or not. If it's already set (so not null) then we go to your other viewcontroller
let prefs = NSUserDefaults.standardUserDefaults()
let checkfortoken = prefs.stringForKey("token")
if(checkfortoken != ""){
let storyboard = UIStoryboard(name: "Main", bundle: nil)
let vc = storyboard.instantiateViewControllerWithIdentifier("identifierofyourview") as UIViewController
self.presentViewController(vc, animated: true, completion: nil)
}
Now you want to check this when you start your app so we gonna put the code into appdelegate in the first function (didFinishLaunchingWithOptions):
func application(application: UIApplication!, didFinishLaunchingWithOptions launchOptions: NSDictionary!) -> Bool {
UIApplication.sharedApplication().setStatusBarStyle(UIStatusBarStyle.LightContent, animated: true)
//so here ...
return true
}

Resources