🙆‍♀️

【DAY9】100 Days of SwiftUI -closures, passing functions into functions

2023/01/05に公開

はじめに

iOSアプリ界隈で(たぶん)有名なPaul Hudsonさんが無料で公開しているSwiftUIの100日学習コースを進めてみます。学習記録及び備忘録がてらにつらつらと書いてみます。
100 Days of SwiftUI

学んだこと

序盤はSwiftUIは関係なくSwiftという言語の言語仕様のお話。このあたりから少し難しくなってくる。

クロージャ

処理自体を変数(もしくは定数)かのように扱うことをクロージャという。最も簡単な例は以下。

let sayHello = {
    print("Hi there!")
}

sayHello()

関数の型を定義する場合はinで型と処理を区切って記述する。

let sayHello = { (name: String) -> String in
    "Hi \(name)!"
}

ただし、以下のように関数のコピーを行った場合、関数の型に外部パラメータforが含まれないため、コピーを呼び出すときはdata(for: 1989)ではなく、data(1989)となる。

func getUserData(for id: Int) -> String {
    if id == 1989 {
        return "Taylor Swift"
    } else {
        return "Anonymous"
    }
}

let data: (Int) -> String = getUserData
let user = data(1989) // コピーを呼び出すとき元の関数に従ってdata(for: 1989)と書くとエラーになる
print(user)

例えばソートを行うsorted()は引数にクロージャを指定することができる。パラメータbyについて(strig, string) -> Boolのクロージャを指定するとソートの制約等を設定できる。以下の例ではチームメンバーのソートを行う際にキャプテンは最初に置くという制約を設定している。

let team = ["Gloria", "Suzanne", "Piper", "Tiffany", "Tasha"]

let captainFirstTeam = team.sorted(by: { (name1: String, name2: String) -> Bool in
    if name1 == "Suzanne" {
        return true
    } else if name2 == "Suzanne" {
        return false
    }

    return name1 < name2
})

print(captainFirstTeam)
// 出力: ["Suzanne", "Gloria", "Piper", "Tasha", "Tiffany"]
// 通常のsorted()では["Gloria", "Piper", "Suzanne", "Tasha", "Tiffany"]

トレイリングクロージャ

ある関数の末尾の引数が関数であるとき、Swiftではトレイリングクロージャという特別な構文を使うことができる。トレイリング(trailing)は末尾を意味する。発想としては引数に入れる関数の型は確定してるから書く必要なくない?といったところ。先ほどの例をトレイリングクロージャを使って書くと以下のようになる。

let captainFirstTeam = team.sorted { name1, name2 in
    if name1 == captain {
        return true
    } else if name2 == captain {
        return false
    } else {
        return name1 < name2
    }
}

あるいはクロージャの引数は明記せず単に$0, $1のように書くこともできる。可読性が損なわれない範囲であれば使うのはあり。

let captainFirstTeam = team.sorted {
    if $0 == "Suzanne" {
        return true
    } else if $1 == "Suzanne" {
        return false
    }

    return $0 < $1
}

別の例

// 配列の中からTがつく要素をフィルタリングして新たな配列をつくる
let tOnly = team.filter { $0.hasPrefix("T")}

// 配列の各要素を大文字(uppercase)に変換する
let uppercaseTeam = tOnly.map{ $0.uppercased() }

Discussion