A complete Swift programming language reference for beginners.
// FILE: main.swift
print("Hello, World! - 0")
/**
Run Swift files using the command line:
$ swift main.swift
> Hello, World!
*/
You can install Swift by following the guides on swift.org.
// this is just a single line comment
/**
multi-line comments are fun :)
__
/ _)
.-^^^-/ /
__/ /
<__.|_|-|_|
*/
References: Swift documentation
// a constant
let name: String = "John"
print(name) // => John
name = "Bob" // => ERROR
// Cannot assign to value:
// 'name' is a 'let' constant
// a variable
var age: Int = 42
age = 69
print(age) // => 69
let emoji: Character = "😅"
// unicode representation of: ♥
let blackHeart: Character = "\u{2665}"
let singleLine: String = "Hello, World!"
let multiLine: String = """
Lorem ipsum
dolor sit amet
"""
let name = "John"
let age = 42
let end = "years old."
// String interpolation
print("\(name) is \(age) \(end).")
// => John is 42 years old.
References: Strings and Characters
var name: String = "John"
name = 69 // => ERROR
// Cannot assign value of type
// 'Int' to type 'String'
name = "Bob" // this will work
References: Type Safety and Type Inference
let bool = false
let int = 6
let double = 6.9
let string = "Lorem ipsum"
Swift infers (automatically figures out) data types when assigning values to a constant or a variable.
// bool
let value = false
// string
let foo = "foo"
// floating point
let pi = 3.14
// integer
let decimal = 42
let binary = 0b101010
let octal = 0o52
let hex = 0x2A
let oneBillion = 1_000_000_000
// other
let arr = [1, 2, 3]
let dict = ["foo": 42, "bar": 69]
let tuple = (404, "Not found")
// an array of Int elements
let numbers: Array<Int> = [1, 2, 3, 4, 5]
// an array of String elements
let names: [String] = ["Joe", "Bob"]
// an empty array of Strings
var empty: [String] = []
// an array of Doubles
var mathConstants = [3.14, 2.71, 0.57]
// array of Any types
var array: [Any] = [1, "foo", false, 3.14]
References: Array
var responses: Dictionary<Int, String> = [
200: "Ok",
404: "Not found",
500: "Internal server error",
]
let scores: [String: Double] = [
"John": 42.1,
"Bob": 31.6,
]
var empty: [String: String] = [:]
References: Dictionary
var constants: (Double, Double, Double) = (
3.14159,
2.71828,
0.57721
)
print(constants.0, constants.1, constant.2)
// => 3.14159 2.71828 0.57721
constants.0 = 4.20
print(constants.0) // => 4.20
var result = (
code: 404,
error: "Not found"
)
print(result.error) // Not found
result.code = 200
let a = "foo"
var b = 42
let c = 69
b = c
print(b) // => 69
References: Assignment Operator
let x = 5
let y = 5
var z = 0
z = x + y // addition
print(z) // => 10
z = x - y // subtraction
print(z) // => 0
z = x * y // multiplication
print(z) // => 25
z = x / y // division
print(z) // => 1
References: Arithmetic Operators
var x = 10
x += 2 // addition
print(x) // => 12
x -= 2 // subtraction
print(x) // => 10
x *= 2 // multiplication
print(x) // => 20
x /= 2 // division
print(x) // => 10
References: Compound Assignment Operators
let x = 6
let y = 9
var z = false
z = x == y // equal
print(z) // false
z = x != y // not equal
print(z) // true
z = x > y // greater than
print(z) // false
z = x >= y // greater than or equal
print(z) // false
z = x < y // less than
print(z) // true
z = x <= y // less than or equal
print(z) // true
References: Comparison Operators
let x = 12
let result1 = x > 10 ? "win" : "lose"
print("You " + result2)
// => You win
let y = 7
let result2 = y > 10 ? "win" : "lose"
print("You " + result2)
// => You lose
References: Ternary Conditional Operator
let error1: String? = "Server error"
let message1 = error1 ?? "Unknown error"
print(message1)
// => Server error
let error2: String? = nil
let message2 = error2 ?? "Unknown error"
print(message2)
// => Unknown error
References: Nil-Coalescing Operator
// closed range
let a = 1...5
// half open range
let b = 0..<9
// one-sided ranges
let c = 2...
let d = ...2
let e = ..<2
References: Range Operators
let x = true
let y = false
// NOT
let r1 = !x
print(r1) // => false
// AND
let r2 = x && y
print(r2) // => false
// OR
let r3 = x || y
print(r3) // => true
References: Logical Operators
var isItRaining = true
if isItRaining {
print("Take an umbrella.")
}
// => Take an umbrella.
References: If Statement
var score = 4
if score > 5 {
print("Great job.")
}
else {
print("Nice try.")
}
// => Nice try.
References: If Statement
var score = 3
if score > 5 {
print("Great job.")
}
else if score > 2 {
print("Not bad.")
}
else {
print("Nice try.")
}
// => Not bad.
References: If Statement
var score = 3
guard score > 1 else {
fatalError("You've failed.")
}
print("You've passed.")
// => You've passed.
References: Guard Statement
var statusCode = 404
switch statusCode {
case 200:
print("Ok")
case 404:
print("Not found")
default:
print(statusCode)
}
// => Not found
References: Switch Statement
var possibleName: String? = "John"
if let name = possibleName {
print(name) // => John
}
if let possibleName {
print(possibleName) // => John
}
guard let name = possibleName else {
fatalError("Missing name")
}
print("Hello, \(name)!")
// => Hello, John!
References: Optional binding
let names = ["John", "Bob"]
for name in names {
print("Hello, \(name)!")
}
// => Hello, John!
// => Hello, Bob!
let scores = [
"John": 8,
"Bob": 7,
]
for (name, score) in scores {
print("\(name)'s score: \(score)")
}
// => John's score: 8
// => Bob's score: 7
References: For-In Loops
var i = 0
while i < 3 {
print("\(i + 1).")
i += 1
}
// => 1.
// => 2.
// => 3.
The while block won't be executed if the condition is not true.
References: While Loops
var i = 0
repeat {
print("\(i + 1).")
i += 1
}
while i < 3
// => 1.
// => 2.
// => 3.
The repeat block will be executed at least 1 time, if the while condition is true, the loop keeps going.
References: Repeat-While Loops
let number = 5
var description = "The number is"
switch number {
case 2, 3, 5, 7:
description += " a prime, and also"
fallthrough
default:
description += " an integer."
}
print(description)
// => The number is a prime,
// and also an integer.
References: Fallthrough
// declaring the function
func greet() {
print("Hello, World!")
}
// calling the function
greet()
// => Hello, World!
References: Defining and Calling Functions
// explicit Void return type
func greet(name: String) -> Void {
print("Hello, \(name)!")
}
greet(name: "John")
// => Hello, John!
greet(name: "Bob")
// => Hello, Bob!
References: Function Parameters and Retrun Values
// returns with an Int type
func add(_ a: Int, _ b: Int) -> Int {
return a + b
}
// implicit return
func greet(name: String) -> String {
"Hello, \(name)!"
}
print(greet(name: "John"))
// => Hello, John!
print(greet(name: "Bob"))
// => Hello, Bob!
References: Functions With an Implicit Return
// omit argument labels
func add(_ a: Int, _ b: Int) -> Int {
return a + b
}
// custom argument labels
func printSum(of a: Int, and b: Int) {
let c = add(a, b)
print("\(a) + \(b) = \(c)")
}
printSum(of: 4, and: 2)
// => 4 + 2 = 6
References: Argument Labels and Parameter Names
// you can pass multiple Int values
func add(_ numbers: Int...) -> Int {
var result = 0
// numbers is an Array<Int> type
for number in numbers {
result += number
}
return result
}
print(add(1,2,3,4))
// => 10
References: Variadic Parameters
func increment(_ value: inout Int) {
value += 1
}
var counter = 0
increment(&counter)
print(counter) // => 1
increment(&counter)
print(counter) // => 2
References: In-Out Parameters
func printSum(of a: Int, and b: Int) {
// nested function
func add(_ a: Int, _ b: Int) -> Int {
a + b
}
let c = add(a, b)
print("The sum is \(c)")
}
printSum(of: 42, and: 6) // => 48
References: Nested Functions
func add(_ a: Int, _ b: Int) -> Int {
print("Add two Int values")
return a + b
}
func add(_ a: Float, _ b: Float) -> Float {
print("Add two Float values")
return a + b
}
print(add(42, 69))
// => Add two Int values
// => 111
print(add(4.20, 6.9))
// => Add two Float values
// => 11.1
// check a condition, fail if false
precondition(false, "ouch")
// indicate a precondition failure
preconditionFailure("ouch")
// assert a condition, fail if false
assert(false, "ouch")
// indicate an assertion failure
assertionFailure("ouch")
// fatal error
fatalError("ouch")
// exit with a failure
exit(EXIT_FAILURE)
References: precondition, assert, fatalError, exit
enum UserError: Error {
case invalidPassword
}
func login() throws {
throw UserError.invalidPassword
}
do {
try login()
}
catch {
// this block will be executed
print(error.localizedDescription)
}
// => The operation couldn’t be completed.
// (Cheatsheet.UserError error 0.)
References: Handling Errors Using Do-Catch
enum UserError: Error {
case invalidPassword
}
func login() throws {
throw UserError.invalidPassword
}
do {
try login()
}
catch {} // catches the error
try? login() // hides the error
try! login() // FATAL ERROR
// Thread 1: Fatal error: 'try!'
// expression unexpectedly
// raised an error:
// Cheatsheet.UserError.invalidPassword
References: Optional, Converting Errors to Optional Values
enum UserError: Error {
case invalidPassword
}
func login() throws {
throw UserError.invalidPassword
}
do {
try login()
}
catch UserError.invalidPassword {
// this block will be executed
print("Invalid password")
}
catch {
print(error.localizedDescription)
}
// => Invalid password
References: Handling Errors Using Do-Catch
enum UserError: Error {
case invalidPassword
}
func login() throws {
throw UserError.invalidPassword
}
do {
try login()
}
catch let error as UserError {
switch error {
case .invalidPassword:
print("Invalid password")
}
}
catch {
print(error.localizedDescription)
}
// => Invalid password
References: Error Handling
enum UserError: LocalizedError {
case invalidPassword
var errorDescription: String? {
"Invalid password"
}
}
func login() throws {
throw UserError.invalidPassword
}
do {
try login()
}
catch {
print(error.localizedDescription)
}
// => Invalid password
References: Error Handling
// Int type with 0 value
let zeroValue = Int("0")!
// Int? type with nil value
let nilValue = Int("not a number")
guard let number = Int("6") else {
fatalError("Invalid number.")
}
print(number) // => 6
References: Optional
enum DivisionError: Error {
case zeroDivisor
}
func divide(
_ x: Int,
_ y: Int
) -> Result<Int, DivisionError> {
guard y != 0 else {
return .failure(.zeroDivisor)
}
return .success(x / y)
}
let result = divide(10, 0)
switch result {
case .success(let number):
print(number)
case .failure(let error):
print(error.localizedDescription)
}
// => The operation couldn’t be completed.
// (Cheatsheet.DivisionError error 0.)
References: Result
enum MyError: Error {
case ouch
}
let result: Result<String, Error>
// result = .success("ok")
result = .failure(MyError.ouch)
do {
let value = try result.get()
print(value)
}
catch {
print(error.localizedDescription)
}
// => The operation couldn’t be completed.
// (Cheatsheet.MyError error 0.)
References: Result
enum Weekdays {
case monday
case tuesday
case wednesday
case thursday
case friday
case saturday
case sunday
}
let weekday: Weekdays = .monday
print(weekday) // monday
References: Enumerations
enum Characters: String {
case newline = "\n"
case tab = "\t"
}
let character: Characters = .newline
switch character {
case .newline:
print("New line")
case .tab:
print("Tab")
}
// => New line
References: Raw Values
enum Directions: CaseIterable {
case north
case east
case south
case west
}
print(Directions.allCases)
// => [.north, .east, .south, .west]
References: CaseIterable
enum HttpStatus {
case error
case code(Int)
}
let status = HttpStatus.code(404)
switch status {
case .error:
print("HTTP error")
case .code(let value):
print("HTTP code: \(value)")
}
// => HTTP code: 404
References: Associated Values
struct Person {
let name: String
var age: Int
}
// value type
var john = Person(name: "John", age: 42)
let bob = Person(name: "Bob", age: 69)
print(john.name, john.age) // => John 42
print(bob.name, bob.age) // => Bob 69
john.age = 69
print(john.name, john.age) // => John 69
bob.age = 70 // ERROR
// Cannot assign to property:
// 'bob' is a 'let' constant
References: Structures and Classes
struct Person {
let name: String
let age: Int
init(_ name: String, age: Int) {
self.name = name
self.age = age
}
}
let john = Person("John", age: 42)
let bob = Person("Bob", age: 69)
print(john.name, john.age) // => John 42
print(bob.name, bob.age) // => Bob 69
References: Initialization
struct Person {
static var kind = "human"
let name: String
let age: Int
}
print(Person.kind) // => human
Person.kind = "alien"
print(Person.kind) // => alien
References: Properties
struct Person {
let name: String
let age: Int
static func john() -> Person {
.init(name: "John", age: 42)
}
}
let john = Person.john()
print(john.name)
print(john.age)
References: Methods
struct Person: CustomDebugStringConvertible {
let name: String
let age: Int
var debugDescription: String {
"Person<name: \(name), age: \(age)>"
}
}
let john = Person(name: "John", age: 42)
print(john) // => Person<name: John, age: 42>
References: Extensions
class Person {
var name: String
var age: Int
init(name: String, age: Int) {
self.name = name
self.age = age
}
}
// reference type
let john = Person(name: "John", age: 42)
print(john.name) // => John
john.age = 69
print(john.age) // => 69
References: Structures and Classes
class Person {
var name: String
var age: Int
init(name: String, age: Int) {
self.name = name
self.age = age
}
// let's assume it's 2022 :)
var birthDate: Int {
2022 - age
}
}
let john = Person(name: "John", age: 42)
print(john.name, john.age, john.birthDate)
// => John 43 1980
References: Properties
class Person {
var name: String
var age: Int
init(name: String, age: Int) {
self.name = name
self.age = age
}
// let's assume it's 2022 :)
var birthDate: Int {
get {
2022 - age
}
set {
age = 2022 - newValue
}
}
}
let john = Person(name: "John", age: 42)
john.birthDate = 1953
print(john.age)
print(john.name, john.age, john.birthDate)
// => John 69 1953
References: Properties
class Person {
static let currentYear = 2022
var name: String
var age: Int
init(name: String, age: Int) {
self.name = name
self.age = age
}
// let's assume it's 2022 :)
var birthDate: Int {
get {
Person.currentYear - age
}
set {
age = Person.currentYear - newValue
}
}
}
let john = Person(name: "John", age: 42)
john.birthDate = 1953
print(john.age)
print(john.name, john.age, john.birthDate)
// => John 69 1953
References: Properties
class Person {
var name: String
var age: Int
init(name: String, age: Int) {
self.name = name
self.age = age
}
static func john() -> Person {
.init(name: "John", age: 42)
}
}
let john = Person.john()
print(john.name, john.age) // => John 42
References: Methods
class Person {
var name: String
var age: Int
init(name: String, age: Int) {
self.name = name
self.age = age
}
}
class John: Person {
init() {
super.init(name: "John", age: 42)
}
}
let john = John()
print(john.name, john.age) // => John 42
References: Inheritance
// singleton class
final public class Storage {
public static let shated = Storage()
var limit: Int
private init(_ limit: Int = 20) {
self.limit = limit
}
}
print(Storage.shated.limit) // => 20
Storage.shated.limit = 10
print(Storage.shated.limit) // => 10
References: Initialization
class Person {
var name: String
var age: Int
init(name: String, age: Int) {
self.name = name
self.age = age
}
deinit {
print("Deinit \(name).")
}
}
var john: Person? = Person(
name: "John",
age: 42
)
// force call deinit
john = nil
// => Deinit John.
References: Deinitialization
class Address {
var line: String
weak var person: Person?
init(line: String, person: Person? = nil) {
self.line = line
self.person = person
}
deinit { print("Address deinit") }
}
class Person {
var name: String
var address: Address?
init(name: String, address: Address? = nil) {
self.name = name
self.address = address
}
deinit { print("Person deinit") }
}
var person: Person? = Person(name: "John")
var address: Address? = Address(line: "Six street 9.")
person?.address = address
address?.person = person
person = nil
address = nil
// => Person deinit
// => Address deinit
References: Automatic Reference Counting
/**
open
final
public
internal
private
fileprivate
*/
open class John: Person {
private func sayHello() {
print("Hello!")
}
}
References: Access Control
protocol Person {}
class John: Person {}
class Bob: Person {}
let john: Any = John()
let bob: Bob = Bob()
print(john is Person) // => true
let p1 = bob as Person // Person
let p2 = john as? Person // Person?
let p3 = john as! Person // Person
References: Type Casting
protocol Animal {
var name: String { get }
}
struct Cat: Animal {
var name: String
}
let lucky = Cat(name: "Lucky")
print(lucky.name) // => Lucky
References: Property Requirements
protocol Animal {
var name: String { get }
func makeSound()
}
struct Cat: Animal {
var name: String
func makeSound() {
print("Meow")
}
}
let lucky = Cat(name: "Lucky")
lucky.makeSound() // => Meow
References: Method Requirements
protocol Animal {
var name: String { get }
func makeSound()
}
struct Cat: Animal {
var name: String
func makeSound() {
print("Meow")
}
}
extension Animal {
func sleep() {
print("Zzz...")
}
}
let lucky = Cat(name: "Lucky")
lucky.sleep() // => Zzz...
References: Extensions
protocol Animal {
var name: String { get }
func makeSound()
}
class Cat {
var name: String
init(name: String) {
self.name = name
}
}
extension Cat: Animal {
func makeSound() {
print("Meow")
}
}
let lucky = Cat(name: "Lucky")
lucky.makeSound() // => Meow
References: Adding Protocol Conformance with an Extension
protocol Animal {
var name: String { get }
func makeSound()
}
struct Cat: Animal {
var name: String
func makeSound() {
print("Meow")
}
}
extension Animal {
func sleep() {
print("Zzz...")
}
}
extension Cat {
func sleep() {
print("Purr...")
}
}
let lucky = Cat(name: "Lucky")
lucky.sleep() // => Purr...
References: Extensions
func swapTwoValues(
_ a: inout Int,
_ b: inout Int)
{
(a, b) = (b, a)
}
func swapTwoValues(
_ a: inout String,
_ b: inout String)
{
(a, b) = (b, a)
}
var a = 420
var b = 69
swapTwoValues(&a, &b)
print(a, b)
var x = "foo"
var y = "bar"
swapTwoValues(&x, &y)
print(x, y)
// T is a generic placeholder for the type
func swapTwoValues<T>(
_ a: inout T,
_ b: inout T)
{
(a, b) = (b, a)
}
var a = 420
var b = 69
swapTwoValues(&a, &b)
print(a, b)
var x = "foo"
var y = "bar"
swapTwoValues(&x, &y)
print(x, y)
References: Generics
struct Stack<Element> {
var items: [Element] = []
mutating func push(_ item: Element) {
items.append(item)
}
mutating func pop(){
items.removeLast()
}
}
var stack = Stack<String>()
stack.push("a")
stack.push("b")
stack.push("c")
stack.pop()
print(stack.items) // ["a", "b"]
References: Generics
protocol Container {
associatedtype Item
mutating func push(_ item: Item)
}
struct Stack<Element>: Container {
var items: [Element] = []
mutating func push(_ item: Element) {
items.append(item)
}
}
var stack = Stack<String>()
stack.push("a")
stack.push("b")
stack.push("c")
print(stack.items) // ["a", "b", "c"]
References: Associated Types
extension Array where Element == Int {
func printSum() {
var sum: Int = 0
for i in self {
sum += i
}
print(sum)
}
}
let array = [1, 2, 3, 4, 5, 6]
array.printSum() // => 21
References: Extending a Generic Type
extension Array {
func printSum() where Element == Int {
var sum: Int = 0
for i in self {
sum += i
}
print(sum)
}
}
func checkIsEqual<T: Equatable>(
_ value1: T,
_ value2: T
) -> Bool {
value1 == value2
}
let array = [1, 2, 3, 4, 5, 6]
array.printSum() // => 21
print(checkIsEqual("420", "69"))
// => false
References: Type Constraints
// async function
func performTask() async {
// ...
}
await performTask()
// async throwing function
func performThrowingTask() async throws {
// ...
}
try await performTask()
enum CustomError: Error {
case ouch
}
func performTask() async -> String {
await withCheckedContinuation { c in
c.resume(returning: "foo")
}
}
func performThrowingTask() async throws -> String {
try await withCheckedThrowingContinuation { c in
c.resume(throwing: CustomError.ouch)
}
}
enum CustomError: Error {
case ouch
}
func perormTask() async -> String {
await withUnsafeContinuation { c in
c.resume(with: .success("foo"))
}
}
func performThrowingTask() async throws -> String {
try await withUnsafeThrowingContinuation { c in
c.resume(with: .failure(CustomError.ouch))
}
}
@main
struct MyProgram {
static func main() async {
let result = await performTask()
switch result {
case .success(let value):
print(value)
exit(EXIT_SUCCESS)
case .failure(let error):
fatalError(error.localizedDescription)
}
}
}
func performTask() async {
print("Start.")
// wait for 5 seconds...
sleep(5)
// try? await Task.sleep(nanoseconds: 5 * 1_000_000_000)
print("Done.")
}
Task.detached {
await performTask()
exit(EXIT_SUCCESS)
}
dispatchMain()
func performTask(_ wait: Int) async {
print("Sleep for (\(wait)) seconds...")
sleep(UInt32(wait))
print("Slept \(wait) seconds.")
}
async let t1: () = performTask(3)
async let t2: () = performTask(1)
async let t3: () = performTask(2)
// executing tasks in parallel
_ = await [t1, t2, t3]
func performTask(_ wait: Int) async -> Int {
print("Sleep for (\(wait)) seconds...")
sleep(UInt32(wait))
print("Slept \(wait) seconds.")
return wait
}
await withTaskGroup(of: Int.self) { group in
group.addTask { await performTask(1) }
group.addTask { await performTask(2) }
group.addTask { await performTask(3) }
var sum: Int = 0
for await res in group {
sum += res
}
print(sum)
}
func intTask() async -> Int { 42 }
func stringTask() async -> String { "foo" }
enum TaskResults {
case first(Int)
case second(String)
}
await withTaskGroup(of: TaskResults.self) { group in
group.addTask { .first(await intTask()) }
group.addTask { .second(await stringTask()) }
var results: [Any] = []
for await res in group {
switch res {
case .first(let value):
results.append(value)
case .second(let value):
results.append(value)
}
}
print(results)
}
// => [42, "foo"]
actor AtomicStorage {
private var storage: [Int: String]
init() {
self.storage = [:]
}
func get(_ key: Int) -> String? {
storage[key]
}
func set(_ key: Int, value: String) {
storage[key] = value
}
var allValues: [Int: String] {
storage
}
}
let storage = AtomicStorage()
await withTaskGroup(of: Void.self) { group in
for i in 0..<100 {
group.addTask {
await storage.set(i, value: "foo")
}
}
}
print(await storage.allValues)
@resultBuilder
public enum StringBuilder {
public static func buildBlock(
_ components: String...
) -> [String] {
components
}
}
@StringBuilder func getStrings() -> [String] {
"foo"
"bar"
"baz"
}
let values = getStrings()
print(values)
// => ["foo", "bar", "baz"]
References: Result builders in Swift
struct Config {
private var values: [String: String]
init() {
self.values = [
"foo": "bar"
]
}
subscript(index: String) -> String? {
get {
values[index]
}
set {
values[index] = newValue
}
}
}
let config = Config()
guard let value = config["foo"] else {
fatalError("Missing config key.")
}
print(value) // => bar
References: Subscripts