Swift has a formal process for proposing and accepting changes to the language known as the Swift Evolution Process. This process encompasses all aspects of the language, including language features, standard library, compiler, and package manager.
Swift 5.10, which is included in Xcode 15.3, incorporates 5 implemented proposals, accomplishes full data isolation in the concurrency language model. This important milestone has taken years of active development over many releases.
@UIApplicationMain
and @NSApplicationMain
used to be the standard way for iOS and macOS apps respectively to declare a synthesized platform-specific entrypoint for an app. These functions have since been obsoleted by introduction of the @main
attribute in Swift 5.3.
This release will deprecate these alternative entrypoint attributes in favor of @main
in pre-Swift 6, and it makes their use in Swift 6 a hard error.
@UIApplicationMain // warning: '@UIApplicationMain' is deprecated in Swift 5
// fixit: Change `@UIApplicationMain` to `@main`
final class MyApplication: UIResponder, UIApplicationDelegate {
/**/
}
Once the fixit has been applied, the result will be:
@main
final class MyApplication: UIResponder, UIApplicationDelegate {
/**/
}
So far protocols cannot be nested at all, and so must always be top-level types within a module. This is unfortunate, Swift 5.10 will relax this restriction so that developers can express protocols which are naturally scoped to some outer type.
For example, TableView.Delegate
is naturally a delegate protocol pertaining to table-views. You should be declare it as such - nested within their TableView
class:
class TableView {
protocol Delegate: AnyObject {
func tableView(_: TableView, didSelectRowAtIndex: Int)
}
}
class DelegateConformer: TableView.Delegate {
func tableView(_: TableView, didSelectRowAtIndex: Int) {
// ...
}
}
Protocols can also be nested within non-generic functions and closures. Admittedly, this is of somewhat limited utility, as all conformances to such protocols must also be within the same function.
Default value expressions can now have the same isolation as the enclosing function or the corresponding stored property:
@MainActor
func requiresMainActor() -> Int { ... }
class C {
@MainActor
var x: Int = requiresMainActor()
}
@MainActor func defaultArg(value: Int = requiresMainActor()) { ... }
For isolated default values of stored properties, the implicit initialization only happens in the body of an init with the same isolation. This closes an important data-race safety hole where global-actor-isolated default values could inadvertently run synchronously from outside the actor.
Under strict concurrency checking, every global or static variable must be either isolated to a global actor or be both immutable and of Sendable type.
var mutableGlobal = 1
// warning: var 'mutableGlobal' is not concurrency-safe because it is non-isolated global shared mutable state
// (unless it is top-level code which implicitly isolates to @MainActor)
final class NonsendableType {
init() {}
}
struct S {
static let immutableNonsendable = NonsendableType()
// warning: static property 'immutableNonsendable' is not concurrency-safe because it is not either conforming to 'Sendable' or isolated to a global actor
}
The attribute nonisolated(unsafe) can be used to annotate a global variable (or any form of storage) to disable static checking of data isolation, but note that without correct implementation of a synchronization mechanism to achieve data isolation, dynamic run-time analysis from exclusivity enforcement or tools such as Thread Sanitizer could still identify failures.
nonisolated(unsafe) var global: String
This proposal aims to shore up the definition of an actor, to clarify when the isolation of the data begins and ends for an actor instance, along with what can be done inside the body of an actor’s init
and deinit
declarations.
A non-delegating initializer of an actor or a global-actor isolated type (GAIT) is required to initialize all of the stored properties of that type.
While actors are a reference type, their delegating initializers will follow the same basic rules that exist for value types, namely:
self.init
, then it’s a delegating initializer. No convenience keyword is required.self.init
must always be called on all paths, before self
can be used.The reason for this difference between actor and class types is that actors do not support inheritance, so they can shed the complexity of class initializer delegation. GAITs use the same syntactic form as ordinary classes to define delegating initializers.
The only difference between the init
and the deinit
is that the deinit
can only access Sendable properties, whereas the init
can access non-Sendable properties prior to the isolation decay.