Functions

Classic Functions

func greeting(for person: String) -> String {
    "Hello, " + person + "!"
}
print(greeting(for: "Dave"))
// Prints "Hello, Dave!"


func anotherGreeting(for person: String) -> String {
    return "Hello, " + person + "!"
}
print(anotherGreeting(for: "Dave"))
// Prints "Hello, Dave!"
func greet(person: String) -> String {
    let greeting = "Hello, " + person + "!"
    return greeting
}
print(greet("Dave"))
// Prints "Hello, Dave!"
func greet(person: String) {
    print("Hello, \(person)!")
}
greet(person: "Dave")
// Prints "Hello, Dave!"

Mutability: inout

  • Function parameters are constants by default. Trying to change the value of a function parameter from within the body of that function results in a compile-time error.

  • If you want a function to modify a parameter’s value, and you want those changes to persist after the function call has ended, define that parameter as an in-out parameter  instead.

func swapTwoInts(_ a: inout Int, _ b: inout Int) {
    let temporaryA = a
    a = b
    b = temporaryA
}

var someInt = 3
var anotherInt = 107
swapTwoInts(&someInt, &anotherInt)
print("someInt is now \(someInt), and anotherInt is now \(anotherInt)")
// Prints "someInt is now 107, and anotherInt is now 3"
  • Note :

    • In-out parameters can’t have default values, and variadic parameters can’t be marked as inout .

Multiple returns

func minMax(array: [Int]) -> (min: Int, max: Int) {
    var currentMin = array[0]
    var currentMax = array[0]
    for value in array[1..<array.count] {
        if value < currentMin {
            currentMin = value
        } else if value > currentMax {
            currentMax = value
        }
    }
    return (currentMin, currentMax)
}

let bounds = minMax(array: [8, -6, 2, 109, 3, 71])
print("min is \(bounds.min) and max is \(bounds.max)")
// Prints "min is -6 and max is 109"
  • Can be optional:

    func minMax(array: [Int]) -> (min: Int, max: Int)? {
        if array.isEmpty { return nil }
        var currentMin = array[0]
        var currentMax = array[0]
        for value in array[1..<array.count] {
            if value < currentMin {
                currentMin = value
            } else if value > currentMax {
                currentMax = value
            }
        }
        return (currentMin, currentMax)
    }
    

Ignoring the return

  • The return value of a function can be ignored when it’s called:

func printAndCount(string: String) -> Int {
    print(string)
    return string.count
}

let _ = printAndCount(string: "Caio")

Parameters

Order
  • Using _  in a function allows positional arguments, as in whatsUp('jeff', emoji: 'foguinho') .

Default
func someFunction(parameterWithoutDefault: Int, parameterWithDefault: Int = 12) {
    // If you omit the second argument when calling this function, then
    // the value of parameterWithDefault is 12 inside the function body.
}
someFunction(parameterWithoutDefault: 3, parameterWithDefault: 6) // parameterWithDefault is 6
someFunction(parameterWithoutDefault: 4) // parameterWithDefault is 12
Labels
  • If a parameter has an argument label, the argument must  be labeled when you call the function.

func greeting(for person: String) -> String {
    "Hello, " + person + "!"
}
print(greeting(for: "Dave"))
// Prints "Hello, Dave!"


func anotherGreeting(for person: String) -> String {
    return "Hello, " + person + "!"
}
print(anotherGreeting(for: "Dave"))
// Prints "Hello, Dave!"
  • Omitting the label:

    func someFunction(_ firstParameterName: Int, secondParameterName: Int) {
        // In the function body, firstParameterName and secondParameterName
        // refer to the argument values for the first and second parameters.
    }
    someFunction(1, secondParameterName: 2)
    
Variadic
func arithmeticMean(_ numbers: Double...) -> Double {
    var total: Double = 0
    for number in numbers {
        total += number
    }
    return total / Double(numbers.count)
}
arithmeticMean(1, 2, 3, 4, 5)
// returns 3.0, which is the arithmetic mean of these five numbers
arithmeticMean(3, 8.25, 18.75)
// returns 10.0, which is the arithmetic mean of these three numbers

Function as Arguments / Function Types

  • Functions are 'first-class objects'.

  • " (Int, Int) -> Int ":

    func addTwoInts(_ a: Int, _ b: Int) -> Int {
        return a + b
    }
    
    let anotherMathFunction = addTwoInts
    // anotherMathFunction is inferred to be of type (Int, Int) -> Int
    
  • " () -> Void ":

    func printHelloWorld() {
        print("hello, world")
    }
    
  • Ex1 :

    var mathFunction: (Int, Int) -> Int = addTwoInts
    
    print("Result: \(mathFunction(2, 3))")
    // Prints "Result: 5"
    
    func printMathResult(_ mathFunction: (Int, Int) -> Int, _ a: Int, _ b: Int) {
        print("Result: \(mathFunction(a, b))")
    }
    printMathResult(addTwoInts, 3, 5)
    // Prints "Result: 8"
    
  • Ex2 :

    func stepForward(_ input: Int) -> Int {
        return input + 1
    }
    func stepBackward(_ input: Int) -> Int {
        return input - 1
    }
    
    func chooseStepFunction(backward: Bool) -> (Int) -> Int {
        return backward ? stepBackward : stepForward
    }
    
    var currentValue = 3
    let moveNearerToZero = chooseStepFunction(backward: currentValue > 0)
    // moveNearerToZero now refers to the stepBackward() function
    
  • Ex3 :

    func backward(_ s1: String, _ s2: String) -> Bool {
        return s1 > s2
    }
    var reversedNames = names.sorted(by: backward)
    // reversedNames is equal to ["Ewa", "Daniella", "Chris", "Barry", "Alex"]
    
Nested Functions
  • Nested functions are hidden from the outside world by default.

func chooseStepFunction(backward: Bool) -> (Int) -> Int {
    func stepForward(input: Int) -> Int { return input + 1 }
    func stepBackward(input: Int) -> Int { return input - 1 }
    return backward ? stepBackward : stepForward
}
  • Can be returned as a parameter to be accessed outside the function.

Closures

Ways to Write
reversedNames = names.sorted(by: { (s1: String, s2: String) -> Bool in
    return s1 > s2
})
reversedNames = names.sorted(by: { (s1: String, s2: String) -> Bool in return s1 > s2 } )
reversedNames = names.sorted(by: { s1, s2 in return s1 > s2 } )
reversedNames = names.sorted(by: { s1, s2 in s1 > s2 } )
reversedNames = names.sorted(by: { $0 > $1 } )  // shorthanded
reversedNames = names.sorted(by: >)  // Using the default implementation of the `>` symbol
When the Closure is Called
  • An autoclosure  is a closure that’s automatically created to wrap an expression that’s being passed as an argument to a function.

  • It doesn’t take any arguments, and when it’s called, it returns the value of the expression that’s wrapped inside of it.

  • An autoclosure lets you delay evaluation because the code inside isn’t run until you call the closure.

var customersInLine = ["Chris", "Alex", "Ewa", "Barry", "Daniella"]
print(customersInLine.count)
// Prints "5"


let customerProvider = { customersInLine.remove(at: 0) }
print(customersInLine.count)
// Prints "5"


print("Now serving \(customerProvider())!")
// Prints "Now serving Chris!"
print(customersInLine.count)
// Prints "4"
// customersInLine is ["Alex", "Ewa", "Barry", "Daniella"]
func serve(customer customerProvider: () -> String) {
    print("Now serving \(customerProvider())!")
}
serve(customer: { customersInLine.remove(at: 0) } )
// Prints "Now serving Alex!"
Shorthand
reversedNames = names.sorted(by: { $0 > $1 } )
  • Here, $0  and $1  refer to the closure’s first and second String  arguments. Because $1  is the shorthand argument with the highest number, the closure is understood to take two arguments. Because the sorted(by:)  function here expects a closure whose arguments are both strings, the shorthand arguments $0  and $1  are both of type String .

Ways to Call (Trailing Closures)
  • You write a trailing closure after the function call’s parentheses, even though the trailing closure is still an argument to the function.

  • When you use the trailing closure syntax, you don’t write the argument label for the first closure as part of the function call.

  • A function call can include multiple trailing closures; however, the first few examples below use a single trailing closure.

reversedNames = names.sorted(by: { $0 > $1 } )
reversedNames = names.sorted() { $0 > $1 }
  • If a closure expression is provided as the function’s or method’s only argument and you provide that expression as a trailing closure, you don’t need to write a pair of parentheses ()  after the function or method’s name when you call the function:

    reversedNames = names.sorted { $0 > $1 }
    
  • Ex1 :

    let digitNames = [
        0: "Zero", 1: "One", 2: "Two",   3: "Three", 4: "Four",
        5: "Five", 6: "Six", 7: "Seven", 8: "Eight", 9: "Nine"
    ]
    let numbers = [16, 58, 510]
    
    
    let strings = numbers.map { (number) -> String in
        var number = number
        var output = ""
        repeat {
            output = digitNames[number % 10]! + output
            number /= 10
        } while number > 0
        return output
    }
    // strings is inferred to be of type [String]
    // its value is ["OneSix", "FiveEight", "FiveOneZero"]
    
  • Ex2  (multiple closures):

    func loadPicture(from server: Server, completion: (Picture) -> Void, onFailure: () -> Void) {
        if let picture = download("photo.jpg", from: server) {
            completion(picture)
        } else {
            onFailure()
        }
    }
    
    loadPicture(from: someServer) { picture in
        someView.currentPicture = picture
    } onFailure: {
        print("Couldn't download the next picture.")
    }