Swift is a general purpose programming language that employs modern programming-language theory concepts and strives to present a simple, yet powerful syntax. Swift incorporates innovations and conventions from various programming languages, with notable inspiration from Objective-C, which it replaced as the primary development language on Apple platforms. Swift was designed to be safe and friendly to new programmers while not sacrificing speed. By default Swift manages all memory automatically and ensures variables are always initialized before use. Array accesses are checked for out-of-bounds errors and integer operations are checked for overflow. Parameter names allow creating clear APIs. Protocols define interfaces that types may adopt, while extensions allow developers to add more function to existing types. Swift enables
object-oriented programming with the support for
classes,
subtyping, and
method overriding. Optionals allow
nil values to be handled explicitly and safely. Concurrent programs can be written using
async/await syntax, and
actors isolate shared mutable state in order to eliminate data races.
Basic syntax Swift's
syntax is similar to C-style languages. Code begins executing in the global scope by default. Alternatively, the attribute can be applied to a structure, class, or enumeration declaration to indicate that it contains the program's entry point. Swift's
"Hello, World!" program is:print("Hello, world!") The function used here is included in Swift's standard library, which is available to all programs without the need to import external modules. Statements in Swift don't have to end with a semicolon, however semicolons are required to separate multiple statements written on the same line. Single-line
comments begin with and continue until the end of the current line. Multiline comments are contained by and characters. Constants (
variables which cannot be modified) are declared with the keyword, while variables declared using the {{code|var var currentScore = 980 // A variable with type Int. currentScore = 1200 // The value of variables can change over time. let playerMessage: String // A constant with explicit type String. if currentScore > highScoreThreshold { playerMessage = "You are a top player!" } else { playerMessage = "Better luck next time." } print(playerMessage) // Prints "You are a top player!" Control flow in Swift is managed with
if-else,
guard, and
switch statements, along with
while and
for-in loops. The statements take a Boolean parameter and execute the body of the statement if the condition is true, otherwise it executes the optional body. syntax provides syntactic sugar for checking for the existence of an optional value and unwrapping it at the same time.let someNumber = 42 if someNumber % 2 == 0 { // Use the remainder operator to find the remainder of someNumber divided by 2. print("\(someNumber) is even.") } else { print("\(someNumber) is odd.") } // Prints "42 is even." Functions are defined with the keyword. Function parameters may have names which allow function calls to read like phrases. An underscore before the parameter name allows the argument label to be omitted from the call site.
Tuples can be used by functions to return multiple pieces of data at once.func constructGreeting(for name: String) -> String { return "Hello \(name)!" } let greeting = constructGreeting(for: "Craig") print(greeting) // Prints "Hello Craig!" Functions, and anonymous functions known as
closures, can be assigned to properties and passed around the program like any other value.func divideByTwo(_ aNum: Int) -> Int { return aNum / 2 } func multiplyByTwo(_ aNum: Int) -> Int { return aNum * 2 } let mathOperation = multiplyByTwo print(mathOperation(21)) // Prints "42" statements require that the given condition is true before continuing on past the statement, otherwise the body of the provided clause is run. The clause must exit control of the code block in which the statement appears. statements are useful for ensuring that certain requirements are met before continuing on with program execution. In particular they can be used to create an unwrapped version of an optional value that is guaranteed to be non-nil for the remainder of the enclosing scope.func divide(numerator: Int?, byDenominator denominator: Int) -> Int? { guard denominator != 0 else { print("Can't divide by 0.") return nil } guard let numerator else { print("The provided numerator is nil.") return nil } return numerator / denominator } let result = divide(numerator: 3, byDenominator: 0) print("Division result is: \(result)") // Prints: // "Can't divide by 0." // "Division result is: nil."
switch statements compare a value with multiple potential values and then executes an associated code block. switch statements must be made exhaustive, either by including cases for all possible values or by including a default case which is run when the provided value doesn't match any of the other cases. switch cases do not implicitly fall through, although they may explicitly do so with the fallthrough keyword.
Pattern matching can be used in various ways inside switch statements. Here is an example of an integer being matched against a number of potential ranges:let someNumber = 42 switch someNumber { case ..for-in loops iterate over a sequence of values:let names = ["Will", "Anna", "Bart"] for name in names { print(name) } // Prints: // Will // Anna // Bart
while loops iterate as long as the given Boolean condition evaluates to true:// Add together all the numbers from 1 to 5. var i = 1 var result = 0 while i
Closure support Swift supports
closures, which are self-contained blocks of functionality that can be passed around and used in code, and can also be used as
anonymous functions. Here are some examples: // Closure type, defined by its input and output values, can be specified outside the closure: let closure1: (Int, Int) -> Int = { arg1, arg2 in return arg1 + arg2 } // …or inside it: let closure2 = { (arg1: Int, arg2: Int) -> Int in return arg1 + arg2 } // In most cases, closure's return type can be inferred automatically by the compiler. let closure3 = { arg1: Int, arg2: Int in return arg1 + arg2 } Closures can be assigned to variables and constants, and can be passed into other functions or closures as parameters. Single-expression closures may drop the keyword. Swift also has a trailing closure syntax, which allows the closure to be written after the end of the function call instead of within the function's parameter list. Parentheses can be omitted altogether if the closure is the function's only parameter: // This function takes a closure which receives no input parameters and returns an integer, // evaluates it, and uses the closure's return value (an Int) as the function's return value. func foo(closure bar: () -> Int) -> Int { return bar() } // Without trailing closure syntax: foo(closure: { return 1 }) // With trailing closure syntax, and implicit return: foo { 1 } Starting from version 5.3, Swift supports multiple trailing closures: // This function passes the return of the first closure as the parameter of the second, // and returns the second closure's result: func foo(bar: () -> Int, baz: (Int) -> Int) -> Int { return baz(bar()) } // With no trailing closures: foo(bar: { return 1 }, baz: { x in return x + 1 }) // With 1 trailing closure: foo(bar: { return 1 }) { x in return x + 1 } // With 2 trailing closures (only the first closure's argument name is omitted): foo { return 1 } baz: { x in return x + 1 } Swift will provide shorthand argument names for inline closures, removing the need to explicitly name all of the closures parameters. Arguments can be referred to with the names $0, $1, $2, and so on: let names = ["Josephine", "Steve", "Chris", "Barbara"] // filter calls the given closure for each value in names. // Values with a character count less than 6 are kept, the others are dropped. let shortNames = names.filter { $0.count Closures may capture values from their surrounding scope. The closure will refer to this captured value for as long as the closure exists: func makeMultiplier(withMultiple multiple: Int) -> (Int) -> (Int) { // Create and return a closure that takes in an Int and returns the input multiplied by the value of multiple. return { $0 * multiple } } let multiplier = makeMultiplier(withMultiple: 3) print(multiplier(3)) // Prints "9" print(multiplier(10)) // Prints "30"
String support The Swift standard library includes unicode-compliant and types. String values can be initialized with a String literal, a sequence of characters surrounded by double quotation marks. Strings can be concatenated with the operator: var someString = "Hello," someString += " world!"String interpolation allows for the creation of a new string from other values and expressions. Values written between parentheses preceded by a will be inserted into the enclosing string literal: var currentScore = 980 print("Your score is \(currentScore).") // Prints "Your score is 980."A for-in loop can be used to iterate over the characters contained in a string: for character in "Swift" { print(character) } // S // w // i // f // tIf the Foundation framework is imported, Swift invisibly bridges the String type to NSString (the String class commonly used in Objective-C).
Callable objects Access control Swift supports five
access control levels for symbols: , , , , and . Unlike many object-oriented languages, these access controls ignore
inheritance hierarchies: indicates that a symbol is accessible only in the immediate
scope, indicates it is accessible only from within the file, indicates it is accessible within the containing module, indicates it is accessible from any module, and (only for classes and their methods) indicates that the class may be subclassed outside of the module.
Optionals and chaining An important feature in Swift is
option types, which allow
references or values to operate in a manner similar to the common pattern in
C, where a
pointer may either refer to a specific value or no value at all. This implies that non-optional types cannot result in a
null-pointer error; the compiler can ensure this is not possible. Optional types are created with the Optional enum. To make an Integer that is nullable, one would use a declaration similar to var optionalInteger: Optional<Int>. As in C#, Swift also includes syntactic sugar for this, allowing one to indicate a variable is optional by placing a question mark after the type name, var optionalInteger: Int?. Variables or constants that are marked optional either have a value of the underlying type or are nil. Optional types
wrap the base type, resulting in a different instance. String and String? are fundamentally different types, the former is of type String while the latter is an Optional that may be holding some String value. To access the value inside, assuming it is not nil, it must be
unwrapped to expose the instance inside. This is performed with the ! operator: let myValue = anOptionalInstance!.someMethod() In this case, the ! operator unwraps anOptionalInstance to expose the instance inside, allowing the method call to be made on it. If anOptionalInstance is nil, a null-pointer error occurs, terminating the program. This is known as force unwrapping. Optionals may be safely unwrapped using
optional chaining which first tests whether the instance is nil, and then unwrap it if it is non-null: let myValue = anOptionalInstance?.someMethod() In this case the runtime calls someMethod only if anOptionalInstance is not nil, suppressing the error. A ? must be placed after every optional property. If any of these properties are nil the entire expression evaluates as nil. The origin of the term
chaining comes from the more common case where several method calls/getters are chained together. For instance: let aTenant = aBuilding.tenantList[5] let theirLease = aTenant.leaseDetails let leaseStart = theirLease?.startDate can be reduced to: let leaseStart = aBuilding.tenantList[5].leaseDetails?.startDate Swift's use of optionals allows the compiler to use
static dispatch because the unwrapping action is called on a defined instance (the wrapper), versus occurring in a runtime dispatch system.
Value types In many object-oriented languages, objects are represented internally in two parts. The object is stored as a block of data placed on the
heap, while the name (or "handle") to that object is represented by a
pointer. Objects are passed between methods by copying the value of the pointer, allowing the same underlying data on the heap to be accessed by anyone with a copy. In contrast, basic types like integers and floating-point values are represented directly; the handle contains the data, not a pointer to it, and that data is passed directly to methods by copying. These styles of access are termed
pass-by-reference in the case of objects, and
pass-by-value for basic types. Both concepts have their advantages and disadvantages. Objects are useful when the data is large, like the description of a window or the contents of a document. In these cases, access to that data is provided by copying a 32- or 64-bit value, versus copying an entire data structure. However, smaller values like integers are the same size as pointers (typically both are one
word), so there is no advantage to passing a pointer, versus passing the value. Swift offers built-in support for objects using either pass-by-reference or pass-by-value semantics, the former using the class declaration and the latter using struct. Structs in Swift have almost all the same features as classes: methods, implementing protocols and using the extension mechanisms. For this reason, Apple terms all data generically as
instances, versus objects or values. Structs do not support inheritance, however. The programmer is free to choose which semantics are more appropriate for each data structure in the application. Larger structures like windows would be defined as classes, allowing them to be passed around as pointers. Smaller structures, like a 2D point, can be defined as structs, which will be pass-by-value and allow direct access to their internal data with no indirection or reference counting. The performance improvement inherent to the pass-by-value concept is such that Swift uses these types for almost all common data types, including Int and Double, and types normally represented by objects, like String and Array. Array, Dictionary, and Set all utilize
copy on write so that their data are copied only if and when the program attempts to change a value in them. This means that the various accessors have what is in effect a pointer to the same data storage. So while the data is physically stored as one instance in memory, at the level of the application, these values are separate and physical separation is enforced by copy on write only if needed.
Extensions Extensions add new functionality to an existing type, without the need to subclass or even have access to the original source code. Extensions can add new methods, initializers, computed properties, subscripts, and protocol conformances. An example might be to add a spell checker to the base String type, which means all instances of String in the program gain the ability to spell-check. The system is also widely used as an organizational technique, allowing related code to be gathered into library-like extensions. Extensions are declared with the extension keyword. struct Rectangle { let width: Double let height: Double } extension Rectangle { var area: Double { return height * width } }
Protocol-oriented programming Protocols promise that a particular type implements a set of methods or properties, meaning that other instances in the system can call those methods on any instance implementing that protocol. This is often used in modern object-oriented languages as a substitute for
multiple inheritance, although the feature sets are not entirely similar. In Objective-C, and most other languages implementing the protocol concept, it is up to the programmer to ensure that the required methods are implemented in each class. Swift adds the ability to add these methods using extensions, and to use
generic programming (generics) to implement them. Combined, these allow protocols to be written once and support a wide variety of instances. Also, the extension mechanism can be used to add protocol conformance to an object that does not list that protocol in its definition. For example, a protocol might be declared called Printable, which ensures that instances that conform to the protocol implement a description property and a printDetails() method requirement: // Define a protocol named Printable protocol Printable { var description: String { get } // A read-only property requirement func printDetails() // A method requirement } This protocol can now be adopted by other types: // Adopt the Printable protocol in a class class MyClass: Printable { var description: String { return "An instance of MyClass" } func printDetails() { print(description) } } Extensions can be used to add protocol conformance to types. Protocols themselves can also be extended to provide default implementations of their requirements. Adopters may define their own implementations, or they may use the default implementation: extension Printable { // All Printable instances will receive this implementation, or they may define their own. func printDetails() { print(description) } } // Bool now conforms to Printable, and inherits the printDetails() implementation above. extension Bool: Printable { var description: String { return "An instance of Bool with value: \(self)" } } In Swift, like many modern languages supporting interfaces, protocols can be used as types, which means variables and methods can be defined by protocol instead of their specific type: func getSomethingPrintable() -> any Printable { return true } var someSortOfPrintableInstance = getSomethingPrintable() print(someSortOfPrintableInstance.description) // Prints "An instance of Bool with value: true" It does not matter what concrete type of someSortOfPrintableInstance is, the compiler will ensure that it conforms to the protocol and thus this code is safe. This syntax also means that collections can be based on protocols also, like let printableArray = [any Printable]. Both extensions and protocols are used extensively in Swift's standard library; in Swift 5.9, approximately 1.2 percent of all symbols within the standard library were protocols, and another 12.3 percent were protocol requirements or default implementations. For instance, Swift uses extensions to add the Equatable protocol to many of their basic types, like Strings and Arrays, allowing them to be compared with the == operator. The Equatable protocol also defines this default implementation: func !=(lhs: T, rhs: T) -> Bool This function defines a method that works on any instance conforming to Equatable, providing a
not equals operator. Any instance, class or struct, automatically gains this implementation simply by conforming to Equatable. Protocols, extensions, and generics can be combined to create sophisticated APIs. For example, constraints allow types to conditionally adopt protocols or methods based on the characteristics of the adopting type. A common use case may be adding a method on collection types only when the elements contained within the collection are Equatable: extension Array where Element: Equatable { // allEqual will be available only on instances of Array that contain Equatable elements. func allEqual() -> Bool { for element in self { if element != self.first { return false } } return true } }
Concurrency Swift 5.5 introduced structured concurrency into the language. Structured concurrency uses
Async/await syntax similar to Kotlin, JavaScript, and Rust. An async function is defined with the async keyword after the parameter list. When calling an async function the await keyword must be written before the function to indicate that execution will potentially suspend while calling function. While a function is suspended the program may run some other concurrent function in the same program. This syntax allows programs to clearly call out potential suspension points and avoid a version of the
Pyramid of Doom caused by the previously widespread use of closure callbacks. func downloadText(name: String) async -> String { let result = // ... some asynchronous downloading code ... return result } let text = await downloadText("text1") The async let syntax allows multiple functions to run in parallel. await is again used to mark the point at which the program will suspend to wait for the completion of the async functions called earlier. // Each of these calls to downloadText will run in parallel. async let text1 = downloadText(name: "text1") async let text2 = downloadText(name: "text2") async let text3 = downloadText(name: "text3") let textToPrint = await [text1, text2, text3] // Suspends until all three downloadText calls have returned. print(textToPrint) Tasks and TaskGroups can be created explicitly to create a dynamic number of child tasks during runtime: let taskHandle = Task { await downloadText(name: "someText") } let result = await taskHandle.value Swift uses the
Actor model to isolate mutable state, allowing different tasks to mutate shared state in a safe manner. Actors are declared with the actor keyword and are reference types, like classes. Only one task may access the mutable state of an actor at the same time. Actors may access and mutate their own internal state freely, but code running in separate tasks must mark each access with the await keyword to indicate that the code may suspend until other tasks finish accessing the actor's state. actor Directory { var names: [String] = [] func add(name: String) { names.append(name) } } let directory = Directory() // Code suspends until other tasks finish accessing the actor. await directory.add(name: "Tucker") print(await directory.names)
Libraries, runtime, development On Apple systems, Swift uses the same runtime as the extant
Objective-C system, but requires iOS 7 or macOS 10.9 or higher. It also depends on
Grand Central Dispatch. Swift and Objective-C code can be used in one program, and by extension, C and C++ also. Beginning in Swift 5.9,
C++ code can be used directly from Swift code. In the case of Objective-C, Swift has considerable access to the object model, and can be used to subclass, extend and use Objective-C code to provide protocol support. The converse is not true: a Swift class cannot be subclassed in Objective-C. To aid development of such programs, and the re-use of extant code, Xcode 6 and higher offers a semi-automated system that builds and maintains a
bridging header to expose Objective-C code to Swift. This takes the form of an additional
header file that simply defines or imports all of the Objective-C symbols that are needed by the project's Swift code. At that point, Swift can refer to the types, functions, and variables declared in those imports as though they were written in Swift. Objective-C code can also use Swift code directly, by importing an automatically maintained header file with Objective-C declarations of the project's Swift symbols. For instance, an Objective-C file in a mixed project called "MyApp" could access Swift classes or functions with the code #import "MyApp-Swift.h". Not all symbols are available through this mechanism, however—use of Swift-specific features like generic types, non-object optional types, sophisticated enums, or even Unicode identifiers may render a symbol inaccessible from Objective-C. Swift also has limited support for
attributes, metadata that is read by the development environment, and is not necessarily part of the compiled code. Like Objective-C, attributes use the @ syntax, but the currently available set is small. One example is the @IBOutlet attribute, which marks a given value in the code as an
outlet, available for use within
Interface Builder (IB). An
outlet is a device that binds the value of the on-screen display to an object in code. On non-Apple systems, Swift does not depend on an Objective-C runtime or other Apple system libraries; a set of Swift "Corelib" implementations replace them. These include a "swift-corelibs-foundation" to stand in for the
Foundation Kit, a "swift-corelibs-libdispatch" to stand in for the Grand Central Dispatch, and an "swift-corelibs-xctest" to stand in for the XCTest APIs from
Xcode. As of 2019, with Xcode 11, Apple has also added a major new UI paradigm called SwiftUI. SwiftUI replaces the older
Interface Builder paradigm with a new declarative development paradigm.
Memory management Swift uses
Automatic Reference Counting (ARC) to
manage memory. Every instance of a class or closure maintains a reference count which keeps a running tally of the number of references the program is holding on to. When this count reaches 0 the instance is deallocated. This automatic deallocation removes the need for a garbage collector as instances are deallocated as soon as they are no longer needed. A
strong reference cycle can occur if two instances each strongly reference each other (e.g. A references B, B references A). Since neither instances reference count can ever reach zero neither is ever deallocated, resulting in a
memory leak. Swift provides the keywords weak and unowned to prevent strong reference cycles. These keywords allow an instance to be referenced without incrementing its reference count. weak references must be optional variables, since they can change and become nil. Attempting to access an unowned value that has already been deallocated results in a runtime error. A closure within a class can also create a strong reference cycle by capturing self references. Self references to be treated as weak or unowned can be indicated using a
capture list. class Person { let name: String weak var home: Home? // Defined as a weak reference in order to break the reference cycle. weak references do not increment the reference count of the instance that they refer to. init(name: String) { self.name = name } deinit { print("De-initialized \(name)") } } class Home { let address: String var owner: Person? init(address: String, owner: Person?) { self.address = address self.owner = owner } deinit { print("De-initialized \(address)") } } var stacy: Person? = Person(name: "Stacy") var house21b: Home? = Home(address: "21b Baker Street", owner: stacy) stacy?.home = house21b // stacy and house42b now refer to each other. stacy = nil // The reference count for stacy is now 1, because house21b is still holding a reference to it. house21b = nil // house21b's reference count drops to 0, which in turn drops stacy's count to 0 because house21b was the last instance holding a strong reference to stacy. // Prints: // De-initialized 21b Baker Street // De-initialized Stacy
Debugging A key element of the Swift system is its ability to be cleanly debugged and run within the development environment, using a
read–eval–print loop (REPL), giving it interactive properties more in common with the scripting abilities of Python than traditional
system programming languages. The REPL is further enhanced with
playgrounds, interactive views running within the Xcode environment or
Playgrounds app that respond to code or debugger changes on-the-fly. Playgrounds allow programmers to add in Swift code along with markdown documentation. Programmers can step through code and add breakpoints using
LLDB either in a console or an
IDE like Xcode. == Comparisons to other languages ==