Clean up AppDelegate

Remove storyboards and add fake AppDelegate for testing.

Storyboards are Apple choice for all new projects. They have their pros and cons but you are not forced to use them. You have a couple of options to choose from:

  • Storyboard - multiple views and transitions between them in one file. Remember when you using storyboards: you can and actually should use more than one storyboard file per project. Create new storyboard file for each story in your application.
  • XIB files - each view is in different file, all actions have to be handled programmatically.
  • Custom Code - all views are generated programmatically.

In fact you can and probably will mix them in your projects. For now let’s remove existing storyboard and add a simple xib view.

Search for Main.storyboard file in Project navigator, right click on it and select Delete > Move to Trash. We also have to delete it from the project settings, go to iOSProjectStarter target and in General tab look for Deployment Info section. Inside Main Interface delete Main and just leave it blank. When you open Info.plist file the key UIMainStoryboardFile should not be there.

Inside AppDelegate.swift add those changes:

func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {

  self.window = UIWindow(frame: UIScreen.main.bounds)

  let mainVC = ViewController()
  self.window?.rootViewController = mainVC
  self.window?.makeKeyAndVisible()

  return true
}

We can still use ViewController class, just create new XIB file for it. Go to Modules > Main > View > right click New file... choose View in User Interface section. Name it: ViewController.xib.

Open ViewController.xib, click File's Owner and select Custom Class, set it to ViewController:

Custom class

Right click and hold on File's Owner, then connect it to View below:

Connect view

And select view from Outlets:

Select view

Add sample UILabel to this view, change it to “Hello” and add constraints:

Sample label

Now run the project, you should see our temporary view:

Sample view

Fake AppDelegate

Right now there is not much going on inside AppDelegate, but later it can grow and during testing we don’t want to run all of this stuff on each test case. For testing purposes let’s create a fake AppDelegate, it will be invoked for each test case.

First of all remove @UIApplicationMain annotation inside AppDelegate.swift file:

import UIKit

//@UIApplicationMain << remove this line
class AppDelegate: UIResponder, UIApplicationDelegate {
	...

@UIApplicationMain basically replaces the old main.m file known from Objective-C. It will just run function UIApplicationMain() under the hood. After we remove it, we need to provide main.swift file and run UIApplicationMain() there.

Create a new file: SupportingFiles/main.swift:

import UIKit

func isRunningTests() -> Bool {
  let environment = ProcessInfo.processInfo.environment
  if environment["XCTestConfigurationFilePath"] != nil {
    return true
  }
  return false
}

class SpecAppDelegate: UIResponder, UIApplicationDelegate {
}

if isRunningTests() {
  UIApplicationMain(
    CommandLine.argc,
    UnsafeMutableRawPointer(CommandLine.unsafeArgv)
      .bindMemory(
        to: UnsafeMutablePointer<Int8>.self,
        capacity: Int(CommandLine.argc)),
    nil,
    NSStringFromClass(SpecAppDelegate.self)
  )
} else {
  UIApplicationMain(
    CommandLine.argc,
    UnsafeMutableRawPointer(CommandLine.unsafeArgv)
      .bindMemory(
        to: UnsafeMutablePointer<Int8>.self,
        capacity: Int(CommandLine.argc)),
    nil,
    NSStringFromClass(AppDelegate.self)
  )
}

Inside function isRunningTests() we simply look for XCTest configuration path, if it exists it means that we are running tests. Class SpecAppDelegate is empty and its main purpose is to replace the real AppDelegate during testing.

In the next step we will write our first simple test.