I want to logout from application using settings bundle.
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
//enable_logout key for logout switch identifire in setting budle plist.
let userLogout = UserDefaults.standard.bool(forKey: "enabled_logout")
print(userLogout)
let userLogin = UserDefaults.standard.bool(forKey: "isUserLogin")
if userLogin {
let homeController = HomeController()
let homeNav = UINavigationController.init(rootViewController: homeController)
let aboutController = AboutController()
let aboutNav = UINavigationController.init(rootViewController: aboutController)
let userBaseController = UserBaseInfoController()
let userBaseNav = UINavigationController.init(rootViewController: userBaseController)
tabbarController.viewControllers =[homeNav,userBaseNav,aboutNav]
self.window?.rootViewController = tabbarController
}
else {
let login = LoginController()
self.window?.rootViewController = login
}
return true
}
I'm added this code in appDelegate, I want to when the user enables logout switch in setting and then return to application show login view, but when enables switch and back to app appDelegate not call and my key not change.
There is my setting view :
I am solve this problem, instead check enable_logout key in didFinishLaunchingWithOptions method, I checked in applicationWillEnterForeground methods.
Here is my code:
func applicationWillEnterForeground(_ application: UIApplication) {
let userLogout = UserDefaults.standard.bool(forKey: "enable_logout")
print(userLogout)
if !userLogout {
let homeController = HomeController()
let homeNav = UINavigationController.init(rootViewController: homeController)
let aboutController = AboutController()
let aboutNav = UINavigationController.init(rootViewController: aboutController)
let userBaseController = UserBaseInfoController()
let userBaseNav = UINavigationController.init(rootViewController: userBaseController)
tabbarController.viewControllers = [homeNav,userBaseNav,aboutNav]
self.window?.rootViewController = tabbarController
}
else {
let login = LoginController()
self.window?.rootViewController = login
}
}
Related
I want to test my application's behavior that decided on app launch. For example: In a tab bar controller, how many and which tabs will be created is been decided on app launch where the root window has been created so I want to test these behaviors for each test case.
This new feature is set via A/B service and the value retrieved only during app launching. Based on that value, the tab bar's view controllers are set.
For example:
var viewControllers: [UIViewController] = [ tabOne, tabTwo]
if Config.isNewFeatureEnabled {
viewControllers.append(self._menuCoordinator.rootViewController)
} else {
viewControllers.append(self._anotherTabBarController)
viewControllers.append(self._anotherCoordinator.rootViewController)
viewControllers.append(self._someOtherCoordinator.rootViewController)
}
_tabBarController.viewControllers = viewControllers
Let me put in code, in order to make tests easy I created a protocol (not necessarily but better approach for injection)
protocol FeatureFlag {
var isNewFeatureEnabled: Bool { get set }
}
// Implementation
class FeatureFlagService: FeatureFlag {
var isNewFeatureEnabled = false
// Bunch of other feature flags
}
In my test cases I want to switch the config with out effecting other side of the app. Something like this:
class NewFeatureVisibilityTests: XCTestCase {
func test_TabBar_has_threeTabs_when_NewFeature_isEnabled() {
// Looking for a way to inject the config
let tabBar = getKeyWindow()?.rootViewController as? UITabBarController
guard let tabBar = appDel.currentWindow?.rootViewController as? UITabBarController else {
return XCTFail("Expected root view controller to be a tab bar controller")
}
XCTAssertEqual(tabBar.viewControllers?.count, 3)
}
func test_TabBar_has_fiveTabs_when_NewFeature_isDisabled() {
// Looking for a way to inject the config
let tabBar = getKeyWindow()?.rootViewController as? UITabBarController
guard let tabBar = appDel.currentWindow?.rootViewController as? UITabBarController else {
return XCTFail("Expected root view controller to be a tab bar controller")
}
XCTAssertEqual(tabBar.viewControllers?.count, 5)
}
}
What I want is set application's behaviour through injection (a config etc) for each test case.
One test the feature will be enabled, other test will assert the feature disabled state.
Create a config property in AppDelegate using existing type of FeatureFlag along with a default value on override init.
extension UIApplication {
var currentWindow: UIWindow {
return (connectedScenes
.filter({$0.activationState == .foregroundActive})
.compactMap({$0 as? UIWindowScene})
.first?.windows
.filter({$0.isKeyWindow}).first!)!
}
}
#main
class AppDelegate: UIResponder, UIApplicationDelegate {
var window: UIWindow?
let config: FeatureFlag!
override init() {
config = FeatureFlagService()
}
init(config: FeatureFlag!) {
self.config = config
}
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
// Override point for customization after application launch.
// Create a tabBar with 3 tabs
let tabBarController = UITabBarController()
let firstViewController = UIViewController()
let secondViewController = UIViewController()
let thirdViewController = UIViewController()
let fourthViewController = UIViewController()
let fifthViewController = UIViewController()
firstViewController.tabBarItem = UITabBarItem(tabBarSystemItem: .favorites, tag: 0)
secondViewController.tabBarItem = UITabBarItem(tabBarSystemItem: .downloads, tag: 1)
thirdViewController.tabBarItem = UITabBarItem(tabBarSystemItem: .more, tag: 2)
fourthViewController.tabBarItem = UITabBarItem(tabBarSystemItem: .bookmarks, tag: 3)
fifthViewController.tabBarItem = UITabBarItem(tabBarSystemItem: .contacts, tag: 4)
var viewControllers = [firstViewController, secondViewController]
if config.isNewFeatureEnabled {
viewControllers.append(thirdViewController)
} else {
viewControllers.append(fourthViewController)
viewControllers.append(fifthViewController)
}
tabBarController.viewControllers = viewControllers
// Create a window and set the root view controller
let window = UIWindow(frame: UIScreen.main.bounds)
window.rootViewController = tabBarController
window.makeKeyAndVisible()
self.window = window
return true
}
}
And in tests, I set my config, create an instance of AppDelegate, inject the config, and launching the application through appDelegate.application(UIApplication.shared, didFinishLaunchingWithOptions: nil) function of AppDelegate.
let appDelegate = AppDelegate(config: config)
// This is the key function
_ = appDelegate.application(UIApplication.shared, didFinishLaunchingWithOptions: nil)
Tests:
import XCTest
#testable import ExampleApp
final class NewFeatureVisibilityTests: XCTestCase {
func test_app_can_start_with_isNewFeatureEnabled(){
let config = FeatureFlagService()
config.isNewFeatureEnabled = true
let appDelegate = AppDelegate(config: config)
// This is the key function
_ = appDelegate.application(UIApplication.shared, didFinishLaunchingWithOptions: nil)
guard let rootVC = UIApplication.shared.currentWindow.rootViewController as? UITabBarController else {
return XCTFail("RootViewController is nil")
}
XCTAssertEqual(rootVC.viewControllers?.count, 3)
}
func test_app_can_start_with_isNewFeatureDisabled(){
let config = FeatureFlagService()
config.isNewFeatureEnabled = false
let appDelegate = AppDelegate(config: config)
// This is the key function
_ = appDelegate.application(UIApplication.shared, didFinishLaunchingWithOptions: nil)
guard let rootVC = UIApplication.shared.currentWindow.rootViewController as? UITabBarController else {
return XCTFail("RootViewController is nil")
}
XCTAssertEqual(rootVC.viewControllers?.count, 4)
}
}
In appdelegate i am getting UserId.. means i am login but when i run second time i am not getting homeviewcontroller as rootviewcontroller why? still it shows phonenumviewcontroller
navigationcontroller -> phonenumviewcontroller -> registrationiewcontroller -> homeviewcontroller
In storyboard navigationcontroller is initialviewcontroller
In registrationviewcontroller i am getting userId which i have saved in keychain.
I dont have signout button so i have written code like below in registrationiewcontroller
do {
let json = try JSONSerialization.jsonObject(with: data!, options: .mutableContainers) as! [String:AnyObject]
print("terms and condition JSON \(json)")
let jsonObj: String = json["Success"] as? String ?? ""
if jsonObj == "true" {
let userID: String=jsonObj?["userId"] as? String ?? ""
DispatchQueue.main.async {
KeychainWrapper.standard.set(userID, forKey: "USERID")
let viewController = self.storyboard?.instantiateViewController(withIdentifier: "HomeViewController") as! HomeViewController;
UserDefaults.standard.set("NoLogout", forKey:"Signout")
self.navigationController?.pushViewController(viewController, animated: true);
}
}
}
no signout button so added this code in registrationviewcontroller
UserDefaults.standard.set("NoLogout", forKey:"Signout")
this code in appdelegate: getting userId but still homeviewcontroller is not coming as rootviewcontroller, only phonenumviewcontroller is coming why?
var savedUserId: String?
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
savedUserId = KeychainWrapper.standard.string(forKey: "USERID")
KeychainWrapper.standard.set(savedUserId ?? "", forKey: "PersonalID")
print("appdelegate userid \(savedUserId)")
logOutString = UserDefaults.standard.string(forKey: "Signout") as NSString? ?? "" as NSString
if logOutString.isEqual(to: "NoLogout") {
self.window = UIWindow(frame: UIScreen.main.bounds)
let storyBoard = UIStoryboard.init(name: "Main", bundle: nil)
let viewcontroller = storyBoard.instantiateViewController(withIdentifier: "HomeViewController")
let navigationController = UINavigationController(rootViewController: viewcontroller)
self.window?.rootViewController = navigationController
self.window?.makeKeyAndVisible()
}
else {
}
return true
}
once registration is completed i need rootviewcontroller as homeviewcontroller... how to get that, please help me with code
From whatever I've gathered, here's what you need to do:
In AppDelegate:
import UIKit
#UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {
var window: UIWindow?
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
let isUserLoggedIn = UserDefaults.standard.bool(forKey: "isUserLoggedIn") // Give you own check to see if user is logged in
window = UIWindow()
window?.makeKeyAndVisible()
let viewController = isUserLoggedIn ? MainViewController() : LoginViewController()
window?.rootViewController = UINavigationController(rootViewController: viewController)
return true
}
}
class MainViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = .green
navigationItem.rightBarButtonItem = UIBarButtonItem(title: "Logout", style: .plain, target: self, action: #selector(handleUserLoggedOut))
}
#objc func handleUserLoggedOut() {
UserDefaults.standard.set(false, forKey: "isUserLoggedIn")
UIApplication.shared.keyWindow?.rootViewController = UINavigationController(rootViewController: LoginViewController())
}
}
class LoginViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = .red
navigationItem.rightBarButtonItem = UIBarButtonItem(title: "Login", style: .plain, target: self, action: #selector(handleUserLoggedIn))
}
#objc func handleUserLoggedIn() {
UserDefaults.standard.set(true, forKey: "isUserLoggedIn")
UIApplication.shared.keyWindow?.rootViewController = UINavigationController(rootViewController: MainViewController())
}
}
This is only a skeleton. Give both controller different background colours and button action to call handleUserLoginIn and handleUserLoggedOut on respective controllers and see how it works. Play around with it and figure out.
Note: keyWindow is deprecated so you need to use this instead at all places.
let keyWindow = UIApplication.shared.connectedScenes
.filter({$0.activationState == .foregroundActive})
.map({$0 as? UIWindowScene})
.compactMap({$0})
.first?.windows
.filter({$0.isKeyWindow}).first
Edit: I've also added the login, logout buttons and set backgroundColors for you to see for yourself the result.
I have included kukushi side menu. I have done things according to the documentation. The screen shot with the codes in app delegate are below:
func setUpHomeVC() {
var window: UIWindow?
let storyBoard = UIStoryboard.init(name: "Dashboard", bundle: Bundle.main)
let contentViewController = storyBoard.instantiateViewController(withIdentifier: "DashboardViewController") as! DashboardViewController
let menuViewController = storyBoard.instantiateViewController(withIdentifier: "MenuViewCOntroller") as! MenuViewCOntroller
SideMenuController.preferences.basic.menuWidth = 240
SideMenuController.preferences.basic.statusBarBehavior = .hideOnMenu
SideMenuController.preferences.basic.position = .sideBySide
SideMenuController.preferences.basic.direction = .left
SideMenuController.preferences.basic.enablePanGesture = true
SideMenuController.preferences.basic.supportedOrientations = .portrait
SideMenuController.preferences.basic.shouldRespectLanguageDirection = true
window = UIWindow(frame: UIScreen.main.bounds)
window?.rootViewController = SideMenuController(contentViewController: contentViewController,
menuViewController: menuViewController)
window?.makeKeyAndVisible()
}
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
// Override point for customization after application launch.
setUpHomeVC()
return true
}
The identifier, class and module has been added according to the documentation. After login there is dashboard which consist of menu button. On login the code is:
private func goToDashboard() {
let dashboard = UIStoryboard(name: "Dashboard", bundle: nil)
let navView = dashboard.instantiateViewController(identifier: "DashboardViewController") as DashboardViewController
present(navView,animated: false)
}
On dashboard there is a button which have click event:
#IBAction func btnMenuClicked(_ sender: Any) {
print("Menu button has been clicked")
self.sideMenuController?.revealMenu(animated: true)
}
when I click on that button the print function is called but the menu is not revealed.
Can anyone explain it. Thanks in advance.
You can setup your appDelegate like this,
func setUpHomeVC() {
let storyboard = UIStoryboard(name: "Your Login Storyboard", bundle: nil)
let initialViewController = storyboard.instantiateViewController(withIdentifier: "LoginVC")
self.window?.rootViewController = initialViewController
}
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
// Override point for customization after application launch.
setUpHomeVC()
return true
}
And in your login event:
private func goToDashboard() {
self.pushVC()
}
private func pushVC() {
let storyBoard = UIStoryboard.init(name: "Dashboard", bundle: Bundle.main)
let contentViewController = storyBoard.instantiateViewController(withIdentifier: "DashboardViewController") as! DashboardViewController
let menuViewController = storyBoard.instantiateViewController(withIdentifier: "MenuViewCOntroller") as! MenuViewCOntroller
SideMenuController.preferences.basic.menuWidth = 240
SideMenuController.preferences.basic.statusBarBehavior = .hideOnMenu
SideMenuController.preferences.basic.position = .sideBySide
SideMenuController.preferences.basic.direction = .left
SideMenuController.preferences.basic.enablePanGesture = true
SideMenuController.preferences.basic.supportedOrientations = .portrait
SideMenuController.preferences.basic.shouldRespectLanguageDirection = true
window = UIWindow(frame: UIScreen.main.bounds)
window?.rootViewController = SideMenuController(contentViewController: contentViewController,
menuViewController: menuViewController)
window?.makeKeyAndVisible()
}
Your DashboardVC should be in a navigation controller for sidemenu to present. Try pushing the controller instead of presenting it.If you have the controller in a different storyboard, you can use this function:
func pushVC(storyboardName : String, vcname : String) {
let vc = UIStoryboard.init(name: storyboardName, bundle: Bundle.main).instantiateViewController(withIdentifier: vcname)
self.navigationController?.pushViewController(vc, animated: true)
}
Also, I would suggest you learn about when to push, present, and make root view controllers as all serve different purposes.
I think your current implementation is wrong. The problem is we need to implement and push the view controllers as SideMenuControllers bundle, not separate ViewControlers
If you want to have the side menu after login, then set your login page first in your didFinishLaunchingWithOptions.
Then you can call setUpHomeVC from your loginVC.
I've set up a UIViewController as my rootViewController in AppDelegate, however, when a user logs in or skips it I am presenting a UITabBarController over the top.
I need to dismiss the LoginController, and set the UITabController as rootViewController instead after user logs in.
How can I go about reorganizing this?
AppDelegate()
window = UIWindow()
window?.makeKeyAndVisible()
window?.rootViewController = LoginController()
LoginController()
self.present(MainTabBarController(), animated: true, completion: nil)
you can design your code like this.
This one
It's too easy to maintain condition for user logged in or not, and then based on that status. You can navigate to your view.
You can try this way :
1.In AppDelegate you can these methods.
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
// Override point for customization after application launch.
let isLogin = UserDefaults.standard.bool(forKey: "IS_LOGIN")
if isLogin == true {
self.goToDashboardView()
} else {
self.goToLoginView()
}
return true
}
//MARK:- ------- Global Methods -------
class func sharedInstance() -> AppDelegate {
return UIApplication.shared.delegate as! AppDelegate
}
func goToLoginView() {
let sb = UIStoryboard.init(name: "Main", bundle: nil)
let loginVC = sb.instantiateViewController(withIdentifier: "LoginViewController") as! LoginViewController
let navVC = UINavigationController(rootViewController: loginVC) // You can skip this if you do not want to add navigation bar
self.window?.rootViewController = navVC // if you skipped above line, then you have to assign 'loginVC' here.
self.window?.makeKeyAndVisible()
}
func goToDashboardView() {
let sb = UIStoryboard.init(name: "Main", bundle: nil)
let tabbarVC = sb.instantiateViewController(withIdentifier: "MyTabBarController") as! MyTabBarController
self).window?.rootViewController = tabbarVC // if you skipped above line, then you have to assign 'loginVC' here.
self.window?.makeKeyAndVisible()
}
2.In LoginViewController, when user logged in successfully.
#IBAction func btnLoginClicked(_ sender: UIButton) {
// Your API call or other code
// If all things goes well, then login and go to dashboard
UserDefaults.standard.set(true, forKey: "IS_LOGIN")
AppDelegate.sharedInstance().goToDashboardView()
}
3.And finally, whenever and from wherever you want to log out from app, just call below code.
#IBAction func btnLogOutClicked(_ sender: UIButton) {
// Your API call or other code
// If all things goes well, then logout and go to login view
UserDefaults.standard.set(false, forKey: "IS_LOGIN")
AppDelegate.sharedInstance().goToLoginView()
}
4.And Main.storyboard should have design something like :
you do not need to present the UITabBarViewController and then dismiss the LoginViewController, you just need to reset the UITabBarViewController as a rootViewController for you app after login or skip as the following:
func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
self.window = UIWindow(frame: UIScreen.mainScreen().bounds)
// Override point for customization after application launch.
let yourTabBar = UIStoryboard(name: "YOUR_STORYBOARD_NAME", bundle: nil).instantiateViewController(withIdentifier: "YOUR_UITABBARCONTROLLER_ID")
self.window!.rootViewController = yourTabBar
self.window!.makeKeyAndVisible()
return true
}
I have 2 segment in onesignal panel bir EN another FR but in my app have language change and people can change app language everytime in app inside , when I send push from one signal I send by segments but i want to add appdelegate inside language section if user language is English only must be alert English pushes , if user language France only must be alert France pushes.
In my app side language selection string is
let appcurrentlanguage = prefs.value(forKey: "lang") as! String
How can I do it ?
My codes under below
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
// Override point for customization after application launch.
//HERE APP LANGUAGE
let appcurrentlanguage = prefs.value(forKey: "lang") as! String
GMSServices.provideAPIKey("xxx")
OneSignal.initWithLaunchOptions(launchOptions, appId: "xxx") { (result) in
let payload = result?.notification.payload
if let additionalData = payload?.additionalData, let actionSelected = additionalData["comes"] as? String {
if actionSelected == "1" {
let mainStoryboardIpad : UIStoryboard = UIStoryboard(name: "Main", bundle: nil)
let initialViewControlleripad : UIViewController = mainStoryboardIpad.instantiateViewController(withIdentifier: "ViewController") as! ViewController
self.window = UIWindow(frame: UIScreen.main.bounds)
self.window?.rootViewController = initialViewControlleripad
self.window?.makeKeyAndVisible()
}else if actionSelected == "2" {
if let newsID = additionalData["id"] as? String {
prefs.setValue(newsID, forKeyPath: "gelenID")
prefs.synchronize()
}
if let newsType = additionalData["type"] as? String {
prefs.setValue(newsType, forKeyPath: "newsType")
prefs.synchronize()
}
}
}
}
sleep(2)
return true
}