The possible suspension points in your code marked with await indicate that the current piece of code might pause execution while waiting for the asynchronous function or method to return...Because code with await needs to be able to suspend execution, only certain places in your program can call asynchronous functions or methods:
Code in the body of an asynchronous function, method, or property.
Code in the static main() method of a structure, class, or enumeration that’s marked with @main.
However, the following simple example does not compile (see compiler error at the botom) unless main() is also async-annotated:
import Foundation
@main
struct S {
static func main() {
let session = URLSession(configuration: URLSessionConfiguration.default)
guard let url = URL(string: "http://www.google.com") else {
return
}
let (_, response) = try! await session.bytes(for: URLRequest(url: url))
NSLog("Received response")
}
}
Is the book outdated/wrong or am I doing something wrong? The book made it sound like @main was treated specially—perhaps as the program's entry point—but maybe that's inaccurate and it needs async too?
Compiler Error
error: 'async' call in a function that does not support concurrency
35 | @main
36 | struct S {
37 | static func main() {
| `- note: add 'async' to function 'main()' to make it asynchronous
Yeah, I think that is wrong. If you were to write your code in a main.swift-style entry point, rather than @main-style, then it would work as you expect. So maybe that is the special case the book means to call out. It looks like in early drafts of the book this was properly called out, and later revisions simplified the language but perhaps at the cost of correctness.
My code is in main.swift (the only file in my Package)
I tried keeping in main.swift just
func main() async throws {
...
}
but I get a linker error:
Undefined symbols for architecture x86_64:
"_foo_main", referenced from:
_main in command-line-aliases-file
ld: symbol(s) not found for architecture x86_64
I tried renaming the file to something else but then I get
35 | @main
| `- error: 'main' attribute cannot be used in a module that contains top-level code
36 | struct S {
If I just leave the function in a.swift
func main() async throws {
then the project builds but nothing runs (it's a library, I guess, not an executable with an entry point).
As for the different ways to compile a Swift executable on the command line, this can also be pretty confusing and I don't know a place where this is documented in an end-user-friendly way: you have 2 options:
Name your file main.swift and compile it as is. Do not add a func main() or static func main(), just write your program as top-level code. You can also freely use await and/or try in top-level code. The compiler is smart enough to create an async-throwing context for you.
$ swiftc main.swift
$ ./main
Name your file anything butmain.swift (because the compiler treats main.swift as top-level code) and create a @main struct that contains a static func main(). If you want to use await or try in your main func, you can and must annotate it with async and/or throws.
Now you must also pass the -parse-as-library flag to the compiler. This tells the compiler not to expect a main.swift file.
$ swiftc -parse-as-library a.swift
$ ./a
When you use SwiftPM, I believe SwiftPM will automatically do the right thing: if your executable target has a main.swift file, SwiftPM will treat the top-level code in it as the entry point. If you don't have a main.swift file, SwiftPM will pass the -parse-as-library flag and this will tell the compiler to look for a @main struct as the entry point.
The name [of the file] does not seem to matter (well, at least in my simple case where I have a single file in the package). One can't return from a top-level/non-function code so I had to write something ugly like the following but it did compile and run:
I think you're right. SwiftPM new-ish "simplified" one-executable-target package format, where the source code is directly under Sources, not in per-target subdirectories under Sources, seems to always assume top-level code, regardless of filename.
(I don't remember when this simplified package format was introduced. Or did it always exist and only the default template for swift package init --type executable changed? Is there a Swift Evolution proposal for it?)
If you want to use an @main struct, it seems you have to put your target's code in Sources/MyTarget/a.swift (or similar).