reason: '-[FaveoHelpdeskPro_Swift.AppDelegate window]: unrecognized selector sent to instance 0x282efc980' - ios

I am getting following error when I am trying to run the project, my project is crashing.
It is giving following error,
FaveoHelpdeskPro_Swift[1400:370341]
-[FaveoHelpdeskPro_Swift.AppDelegate window]: unrecognized selector sent to instance 0x282efc980
and
*** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason:
'-[FaveoHelpdeskPro_Swift.AppDelegate window]: unrecognized selector
sent to instance 0x282efc980'
and it coming to following screen,
and my SceneDelegate.swift contains already window object,
class SceneDelegate: UIResponder, UIWindowSceneDelegate {
var window: UIWindow?
func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
// Use this method to optionally configure and attach the UIWindow `window` to the provided UIWindowScene `scene`.
// If using a storyboard, the `window` property will automatically be initialized and attached to the scene.
// This delegate does not imply the connecting scene or session are new (see `application:configurationForConnectingSceneSession` instead).
guard let _ = (scene as? UIWindowScene) else { return }
}
what is the issue, why it is crashing ?
Here is the complete project

Your AppDelegate does not contain a window reference. Add a window variable in your AppDelegate
var window: UIWindow?
An excerpt from Apple's documentation
This property contains the window used to present the app’s visual content on the device’s main screen.
Implementation of this property is required if your app’s Info.plist file contains the UIMainStoryboardFile key

When you start to init your main ViewController in SceneDelegate in the project and some UI libraries (like SVProgressHUD,...) still point to AppDelegate's window and nothing there, so it crashes. I have met and successfully fixed this problem following this way:
Declare an UIWindow in AppDelegate if it's not there, also declare a static variable for fast access to AppDelegate:
#UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {
var window: UIWindow? // HERE, add it if it's not available.
static var shared: AppDelegate { return UIApplication.shared.delegate as! AppDelegate }
//...
}
In SceneDelegate, make a reference of UIWindow object back to AppDelegate:
#available(iOS 13.0, *)
class SceneDelegate: UIResponder, UIWindowSceneDelegate {
var window: UIWindow? // HERE
func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
guard let windowScene = (scene as? UIWindowScene) else { return }
window = UIWindow(frame: windowScene.coordinateSpace.bounds)
window?.windowScene = windowScene
window?.rootViewController = YOUR_VIEW_CONTROLLER()
window?.makeKeyAndVisible()
AppDelegate.shared.window = window // Connect it HERE!
}
// ....
}
Note: If you just finish step 1, it will not crash but some UI component will display wrong because they cannot determine which is the main Window.

So what I just did is I added a UIWindow var to my AppDelegate like so:
#UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {
var window: UIWindow?
...
Then in the SceneDelegate, when I am setting the UIWindow property:
class SceneDelegate: UIResponder, UIWindowSceneDelegate {
var window: UIWindow?
func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
guard let windowScene = (scene as? UIWindowScene) else { return }
window = UIWindow(frame: windowScene.coordinateSpace.bounds)
window?.rootViewController = MainTabBarController()
window?.windowScene = windowScene
window?.makeKeyAndVisible()
I also set it for the AppDelegate as well right below like so:
if let appDelegate = UIApplication.shared.delegate as? AppDelegate {
appDelegate.window = window
}

Related

AppDelegate to SceneDelegate

I had an old project that I was working which doesn't has the sceneDelegate. My AppDelegate didFinishLaunchingWithOptions looks like :
import Firebase
import customFramework
class AppDelegate: UIResponder, UIApplicationDelegate {
var window: UIWindow?
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
FirebaseApp.configure()
// Create window
window = UIWindow(frame: UIScreen.main.bounds)
window?.rootViewController = ViewController()
window?.makeKeyAndVisible()
customFrameworkManager.shared.start()
return true
}
.
.
.
I wanted to transform this code and hence decided to create a new fresh project but now there is an implementation of scene Delegate instead. I tried changing my func scene (willConnectTo) which looked like :
func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
// Use this method to optionally configure and attach the UIWindow `window` to the provided UIWindowScene `scene`.
// If using a storyboard, the `window` property will automatically be initialized and attached to the scene.
// This delegate does not imply the connecting scene or session are new (see `application:configurationForConnectingSceneSession` instead).
guard let _ = (scene as? UIWindowScene) else { return }
if let windowScene = scene as? UIWindowScene {
self.window = UIWindow(windowScene: windowScene)
self.window!.makeKeyAndVisible()
}
customFrameworkManager.shared.start()
}
But the customFramework is not launching in on the application. Can anyone suggest what wrong am I doing here. Any help would be appreciated.
Thanks
In your sceneDelegate I don't see the rootViewController, try like this:
guard let windowScene = (scene as? UIWindowScene) else { return }
let window = UIWindow(windowScene: windowScene)
let controller = ViewController()
window.rootViewController = controller
self.window = window
window.makeKeyAndVisible()
customFrameworkManager.shared.start()

iOS Swift Firebase Checking user at app startup not working

I developed an app with firebase. The application checks to see if any users are logged in every boot in sceneDelegate.swift file. But its works only iOS 13 devices. iOS 12 devices users has to log in every time. How can I fix this problem? Thanks.
class SceneDelegate: UIResponder, UIWindowSceneDelegate {
var window: UIWindow?
#available(iOS 13.0, *)
func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
// Use this method to optionally configure and attach the UIWindow `window` to the provided UIWindowScene `scene`.
// If using a storyboard, the `window` property will automatically be initialized and attached to the scene.
// This delegate does not imply the connecting scene or session are new (see `application:configurationForConnectingSceneSession` instead).
let currentUser = Auth.auth().currentUser
if currentUser != nil {
let board = UIStoryboard(name: "Main", bundle: nil)
let navigationController = board.instantiateViewController(identifier: "navigationController") as! UINavigationController
window?.rootViewController = navigationController
}
guard let _ = (scene as? UIWindowScene) else { return }
}
SceneDelegate was introduced in iOS 13. For iOS 12 and below you should implement this same logic in the AppDelegate's didFinishLaunchingWithOptions method like this:
#UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {
var window: UIWindow?
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
window = UIWindow()
window?.makeKeyAndVisible()
let currentUser = Auth.auth().currentUser
if currentUser != nil {
let board = UIStoryboard(name: "Main", bundle: nil)
let navigationController = board.instantiateViewController(withIdentifier: "navigationController") as! UINavigationController
window?.rootViewController = navigationController
}
return true
}
}

Update root view controller after user login + iOS 13 and later

Using scene delegate I'm able to set the root view controller.(I'm using Xcode 11.3 and iOS version 13.3 and running my app on iPhone 6+ with iOS 12.4)
What I want is when user login, I need to update the root view controller. For that I did the following
class SceneDelegate: UIResponder, UIWindowSceneDelegate {
var window: UIWindow?
static let shared = SceneDelegate()
func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
//some code is here
}
}
#available(iOS 13.0, *)
extension SceneDelegate {
func setRootViewControllerBasedOnLogin() {
if let isLoggedIn = UserDefaults.standard.bool(forKey: "isLogin"), isLoggedIn {
let tabbar = UIStoryboard(name: "Other", bundle: nil).instantiateViewController(withIdentifier: "Tabbar") as! UITabBarController
if var vcs = tabbar.viewControllers {
vcs.remove(at: 2)
tabbar.viewControllers = vcs
}
self.window?.rootViewController = tabbar
} else {
//other stuff
}
}
}
So when user login in to the app I need to remove a tab item from tab bar and update the root view controller.
So I'm doing as follows.
func processLogin() {
//performing login in this method so when login successful we setting root view controller
callLoginAPI { response in
if response.isSuccess {
UserDefaults.standard.set(true, forKey: "isLogin")
if #available(iOS 13.0, *) {
SceneDelegate.shared.setRootViewControllerBasedOnLogin()
} else {
// Fallback on earlier versions
}
}
}
}
When I'm doing this nothing happened. I'm not able to change the root view controller of the app after user successfully login into the app?
Any suggestions? what am I doing wrong?
This is how I managed navigation for both the older version and the new version. So when the user has the latest iOS we need to setup root from sceneDelegate and for older version we need to setup root from appDelegate
AppDelegate.swift
class AppDelegate: UIResponder, UIApplicationDelegate {
var window: UIWindow?
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
if #available(iOS 13, *) {
} else {
setupRoot()
}
return true
}
// MARK: UISceneSession Lifecycle
#available(iOS 13.0, *)
func application(_ application: UIApplication, configurationForConnecting connectingSceneSession: UISceneSession, options: UIScene.ConnectionOptions) -> UISceneConfiguration {
return UISceneConfiguration(name: "Default Configuration", sessionRole: connectingSceneSession.role)
}
#available(iOS 13.0, *)
func application(_ application: UIApplication, didDiscardSceneSessions sceneSessions: Set<UISceneSession>) {
}
func setupRoot() {
//Setup Your Root Here
//window?.rootViewController = objNavigationVC
//window?.makeKeyAndVisible()
}
}
SceneDelegate.swift
#available(iOS 13.0, *)
class SceneDelegate: UIResponder, UIWindowSceneDelegate {
var window: UIWindow?
func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
guard let _ = (scene as? UIWindowScene) else { return }
let appDelegate = UIApplication.shared.delegate as! AppDelegate
appDelegate.window = window
appDelegate.setupRoot()
}
}
func presentYourView(from view: YourViwe) {
if #available(iOS 13, *) {
let mySceneDelegate = view.view.window?.windowScene?.delegate
if let sceneDelegate = mySceneDelegate as? SceneDelegate {
sceneDelegate.changeRootViewController(newViewController())
}
} else {
(UIApplication.shared.delegate as? AppDelegate)?.changeRootViewController(newViewController())
}
}
Update Swift 5+ , Xcode 13+
To change rootViewController depending upon user has logged in or not, here is the full code for SceneDelegate
class SceneDelegate: UIResponder, UIWindowSceneDelegate {
var window: UIWindow?
func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
// Use this method to optionally configure and attach the UIWindow `window` to the provided UIWindowScene `scene`.
// If using a storyboard, the `window` property will automatically be initialized and attached to the scene.
// This delegate does not imply the connecting scene or session are new (see `application:configurationForConnectingSceneSession` instead).
guard let _ = (scene as? UIWindowScene) else { return }
if UserDefaultHelper.isLoggedIn! {
print("User logged in")
let mainStoryboard: UIStoryboard = UIStoryboard(name: "Main", bundle: nil) // this assumes your storyboard is titled "Main.storyboard"
let yourVC = mainStoryboard.instantiateViewController(withIdentifier: "CustomerMainViewController") as! CustomerMainViewController // inside "YOUR_VC_IDENTIFIER" substitute the Storyboard ID you created in step 2 for the view controller you want to open here. And substitute YourViewController with the name of your view controller, like, for example, ViewController2.
self.window?.rootViewController = yourVC
self.window?.makeKeyAndVisible()
}
else {
print("User Not logged in")
}
}
func sceneDidDisconnect(_ scene: UIScene) {
// Called as the scene is being released by the system.
// This occurs shortly after the scene enters the background, or when its session is discarded.
// Release any resources associated with this scene that can be re-created the next time the scene connects.
// The scene may re-connect later, as its session was not necessarily discarded (see `application:didDiscardSceneSessions` instead).
}
func sceneDidBecomeActive(_ scene: UIScene) {
// Called when the scene has moved from an inactive state to an active state.
// Use this method to restart any tasks that were paused (or not yet started) when the scene was inactive.
}
func sceneWillResignActive(_ scene: UIScene) {
// Called when the scene will move from an active state to an inactive state.
// This may occur due to temporary interruptions (ex. an incoming phone call).
}
func sceneWillEnterForeground(_ scene: UIScene) {
// Called as the scene transitions from the background to the foreground.
// Use this method to undo the changes made on entering the background.
}
func sceneDidEnterBackground(_ scene: UIScene) {
// Called as the scene transitions from the foreground to the background.
// Use this method to save data, release shared resources, and store enough scene-specific state information
// to restore the scene back to its current state.
}
}
The problem with PinkeshGjr's answer is that it discardings the window object provided by the scene. Here's what I feel is a better/simpler approach:
#UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {
static var current: AppDelegate {
return UIApplication.shared.delegate as! AppDelegate
}
var window: UIWindow?
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
if #available(iOS 13, *) {
} else {
window = UIWindow();
setUpRoot()
}
return true
}
func setUpRoot() {
window?.rootViewController = ViewController(nibName: nil, bundle: nil)
window?.makeKeyAndVisible()
}
}
#available(iOS 13.0, *)
class SceneDelegate: UIResponder, UIWindowSceneDelegate {
func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
guard let windowScene = scene as? UIWindowScene else { fatalError() }
let firstWindow = windowScene.windows.first ?? UIWindow(windowScene: windowScene)
AppDelegate.current.window = firstWindow
AppDelegate.current.setUpRoot()
}
}

How to write to a SwiftUI environment object from a class (-extension)

Given the following setup:
Environment Variable UserState
class UserState: ObservableObject {
#Published var loggedIn = Auth.auth().currentUser != nil
}
UserState as a variable in SceneDelegate
class SceneDelegate: UIResponder, UIWindowSceneDelegate {
...
//creating the variable
var userState = UserState()
func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
window.rootViewController = UIHostingController(rootView: ContentView().environmentObject(userState))
}
...
}
I can now perfectly read/write to this variable in SwiftUI views by declaring
struct ProfileTab: View {
#EnvironmentObject var userState: UserState
var body: some View {
// here I could easily read/write to userState
}
}
So far so good. But:
What is the proper way to write to this variable outside of a SwiftUI view? E.g. from a class or class extension.
Example
extension AppDelegate {
func something(loggedIn: Bool) {
// here I would like to access the environment variable. Something like
// userState.loggedIn = loggedIn
}
}
Here is possible approach...
class AppDelegate: UIResponder, UIApplicationDelegate {
//creating the variable in AppDelegate to have it shared
var userState = UserState()
...
so, then you can ...
extension AppDelegate {
func something(loggedIn: Bool) {
// just use it here as now it is own property
self.userState.loggedIn = loggedIn
}
}
and use it in scene delegate via shared application instance
class SceneDelegate: UIResponder, UIWindowSceneDelegate {
...
func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
// safe as it is known the one AppDelegate
let appDelegate = UIApplication.shared.delegate as! AppDelegate
window.rootViewController = UIHostingController(rootView:
ContentView().environmentObject(appDelegate.userState))
}

Set rootViewController iOS 13

After upgrading Xcode a critical part of my application has stopped working.
When my app launches I run a function to check boolean flags and set the correct rootViewController.
But the code I have been using to set this has now stopped working
class func setLoginAsInitialViewContoller(window:UIWindow) {
print("SET LOGIN")
let storyboard = UIStoryboard(name: "Login", bundle: nil)
let controller = storyboard.instantiateViewController(withIdentifier: "LoginViewController")
controller.modalPresentationStyle = .overFullScreen
window.rootViewController = controller
window.makeKeyAndVisible()
}
Specifically when the app gets the the second last line window.rootViewController = controller it crashes with a libc++abi.dylib: terminating with uncaught exception of type NSException error.
The above function is in a class called Utilities.swift and I am calling the function from within my AppDelegate.swift as shown below:
class AppDelegate: UIResponder, UIApplicationDelegate {
var window: UIWindow?
var storyboard: UIStoryboard? = nil
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
UIApplication.shared.isIdleTimerDisabled = true
Utilities.decideInitialViewController(window: self.window!)
return true
}
Any solutions or fixes on how I can set the root controller is much appreciated.
Thank!
This is because AppDelegate doesn't have window property anymore.
Now you must use SceneDelegate's scene(_:willConnectTo:options:) method to change root view controller.
Like shown in this example:
func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
guard let scene = (scene as? UIWindowScene) else { return }
// Instantiate UIWindow with scene
let window = UIWindow(windowScene: scene)
// Assign window to SceneDelegate window property
self.window = window
// Set initial view controller from Main storyboard as root view controller of UIWindow
self.window?.rootViewController = UIStoryboard(name: "Main", bundle: nil).instantiateInitialViewController()
// Present window to screen
self.window?.makeKeyAndVisible()
}
It is available in SceneDelegate.swift file in your project
It will have delegate method :
func scene(_ scene: UIScene, willConnectTo session: UISceneSession,
options connectionOptions: UIScene.ConnectionOptions)
Example
func scene(_ scene: UIScene, willConnectTo session: UISceneSession,
options connectionOptions: UIScene.ConnectionOptions) {
if let windowScene = scene as? UIWindowScene {
self.window = UIWindow(windowScene: windowScene)
let initialViewController =
storyboard.instantiateViewController(withIdentifier: "FirstViewController")
self.window!.rootViewController = initialViewController
self.window!.makeKeyAndVisible()
}
}
In viewDidAppear you can set root:-
override func viewDidAppear(_ animated: Bool) {
print(self.view.window)
let vc = self.storyboard?.instantiateViewController(identifier: "SecondViewController") as? SecondViewController
self.view.window?.rootViewController = vc
}
For anyone looking to create a couple of extensions to change the root view controller and need to support both delegate types (UISceneDelegate and AppDelegate), here's a couple:
extension UIViewController {
var appDelegate: AppDelegate {
return UIApplication.shared.delegate as! AppDelegate
}
var sceneDelegate: SceneDelegate? {
guard let windowScene = UIApplication.shared.connectedScenes.first as? UIWindowScene,
let delegate = windowScene.delegate as? SceneDelegate else { return nil }
return delegate
}
}
And if you're in need of an extension to reach the UIWindow from a ViewController with iOS12 and iOS13 Support:
extension UIViewController {
var window: UIWindow? {
if #available(iOS 13, *) {
guard let windowScene = UIApplication.shared.connectedScenes.first as? UIWindowScene,
let delegate = windowScene.delegate as? SceneDelegate, let window = delegate.window else { return nil }
return window
}
guard let delegate = UIApplication.shared.delegate as? AppDelegate, let window = delegate.window else { return nil }
return window
}
}
If you want to use ScenceDelegate. In scene(_:willConnectTo:options:, creates a new UIWindow, sets the window’s rootViewController and makes this window the key window.
If you don't want to use ScenceDelegate, you can try to remove it by following this.

Resources