Swift

As explained by Hacking With Swift - Swift In Sixty Seconds and the official Swift Book

Complex Types

  • Set Set(["red", "green", "blue", "red", "blue"]).

  • Array let beatles = ["john", "paul", "george", "ringo"].

  • Tuple var name = (first: "Taylor", last: "Swift") access via name.0 or name.first.

  • Enum

enum Result {
    case success
    case failure
}
  • Associated values

enum Activity {
    case bored
    case running(destination: String)
    case talking(topic: String)
    case singing(volume: Int)
}
let talking = Activity.talking(topic: "football")

Basic behaviors

Operators

Usual operators

  • + - * / %

  • -=

  • +=

  • ==

  • !=

  • <

  • <=

  • &&

  • ||

  • print(firstCard == secondCard ? "Cards are the same" : "Cards are different")

And range operator

  • 0...50

  • 0..<50

Loops

As usual but do while is called repeat while and exiting outer loops is possible by using labels:

outerLoop: for i in 1...10 {
    for j in 1...10 {
        let product = i * j
        print ("\(i) * \(j) is \(product)")

        if product == 50 {
            print("It's a bullseye!")
            break outerLoop
        }
    }
}

Functions

Default value

func greet(_ person: String, nicely: Bool = true) {
    if nicely == true {
        print("Hello, \(person)!")
    } else {
        print("Oh no, it's \(person) again...")
    }
}

Variadic functions Mark unknown amount of parameters with ....

func square(numbers: Int...) {
    for number in numbers {
        print("\(number) squared is \(number * number)")
    }
}

square(numbers: 1, 2, 3, 4, 5)

Throwing errors

enum PasswordError: Error {
    case obvious
}


func checkPassword(_ password: String) throws -> Bool {
    if password == "password" {
        throw PasswordError.obvious
    }

    return true
}

do {
    try checkPassword("password")
    print("That password is good!")
} catch {
    print("You can't use that password.")
}

Inout parameters can be changed inside your function.

func doubleInPlace(number: inout Int) {
    number *= 2
}
var myNum = 10 
doubleInPlace(number: &myNum)

Closures

https://www.hackingwithswift.com/sixty

let driving = {
    print("I'm driving in my car")
}

driving()

With parameters

let driving = { (place: String) in
    print("I'm going to \(place) in my car")
}

driving("London")

With return values

let drivingWithReturn = { (place: String) -> String in
    return "I'm going to \(place) in my car"
}
let message = drivingWithReturn("London")
print(message)

Closures as parameter

let driving = {
    print("I'm driving in my car")
}

func travel(action: () -> Void) {
    print("I'm getting ready to go.")
    action()
    print("I arrived!")
}

travel(action: driving)

Trailing closures syntax

func travel(action: () -> Void) {
    print("I'm getting ready to go.")
    action()
    print("I arrived!")
}

travel() {
    print("I'm driving in my car")
}

// because of missing params

travel {
    print("I'm driving in my car")
}

Closures with parameters

func travel(action: (String) -> Void) {
    print("I'm getting ready to go.")
    action("London")
    print("I arrived!")
}

travel { (place: String) in
    print("I'm going to \(place) in my car")
}

Closures with return values

func travel(action: (String) -> String) {
    print("I'm getting ready to go.")
    let description = action("London")
    print(description)
    print("I arrived!")
}

travel { (place: String) -> String in
    return "I'm going to \(place) in my car"
}

Shorthand parameter names

func travel(action: (String) -> String) {
    print("I'm getting ready to go.")
    let description = action("London")
    print(description)
    print("I arrived!")
}

travel { (place: String) -> String in
    return "I'm going to \(place) in my car"
}

travel { place -> String in
    return "I'm going to \(place) in my car"
}

Closures with multiple parameters

func travel(action: (String, Int) -> String) {
    print("I'm getting ready to go.")
    let description = action("London", 60)
    print(description)
    print("I arrived!")
}

travel {
    "I'm going to \($0) at \($1) miles per hour."
}

Return closures from functions

func travel() -> (String) -> Void {
    return {
        print("I'm going to \($0)")
    }
}
let result = travel()
result("London")

Capturing values

Generics

func someFunction<T: SomeClass, U: SomeProtocol>(someT: T, someU: U) {
    // function body goes here
}
func mid<T>(array: [T]) -> T? {
  guard !array.isEmpty else { return nil }
  return array.sorted()[(array.count - 1) / 2]
}

func mid<T: Comparable>(array: [T]) -> T? {
  guard !array.isEmpty else { return nil }
  return array.sorted()[(array.count - 1) / 2]
}

mid(array: [3, 5, 1, 2, 4]) // 3

Structs

Comparing Structures and Classes

Structures and classes in Swift have many things in common. Both can:

  • Define properties to store values

  • Define methods to provide functionality

  • Define subscripts to provide access to their values using subscript syntax

  • Define initializers to set up their initial state

  • Be extended to expand their functionality beyond a default implementation

  • Conform to protocols to provide standard functionality of a certain kind

struct Sport {
    var name: String
}

var tennis = Sport(name: "Tennis")
print(tennis.name)

Computed properties

struct Sport {
    var name: String
    var isOlympicSport: Bool

    var olympicStatus: String {
        if isOlympicSport {
            return "\(name) is an Olympic sport"
        } else {
            return "\(name) is not an Olympic sport"
        }
    }
}

let chessBoxing = Sport(name: "Chessboxing", isOlympicSport: false)
print(chessBoxing.olympicStatus)

Property observers via didSet and willSet.

struct Progress {
    var task: String
    var amount: Int {
        didSet {
            print("\(task) is now \(amount)% complete")
        }
    }
}

Methods

struct City {
    var population: Int

    func collectTaxes() -> Int {
        return population * 1000
    }
}

Mutating methods When you want to change a property inside a method, you need to mark it using the mutating keyword,

struct Person {
    var name: String

    mutating func makeAnonymous() {
        name = "Anonymous"
    }
}
var person = Person(name: "Ed")
person.makeAnonymous()

Initializers

struct User {
    var username: String

    init() {
        username = "Anonymous"
        print("Creating a new user!")
    }
}

Lazy

lazy var familyTree = FamilyTree()

Static properties and methods

struct Student {
    static var classSize = 0
    var name: String

    init(name: String) {
        self.name = name
        Student.classSize += 1
    }
}

Access control

struct Person {
    private var id: String

    init(id: String) {
        self.id = id
    }
}

Classes

Comparing Structures and Classes

Classes have additional capabilities that structures don’t have:

  • Inheritance enables one class to inherit the characteristics of another.

  • Type casting enables you to check and interpret the type of a class instance at runtime.

  • Deinitializers enable an instance of a class to free up any resources it has assigned.

  • Reference counting allows more than one reference to a class instance.

class Dog {
    var name: String
    var breed: String

    init(name: String, breed: String) {
        self.name = name
        self.breed = breed
    }
}

Inheritance

class Poodle: Dog {
    init(name: String) {
        super.init(name: name, breed: "Poodle")
    }
}

Overriding methods

class Dog {
    func makeNoise() {
        print("Woof!")
    }
}

[...]

class Poodle: Dog {
    override func makeNoise() {
        print("Yip!")
    }
}

Final class no other class can inherit from it.

final class Dog {
    var name: String
    var breed: String

    init(name: String, breed: String) {
        self.name = name
        self.breed = breed
    }
}

Deinitializers

class Person {
    var name = "John Doe"

    init() {
        print("\(name) is alive!")
    }

    func printGreeting() {
        print("Hello, I'm \(name)")
    }

    deinit {
        print("\(name) is no more!")
    }
}

Protocols

Protocols are a way of describing what properties and methods something must have.

protocol Identifiable {
    var id: String { get set }
}

struct User: Identifiable {
    var id: String
}

func displayID(thing: Identifiable) {
    print("My ID is \(thing.id)")
}

Inheritance

protocol Payable {
    func calculateWages() -> Int
}

protocol NeedsTraining {
    func study()
}

protocol HasVacation {
    func takeVacation(days: Int)
}

protocol Employee: Payable, NeedsTraining, HasVacation { }

Extensions

extension Int {
    func squared() -> Int {
        return self * self
    }
}

Protocol extensions

let pythons = ["Eric", "Graham", "John", "Michael", "Terry", "Terry"]
let beatles = Set(["John", "Paul", "George", "Ringo"])

extension Collection {
    func summarize() {
        print("There are \(count) of us:")

        for name in self {
            print(name)
        }
    }
}

pythons.summarize()
beatles.summarize()

Protocol oriented programming

Protocol extensions can provide default implementations for our own protocol methods. This makes it easy for types to conform to a protocol, and allows a technique called “protocol-oriented programming” – crafting your code around protocols and protocol extensions.

protocol Identifiable {
    var id: String { get set }
    func identify()
}

extension Identifiable {
    func identify() {
        print("My ID is \(id).")
    }
}

struct User: Identifiable {
    var id: String
}

let twostraws = User(id: "twostraws")
twostraws.identify()

See this playground and this playground for protocol oriented programming.

Optionals

Unwrapping optionals

if let unwrapped = name {
    print("\(unwrapped.count) letters")
} else {
    print("Missing name.")
}

Unwrapping with guards

func greet(_ name: String?) {
    guard let unwrapped = name else {
        print("You didn't provide a name!")
        return
    }

    print("Hello, \(unwrapped)!")
}

Force unwrap

let str = "5"
let num = Int(str)
let num = Int(str)!
let age: Int! = nil

Nil coalescing

func username(for id: Int) -> String? {
    if id == 1 {
        return "Taylor Swift"
    } else {
        return nil
    }
}
let user = username(for: 15) ?? "Anonymous"