Swift cheatsheet

A complete Swift programming language reference for beginners.

Getting started

hello world

// 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.

comments

// this is just a single line comment

/**
multi-line comments are fun :)
            __
           / _)
    .-^^^-/ /
 __/       /
<__.|_|-|_|
*/

References: Swift documentation

print

print("Hello, World!")
// => Hello, World!\n

print("Hello, World!", terminator: "")
// => Hello, World!

print("foo", false, 42, separator: ", ")
// => foo, false, 42

References: print

constants and variables

// 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

characters and strings

let emoji: Character = "😅"

// unicode representation of: ♥
let blackHeart: Character = "\u{2665}"

let singleLine: String = "Hello, World!"

let multiLine: String = """
Lorem ipsum
dolor sit amet
"""

References: Character, String

string interpolation

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

basic data types

let b: Bool = false

let i: Int = 6

let f: Float = 4.20

let d: Double = 6.9

let s: String = "Lorem ipsum"

References: Bool, Int, Float, Double, String

type safety

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

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.

Data types

literals

// 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")

type(of:)

print(type(of: 1))
// => Int

print(type(of: "foo"))
// => String

print(type(of: false))
// => Bool

print(type(of: 3.14))
// => Double

References: type(of:)

optionals

var success: Bool? = false
var responseCode: Int?
var error: Optional<String> = nil

print(responseCode) // nil

responseCode = 404
error = "Something went wrong"

print(success) // => Optional(false)
print(responseCode) // => Optional(404)

References: Optional

arrays

// 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

sets

// a set of unique Int values
let values: Set<Int> = [1, 1, 2, 3, 3, 5]

print(values)
// => [3, 2, 5, 1] (or different)

// a set of String values
let names: Set = ["John", "Bob"]

// an empty set of Strings
var empty: Set<String> = []

References: Set

dictionaries

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

tuples

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

signed integers

let i: Int // 32 or 64 bit

let i1: Int8 // 8 bit
let i2: Int16 // 16 bit
let i3: Int32 // 32 bit
let i4: Int64 // 64 bit

// you can get min and max values
print(Int8.min) // => -128
print(Int8.max) // => 127

References: Int, Int8, Int16, Int32, Int64

unsigned integers

let u: UInt // 32 or 64 bit
let u1: UInt8 // 8 bit
let u2: UInt16 // 16 bit
let u3: UInt32 // 32 bit
let u4: UInt64 // 64 bit

print(UInt8.min) // => 0
print(UInt8.max) // => 255

References: UInt, UInt8, UInt16, UInt32, UInt64

Operators

assignment

let a = "foo"
var b = 42
let c = 69

b = c

print(b) // => 69

References: Assignment Operator

arithmetic

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

compound assignment

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

comparison

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

ternary conditional

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

nil-coalescing

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

range

// 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

logical

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

other

// String concatenation
print("foo" + " " + "bar") // => foo bar

// remainder
print(10 % 6) // => 4

// unary plus
print(+42) // => 42

// unary minus
print(-69) // => -69

References: Operators

Control flow

if

var isItRaining = true

if isItRaining {
    print("Take an umbrella.")
}

// => Take an umbrella.

References: If Statement

if-else

var score = 4

if score > 5 {
    print("Great job.")
}
else {
    print("Nice try.")
}

// => Nice try.

References: If Statement

if-elseif-else

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

guard

var score = 3
guard score > 1 else {
    fatalError("You've failed.")
}
print("You've passed.")

// => You've passed.

References: Guard Statement

switch

var statusCode = 404

switch statusCode {
case 200:
    print("Ok")
case 404:
    print("Not found")
default:
    print(statusCode)
}

// => Not found

References: Switch Statement

optional binding

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

for-in

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

while

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

repeat-while

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

break

for i in 1...5 {
    if i == 4 {
        break
    }
    print(i)
}

// => 1
// => 2
// => 3

References: Break

continue

for i in 1...5 {
    if i == 4 {
        continue
    }
    print(i)
}

// => 1
// => 2
// => 3
// => 5

References: Continue

fallthrough

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

Functions

declaration

// declaring the function
func greet() {
    print("Hello, World!")
}

// calling the function
greet()
// => Hello, World!

References: Defining and Calling Functions

parameters

// 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

return 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

argument labels

// 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

variadic functions

// 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

inout 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

nested functions

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

overloading parameters

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

References: Functions, Generics

closures

// closure block
let log: (Int) -> Void = { value in
    print("Value: \(value)")
}

log(420) // => Value: 420

log(69) // => Value: 469

References: Closures

Error handling

stop execution

// 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

throw, do-try-catch

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

try, try?, try!

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

catch specific errors

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

catch and switch specific errors

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

localized error messages

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

optionals as error indicators

// 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

the result type

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

try get 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

Enums

basics

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

raw values

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

all cases

enum Directions: CaseIterable {
    case north
    case east
    case south
    case west
}

print(Directions.allCases)
// => [.north, .east, .south, .west]

References: CaseIterable

associated values

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

Structs

basics

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

init

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

functions

struct Person {
    let name: String
    let age: Int
    
    func printInfo() {
        print("Name: \(name), age: \(age)")
    }
}

let john = Person(name: "John", age: 42)
let bob = Person(name: "Bob", age: 69)

john.printInfo() // => Name: John, age: 42
bob.printInfo() // => Name: Bob, age: 69

References: Methods

static properties

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

static functions

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

debug description

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

Classes

basics

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

methods

class Person {

    var name: String
    var age: Int
    
    init(name: String, age: Int) {
        self.name = name
        self.age = age
    }
    
    func incrementAge() {
        age += 1
    }
}


let john = Person(name: "John", age: 42)
john.incrementAge()

print(john.name, john.age) // => John 43

References: Methods

computed 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 {
        2022 - age
    }
}


let john = Person(name: "John", age: 42)


print(john.name, john.age, john.birthDate)
// => John 43 1980

References: Properties

property setter and getter

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

static 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

static methods

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

subclassing

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

// 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

deinit

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

breaking reference cycles

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

access control

/**
 open
 final
 public
 internal
 private
 fileprivate
*/

open class John: Person {

    private func sayHello() {
        print("Hello!")
    }
}

References: Access Control

type casting

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

Protocols

basics

protocol Animal {
    var name: String { get set }
    var numberOflegs: Int { get }

    func makeSound()
}

References: Protocols

property requirements

protocol Animal {
    var name: String { get }
}

struct Cat: Animal {
    var name: String
}

let lucky = Cat(name: "Lucky")

print(lucky.name) // => Lucky

References: Property Requirements

method 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

extensions

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

conformance with 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

function overiding with extensions

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

Generics

the problem

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)

generic functions

// 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

generic types

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

protocols with associated types

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

extensions and type constraints

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

functions and type constraints

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

Concurrency

async, await

// async function
func performTask() async {
    // ...
}

await performTask()


// async throwing function
func performThrowingTask() async throws {
    // ...
}

try await performTask()

checked continuation

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)
    }
}                    

unsafe continuation

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))
    }
}

async main program

@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)
        }
    }
}

async calls from sync context

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()

parallel execution

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]

task groups

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)
}

different task results

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"]

basic actor

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)

Miscellaneous

basic result builder

@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

basic subscript

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