There are two ways of creating layout on iOS. First, using readymade storyboard provided by Xcode when you create a new project. And second, by manually creating a UIViewController subclass and assigning it to UIWindow object as a rootViewController in AppDelegate class.

However, things have changed since Xcode 11 and later versions. We no longer need to create and assign viewController in AppDelegate file. All the magic of programmatically creating a layout now happens in SceneDelegate.swift/.m file. Let's see how.

Steps for using programmatic layout eliminating the requirement of storyboard file for projects running on  Xcode 11 and later

Let's say you've already created a project by going through following lifecycle,

  1. File -> New
  2. Select Project
  3. Choose Single View App
  4. Enter Product Name
  5. Press Next and create a project under appropriate directory

Once you open a project, you will see a familiar folder structure as follow,

Please note that the steps I am going to describe next apply to both Swift and Objective-C projects

Since we are going to use programmatic layout, we won't need Main.storyboard file. So let's eliminate it first.

  1. Right click Main.storyboard file -> Delete -> Remove References to eliminate it

Since we deleted storyboard file, we also need to eliminate its reference from Info.plist file. So next select Info.plist file under your main project structure.

Here we will delete two entries which include Main.storyboard file description. They are both annotated in red rectangle

  1. Storyboard Name under Scene Configuration
  2. Main Storyboard file base name entry

Select both rows and delete them and run the app.

What do you see now? A blank screen?

This is expected because you manually deleted storyboard file, but did not provide a programmatic alternative for displaying view controller. That's why the RootViewController associated with UIWindow object is nil and thus displays a blank screen with no content. Let's fix this problem for both Swift and Objective-C projects.

Swift

With Xcode 11 and up, the UIWindow object is now part of SceneDelegate.swift class and NOT of AppDelegate.swift. So open up the file SceneDelegate.swift and add the following code to existing func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) lifecycle method.


    // SceneDelegate.swift

    guard let scene = (scene as? UIWindowScene) else { return }
    window = UIWindow(windowScene: scene)
    let viewController = ViewController()
    viewController.view.backgroundColor = .red
    window?.rootViewController = viewController
    window?.makeKeyAndVisible()

Objective-C

If you're working on Objective-C project, it follows the similar approach with just a change of syntax. Open up SceneDelegate.m file and add following code to existing - (void)scene:(UIScene *)scene willConnectToSession:(UISceneSession *)session options:(UISceneConnectionOptions *)connectionOptions lifecycle method


    // SceneDelegate.m

    _window = [[UIWindow alloc] initWithWindowScene:(UIWindowScene *)scene];
    ViewController *viewController = [[ViewController alloc] init];
    viewController.view.backgroundColor = [UIColor redColor];
    _window.rootViewController = viewController;
    [_window makeKeyAndVisible];

Summary:

  1. When app starts, it automatically calls the lifecycle method willConnectToSession on startup
  2. Through this method, we have access to internal UIWindowScene object which is utilized to initialize UIWindow object
  3. We initialize UIWindow object by passing UIWindowScene object through the initializer
  4. Next we programmatically create the instance of desired UIViewController object (Please note that this being a programmatic initialization, you have to provide a custom initializer by either passing no parameters or required set of parameters necessary to create a ViewController instance)
  5. We also changed the background color of view controller to be able to verify that we're using the correct ViewController class
  6. Assign this viewController to rootViewController property associated with window object on SceneDelegate class
  7. Call makeKeyAndVisible on window object so that we can show and make this window as a key window

And that's all you need to know to create an app through programmatic means on iOS running on Xcode 11 or above (Irrespective of whether you're using Swift or Objective-C for app development). With just a few lines of code, you can assign any viewController you would like to act as a RootViewController associated with window object. It's very easy, convenient and avoids the merge conflict hell emerging from multiple teams using the same storyboard file and simultaneously pushing changes