- Proposal: SE-0281
- Authors: Nate Cook, Nate Chandler, Matt Ricketson
- Review Manager: Tom Doron
- Status: Implemented (Swift 5.3)
- Implementation: apple/swift#30693
- Decision Notes: Rationale
A Swift language feature for designating a type as the entry point for beginning program execution. Instead of writing top-level code, users can use the @main
attribute on a single type. Libraries and frameworks can then provide custom entry-point behavior through protocols or class inheritance.
Related forum threads:
Swift programs start execution at the beginning of a file. This works great for procedural code, and allows simple Swift programs to be as short as a single line, with no special syntax required.
// A self-contained Swift program:
print("Hello, world!")
However, not all kinds of programs naturally fit into this model. For example, a user-facing app launches and runs until it is quit. User interface frameworks like UIKit and AppKit take care of the complexities of launching an application, providing high-level API hooks for defining app behavior. A developer using these frameworks typically does not care about nor interact with the literal starting point of their app’s execution.
In order to resolve these two models, apps need a small amount of “boot-loading” code to kick off the framework’s preferred execution entry point. Ever since its initial release, Swift has provided the domain-specific attributes @UIApplicationMain
and @NSApplicationMain
to smooth over this startup process for developers of UIKit and AppKit applications.
Instead of these hard-coded, framework-specific attributes, it would be ideal for Swift to offer a more general purpose and lightweight mechanism for delegating a program’s entry point to a designated type. A type-based approach to program execution fits within Swift's general pattern of solving problems through the type system and allows frameworks to use standard language features to provide clean, simple entry point APIs.
The Swift compiler will recognize a type annotated with the @main
attribute as providing the entry point for a program. Types marked with @main
have a single implicit requirement: declaring a static main()
method. This main()
method will typically be provided by libraries or frameworks, so that the author of a Swift program will only need to use the @main
attribute to indicate the correct starting point.
When the program starts, the static main()
method is called on the type marked with @main
. For example, this code:
// In a framework:
public protocol ApplicationRoot {
// ...
}
extension ApplicationRoot {
public static func main() {
// ...
}
}
// In MyProgram.swift:
@main
struct MyProgram: ApplicationRoot {
// ...
}
is equivalent to this code:
// In a framework:
public protocol ApplicationRoot {
// ...
}
extension ApplicationRoot {
public static func main() {
// ...
}
}
// In MyProgram.swift:
struct MyProgram: ApplicationRoot {
// ...
}
// In 'main.swift':
MyProgram.main()
Since main()
is a regular static method, it can be supplied by a protocol as an extension method or by a base class. This allows frameworks to easily define custom entry point behavior without additional language features.
For example, the ArgumentParser
library offers a ParsableCommand
protocol that provides a default implementation for main():
// In ArgumentParser:
public protocol ParsableCommand {
// Other requirements
}
extension ParsableCommand {
static func main() {
// Parses the command-line arguments and then
// creates and runs the selected command.
}
}
The @main
attribute would allow clients to focus on just the requirements of their command-line tool, rather than how to launch its execution:
@main
struct Math: ParsableCommand {
@Argument(help: "A group of integers to operate on.")
var values: [Int]
func run() throws {
let result = values.reduce(0, +)
print(result)
}
}
Likewise, UIKit and AppKit could add the static main()
method to the UIApplicationDelegate
and NSApplicationDelegate
protocols, allowing authors to use the single @main
attribute no matter the user interface framework, and allowing for the deprecation of the special-purpose attributes. (Note: These changes are not a part of this proposal.)
The compiler will ensure that the author of a program only specifies one entry point: either a single, non-generic type designated with the @main
attribute or a single main.swift
file. The type designated as the entry point with @main
can be defined in any of a module's source files. @UIApplicationMain
and @NSApplicationMain
will be counted the same way as the @main
attribute when guaranteeing the uniqueness of a program's entry point.
A main.swift
file is always considered to be an entry point, even if it has no top-level code. Because of this, placing the @main
-designated type in a main.swift
file is an error.
@main
can be applied to either a type declaration or to an extension of an existing type. The @main
-designated type can be declared in the application target or in an imported module. @main
can be applied to the base type of a class hierarchy, but is not inherited — only the specific annotated type is treated as the entry point.
The rules for satisfying the main()
requirement are the same as for satisfying a hypothetical protocol with a single requirement:
protocol ProvidesMain {
static func main() throws
}
The main()
method can be provided by the type itself, inherited from a superclass, or declared in an extension to a protocol the type conforms to. The main()
method can either be declared as throws
or not; errors thrown from a main()
method will have the same behavior as errors thrown from top-level code.
This is a purely additive change and has no source compatibility impacts.
The new attribute is only applicable to application code, so there is no effect on ABI stability or API resilience.
The @main
attribute will currently not be usable by Swift packages, since SwiftPM recognizes executable targets by looking for a main.swift
file.
The standard library includes several protocols that the compiler imbues with special functionality, such as expressing instances of a type using literals and enabling for
-in
iteration for sequences. It would similarly be possible to define a protocol Main
instead of creating a @main
attribute, with the same requirements and special treatment. However, such a protocol wouldn’t enable any useful generalizations or algorithms, and the uniqueness requirement is totally non-standard for protocol conformance. These factors, plus the precedent of @UIApplicationMain
and @NSApplicationMain
, make the attribute a more appropriate way to designate a type as an entry point.
One or more protocols could be attributed with @main
to make any type that conforms an automatic entry point, with the compiler ensuring that only one such type exists in an application. As noted above, however, this uniqueness requirement is non-standard for protocols. In addition, this would make the entry point less explicit from the perspective of the program's author and maintainers, since the entry-point conforming type would look the same as any other. Likewise, this would prevent using manual execution if a programmer still wanted to have custom logic in a main.swift
file.
Instead of a dedicated @main
attribute, the compiler could let libraries declare a type that could act as an attribute used to denote a program's entry point. This approach is largely isomorphic to the proposed @main
attribute, but loses the consistency of having a single way to spell the entry point, no matter which library you're using.
Instead of requiring a static main()
method, the compiler could instead require main()
as an instance method and a default initializer. This, however, would increase the implicit requirements of the @main
attribute and split the entry point into two separate calls.
In addition, a default initializer may not make sense for every type. For example, a web framework could offer a main()
method that loads a configuration file and instantiates the type designated with @main
using data from that configuration.
Some types may already define a static main()
method with a different purpose than being a program’s entry point. A different, more specific name could avoid some of these potential collisions.
However, adding @main
isn’t source- or ABI-breaking for those types, as authors would already need to update their code with the new attribute and recompile to see any changed behavior. In addition, the main
name matches the existing behavior of main.swift
, as well as the name of the entry point in several other languages—C, Java, Rust, Kotlin, etc. all use functions or static methods named main
as entry points.
C programs define a function with the signature int main(int argc, char *argv[])
as their entry point, with access to any arguments and returning a code indicating the status of the program. Swift programs have access to arguments through the CommandLine
type, and can use exit
to provide a status code, so the more complicated signature isn't strictly necessary.
To eliminate any overhead in accessing arguments via CommandLine
and to provide a way to handle platform-specific entry points, a future proposal could expand the ways that types can satisfy the @main
requirement. For example, a type could supply either main()
or main(Int, [String]) -> Int
.
Some platforms, such as Windows, base an executable's launch behavior on the specific entry point that the executable provides. A future direction could be to allow @main
designated types to supply other specific entry points, such as wWindowsMain(Int, UnsafeMutablePointer<UnsafeMutablePointer<WCHAR>>) -> Int
, and to allow additional arguments to be given with the @main
attribute:
// In a framework:
extension ApplicationRoot {
static func main(console: Bool = true, ...) { ... }
}
@main(console: false)
struct MyApp: Application {
// ...
}
Alternatively, a future proposal could add an attribute that would let a library designate a different symbol name for the entry point:
// In a framework:
extension ApplicationRoot {
@entryPoint(symbolName: "wWinMain", convention: stdcall)
static func main(_ hInstance: HINSTANCE, _ hPrevInstance: HINSTANCE, lpCmdLine: LPWSTR, _ nCmdShow: Int32) { ... }
}
A previous design of this feature required the static main()
method to return Never
instead of Void
, since that more precisely matches the semantics of how the method is used when invoked by the compiler. That said, because you can’t provide any top-level code when specifying the @main
attribute, the Void
-returning version of the method effectively acts like a () -> Never
function.
In addition, returning Void
allows for more flexibility when the main()
method is called manually. For example, an author might want to leave out the @main
attribute and use top-level code to perform configuration, diagnostics, or other behavior before or after calling the static main()
method.