What does @main do in Swift 5.3?

If you create a new iOS UIKit project with Xcode 12 or later the app delegate will look something like this:

import UIKit
@main
class AppDelegate: UIResponder, UIApplicationDelegate { ... }

Compare this to the app delegate created by Xcode 11:

import UIKit
@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate { ... }

Xcode 12 shipped with Swift 5.3 which introduced the @main attribute as a general purpose way of marking the entry point of a Swift program. You use it in place of Apple specific attributes like @UIApplicationMain and @NSApplicationMain. If you’re writing iOS or macOS apps you can probably just accept that and move on. It becomes more useful if you’re writing a library or framework that provides its own start or entry point.

Understanding how this works led me down a rabbit-hole of how iOS and Swift programs start. Here’s my notes for the curious:

Application Main

When your iOS app launches the system takes care of creating a single UIApplication instance and calling your app delegate. If you’ve been developing iOS apps for a while you may remember our Objective-C projects used to include a main.m source file that looked like this:

// main.m
#import <UIKit/UIKit.h>
int main(int argc, char *argv[]) {
  NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
  int retVal = UIApplicationMain(argc, argv, nil, nil);
  [pool release];
  return retVal;
}

This follows the C language convention of having a main function. The call to UIApplicationMain creates the UIApplication instance. The last two parameters allow you to pass your own class for the application and application delegate. We can do the same in Swift by adding a main.swift to our project that calls UIApplicationMain:

// main.swift
import UIKit
UIApplicationMain(CommandLine.argc, CommandLine.unsafeArgv, nil, nil)

This top-level executable code in main.swift is our programs entry point when launched. There’s little reason to do this unless you want to subclass UIApplication:

// main.swift
import UIKit
UIApplicationMain(CommandLine.argc, CommandLine.unsafeArgv,
 NSStringFromClass(MyApplication.self), 
 NSStringFromClass(AppDelegate.self))

The Apple notes on subclassing UIApplication mention doing this if you need to intercept the standard event or action dispatch. For example, to see every event and control action:

// MyApplication.swift
import UIKit

class MyApplication: UIApplication {
  override func sendEvent(_ event: UIEvent) {
    print("sendEvent: \(event)")
    super.sendEvent(event)
  }

  override func sendAction(_ action: Selector, to target: Any?,
  from sender: Any?, for event: UIEvent?) -> Bool {
    print("sendAction: \(action)")
    return super.sendAction(action, to: target,
           from: sender, for: event)
  }
}

Unless you have a special reason to do this there’s no need to use a main.swift in modern iOS projects. It’s enough to mark your app delegate with the @UIApplicationMain or @main attribute.

The @main Attribute

The @UIApplicationMain attribute is Apple specific. Swift 5.3 provides us a general purpose way of marking a custom type as the entry point of a program. When you add a @main attribute to a type you must also provide a static main() method. In iOS 14, Apple added this to UIApplicationDelegate:

// UIApplication
extension UIApplicationDelegate {
  public static func main()
}

When you add the @main attribute to your app delegate you’re telling the system to call AppDelegate.main() at launch. It’s equivalent to having a main.swift like this:

// main.swift
AppDelegate.main()

Standalone Swift Programs

What if we are writing a standalone Swift program or command-line utility? One option is to include a main.swift with some top-level code to get things started:

// main.swift
struct MyApplication {
  static func main() {
    print(Date())
  }
}

// Top-level executable code
MyApplication.main()

Starting with Swift 5.3 (shipping with Xcode 12) we can drop the need to have a main.swift. As long as we have a static main() method we can mark our custom type with @main and have it called automatically:

// MyApplication.swift
@main
struct MyApplication {
  static func main() {
    print(Date())
  }
}

You can’t mix these approaches. A program can only have one entry point. If you have a main.swift you cannot also use @main.

Libraries and Frameworks

You cannot use @main in libraries but you can create libraries that have a static main() method. The Apple Swift Argument Passer library is a good example of how to use this.

A protocol defines a parsable command with amongst other things a requirement to implement a run() method (simplified, see the source code):

public protocol ParsableCommand {
  ...
  mutating func run() throws
}

An extension to the protocol provides an implementation of the main() method:

extension ParsableCommand {
  public static func main() {
    // Parse an instance of this type and
    // then call its run() method
    ...
  }
}

A user of the library creates a type that adopts the ParsableCommand protocol. This requires providing a run() method. Annotating the type with @main makes the main() method provided by the library the program entry point which then calls our run() method.

For example, here’s a quick command-line utility that repeats a phrase. See the documentation for more details:

// Yes.swift
import ArgumentParser

@main
struct SayYes: ParsableCommand {
    @Option(name: .shortAndLong, help: "Number of times to repeat phrase.")
    var count = 1

    @Option(name: .shortAndLong, help: "The phrase to say.")
    var phrase = "Yes"

    mutating func run() throws {
        for _ in 0..<count {
            print(phrase)
        }
    }
}

// Prior to Swift 5.3 call the main method
// SayYes.main()

When we run this program it first calls the SayYes.main() method provided by the library. After parsing the command line the library calls our run() method.

./SayYes -p "No" -c 3
No
No
No

Read More