My apps uses no Storyboards. All views are created programmatically.
Historically I have deleted my Main.storyboard, removed the reference from my Info.plist and setup my UIWindow and rootViewController as follows:
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
let window = UIWindow(frame: UIScreen.main.bounds)
window.rootViewController = UIViewController()
window.makeKeyAndVisible()
self.window = window
return true
}
However when trying to run my app in iOS 13, I get a crash -
*** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: 'Could not find a storyboard named 'Main' in bundle NSBundle </Users/Dev/Library/Developer/CoreSimulator/Devices/8A4474B1-FCA3-4720-8F34-A6975A03EE19/data/Containers/Bundle/Application/258FA246-A283-4FE6-A075-58BD32424427/Home.app> (loaded)'
***
iOS 12 still runs as expected. How should I setup my view programmatically to support iOS 12 and 13?
You need to add update SceneDelegate.swift also.
Update func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) and add
guard let windowScene = (scene as? UIWindowScene) else { return }
let window = UIWindow(windowScene: windowScene)
let viewController = ViewController()
window.rootViewController = viewController
window.makeKeyAndVisible()
self.window = window
Related
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()
This question already has answers here:
Opt out of UISceneDelegate/SwiftUI on iOS
(6 answers)
Closed 3 years ago.
I just started a new project of iOS. I created the project using xcode 11 and iOS 13. When I created project i found out that in order to set our own rootController we have to use sceneDelegate instead of AppDelegate. I want to ask if there is any possibility to use old method of setting rootControllers in AppDelegate instead of using sceneDelegate.
import UIKit
#UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {
var window : UIWindow?
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
let loginController: HomeViewController = HomeViewController(nibName: "HomeViewController", bundle: nil)
// let navController: UINavigationController = UINavigationController(rootViewController: loginController)
self.window?.rootViewController = loginController
self.window?.makeKeyAndVisible()
// Override point for customization after application launch.
return true
}
}
Follow these steps to use AppDelegate and opt-out for SceneDelegate
Go to Info.plist and remove Application Scene Manifest entry from Info.plist.
Remove SceneDelegate.swift
Add var window: UIWindow? in your AppDelegate.swift file
Delete the UISceneSession Lifecycle code from AppDelegate.swift
import UIKit
#UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {
var window : UIWindow?
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
// Override point for customization after application launch.
let mainStoryBoard = UIStoryboard(name: "Main", bundle: Bundle.main)
let loginController = mainStoryBoard.instantiateViewController(withIdentifier: "HomeViewController")
self.window?.rootViewController = loginController
self.window?.makeKeyAndVisible()
return true
}
}
Make sure to give "HomeViewController" storyboardID to your view controller.
This is how your AppDelegate.swift file should look now. You are good to go!
Why don't you use the SceneDelegate
func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
guard let windowScene = (scene as? UIWindowScene) else { return }
self.window = UIWindow(frame: windowScene.coordinateSpace.bounds)
//Make sure to do this else you won't get
//the windowScene object using UIApplication.shared.connectedScenes
self.window?.windowScene = windowScene
let storyBoard: UIStoryboard = UIStoryboard(name: storyBoardName, bundle: nil)
window?.rootViewController = storyBoard.instantiateInitialViewController()
window?.makeKeyAndVisible()
}
If you don't want to use sceneDelegate then you can remove all sceneDelegate and also remove 'Application Scene Manifest' from info.plist
set UIWindow variable
var window: UIWindow?
Make sure you have removed Application Scene Manifest this from info.plist and change the background colour of your view controller.
Your device's Dark Mode is enable if you want to remove dark mode from app add this key into your info.plist
User Interface Style = Light
I have been trying to learn the Coordinator pattern by creating a new app on Xcode 11.2 using Storyboards as Interface design.
I followed this video by Paul Hudson but I got stuck at minute 12 when code needed to be added to the AppDelegate.swift file. Like it is the app will launch, the first view controller will show but it will not navigate.
What should I change or, better, where should I move the present code to make it work?
Whole project can be found here.
In short the code that in iOS 12 and before was in AppDelegate is this:
var coordinator: MainCoordinator?
var window: UIWindow?
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
let navController = UINavigationController()
coordinator = MainCoordinator(navigationController: navController)
coordinator?.start()
window = UIWindow(frame: UIScreen.main.bounds)
window?.rootViewController = navController
window?.makeKeyAndVisible()
return true
}
I have seen that now window is in SceneDelegate but moving everything there to the sceneDidConnect method is not helping.
Can someone enlighten me here?
Thanks!
So a few changes have to be made to be able to implement this pattern. Firstly, you should restore your AppDelegate to it's initial format on creation:
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
return true
}
You can remove the var coordinator: MainCoordinator? declaration at the top.
In SceneDelegate replace the code in the sceneWillConnectToSession function with the following:
func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
guard let windowScene = (scene as? UIWindowScene) else { return }
let navController = UINavigationController()
let coordinator = MainCoordinator(navigationController: navController)
coordinator.start()
let window = UIWindow(windowScene: windowScene)
window.rootViewController = navController
self.window = window
window.makeKeyAndVisible()
}
The final change is that I removed the weak declaration for MainCoordinator in your view controllers.
So I just replaced it with var coordinator: MainCoordinator?, and then it worked.
Reference Article: The Scene Delegate In Xcode 11 And iOS 13
This question already has answers here:
Why is manually setup root view controller showing black screen?
(3 answers)
Closed 3 years ago.
I want to initialize window inside appDelegate to show specific ViewController depend on some cases. Now I have this code:
class AppDelegate: UIResponder, UIApplicationDelegate {
var window: UIWindow?
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
// Override point for customization after application launch.
window = UIWindow()
let rootNavigationController = UIViewController()
window?.rootViewController = rootNavigationController
window?.rootViewController?.view.backgroundColor = .green
window?.makeKeyAndVisible()
return true
}
// MARK: UISceneSession Lifecycle
func application(_ application: UIApplication, configurationForConnecting connectingSceneSession: UISceneSession, options: UIScene.ConnectionOptions) -> UISceneConfiguration {
// Called when a new scene session is being created.
// Use this method to select a configuration to create the new scene with.
return UISceneConfiguration(name: "Default Configuration", sessionRole: connectingSceneSession.role)
}
func application(_ application: UIApplication, didDiscardSceneSessions sceneSessions: Set<UISceneSession>) {
// Called when the user discards a scene session.
// If any sessions were discarded while the application was not running, this will be called shortly after application:didFinishLaunchingWithOptions.
// Use this method to release any resources that were specific to the discarded scenes, as they will not return.
}
}
I'm using XCode11 and have created new project. SceneDelegate file I removed cause it hasn't effect on this. Also removed Main from Info.plist and from deployment info
As result on device I see black screen but debugger show me that rootNavigationController as should be
image from debugger
How fix it or implement this logic for XCode11?
Solution:
1)Inside manifest(plist) file remove Storyboard Name field
2)inside SceneDelegate.swift implement:
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 windowScene = (scene as? UIWindowScene) else { return }
window = UIWindow(windowScene: windowScene)
let rootNavigationController = UIViewController()
window?.rootViewController = rootNavigationController
window?.rootViewController?.view.backgroundColor = .green
window?.makeKeyAndVisible()
}
And it will work for ios 13 , if you want support ios 12 and lower you need also implement this logic in AppDelegate
I have an app made in Xcode 10 using Main.storyboard and would like to migrate it to use Apple's new framework: SwiftUI.
Is that already possible?
I have already tried to add the UIApplicationSceneManifest key in Info.plist, I changed the AppDelegate.swift to use scenes, I created the SceneDelegate.swift and even then I could not
I assume you're using Xcode 11 GM and macOS Mojave or Catalina.
Along with the changes in the plist, you have to add UISceneSession lifecycle functions in the application delegate.
func application(_ application: UIApplication,
configurationForConnecting connectingSceneSession: UISceneSession,
options: UIScene.ConnectionOptions) -> UISceneConfiguration {
// The name must match the one in the Info.plist
return UISceneConfiguration(name: "Default Configuration", sessionRole: connectingSceneSession.role)
}
func application(_ application: UIApplication, didDiscardSceneSessions sceneSessions: Set<UISceneSession>) {
}
Also, you need to make sure the window is created correctly in the SceneDelegate.
func scene(_ scene: UIScene,
willConnectTo session: UISceneSession,
options connectionOptions: UIScene.ConnectionOptions) {
guard let windowScene = scene as? UIWindowScene else {
return
}
let window = UIWindow(windowScene: windowScene)
window.rootViewController = UIHostingController(rootView: ContentView())
self.window = window
window.makeKeyAndVisible()
}
where ContentView is the main SwiftUI view you want to display.
P.S. Make sure the plist specifies $(PRODUCT_MODULE_NAME).SceneDelegate as delegate class name, and the scene delegate is called SceneDelegate
Example:
If you're on Catalina, you can turn on Previews in the build settings for your target.
Build Options -> Enable Previews
Addendum I:
Make sure you remove the Storyboard key from the Info.Plist and that you're targeting iOS 13.
Addendum II:
Clean Derived Data, as many devs in the comments suggest.
It's a correct only minor change
In SceneDelegate.swift replace
let window = UIWindow(frame: UIScreen.main.bounds)
window.rootViewController = UIHostingController(rootView: ContentView())
self.window = window
window.makeKeyAndVisible()
with
if let windowScene = scene as? UIWindowScene {
let window = UIWindow(windowScene: windowScene)
window.rootViewController = UIHostingController(rootView: ContentView())
self.window = window
window.makeKeyAndVisible()
}
TAKEN FROM HERE
My solution turned out to be different. In my case, I had everything in place, but when the code attempted to load the UISceneConfiguration, it failed to load the config in the Info.plist and gave me a secondary window config instead with no scene delegate set. If I asked for the correct configuration from the debug console it would load as expected. I was confused.
I double checked everything and tried all of the suggestions here but none worked. In the end I did 'Hardware' - 'Erase all contents and settings...' on the simulator and that solved it.
My guess is that because I'd been running the pre-SwiftUI version of the app on the simulator, something in that caused the SwiftUI version to behave differently.