Blog

Swiftのトレーリングクロージャーの書き方

Swiftのトレーリングクロージャーを、URLSessionやmapの例で解説します。引数リストの後にクロージャーを書ける理由と、省略した書き方を確認できます。

Swiftでは、関数の引数リストを閉じた後に、波括弧で囲んだクロージャーが続くことがあります。この書き方をトレーリングクロージャー(Trailing Closure)と呼びます。

引数リストの後に続くクロージャー

URLSessionのdataTaskを使うコードを例に、引数との関係を確認します。

let url:URL = URL(string: "http://127.0.0.1:8080/jsonsample.json")!
let task = URLSession.shared.dataTask(with: url){ data, response, error in
	print(data)

}
task.resume()

このコードは、指定したURLのデータを取得し、完了時にクロージャーで結果を受け取ります。呼び出し部分だけを見ると、次のようになっています。

dataTask(with: url) { data, response, error in

丸括弧の中にはurlしかないため、一見すると引数が一つの関数に見えます。

URLSessionには、次のようにURLだけを引数に取るメソッドもあります。

func dataTask(with url: URL) -> URLSessionDataTask

dataTask(with:) - URLSession | Apple Developer Documentation

ただし、最初の例で呼び出しているのは、URLと完了時のクロージャーを受け取る別のメソッドです。

トレーリングクロージャーの書き方

The Swift Programming Language (Swift 4): Closures の Trailing Closures の章に説明があります。

まず、クロージャーを引数に取る関数を例にします。

func someFunctionThatTakesAClosure(closure: () -> Void) {
    // closureの処理
}

クロージャーを丸括弧の中に書くと、次のようになります。

someFunctionThatTakesAClosure(closure: {
    // closureの処理
})

最後の引数がクロージャーの場合は、丸括弧を閉じた後に書けます。この例では、引数ラベルのclosure:も省略できます。

someFunctionThatTakesAClosure() {
    // closureの処理
}

最初のURLSessionの例でも、丸括弧の後の{...}が最後の引数に当たります。呼び出しているメソッドは、次のdataTask(with:completionHandler:)です。

func dataTask(with url: URL, completionHandler: @escaping (Data?, URLResponse?, Error?) -> Void) -> URLSessionDataTask

dataTask(with:completionHandler:) - URLSession | Apple Developer Documentation

クロージャーを引数リストの中に書く場合

let task = URLSession.shared.dataTask(with: url,completionHandler:{ data, response, error in
	print(data)
})

トレーリングクロージャーを使う場合

let task = URLSession.shared.dataTask(with: url){ data, response, error in
	print(data)
}

ほかの使用例

DispatchQueue.main.asyncや配列のmapでも、同じ記法を使えます。

メインスレッドで処理する場合

// Trailing Closureで書く場合
DispatchQueue.main.async {
	print("main thread proc.")
}

// 引数内に書く場合
DispatchQueue.main.async(execute: {
	print("main thread proc.")
})

配列のmap関数

mapは、配列の各要素を変換するクロージャーを引数に取ります。

 map<T>(_ transform: (Element) throws -> T) rethrows -> [T]
let numbers:[Int] = [1, 2, 3]

// Trailing Closureで書く場合
let res1 = numbers.map { (number) -> Int in
	return number * 10
}
print(res1)

// 引数内に書く場合
let res2 = numbers.map( { (number) -> Int in
	return number * 10
})
print(res2)

// たくさん省略した場合
let res3 = numbers.map {
	$0 * 10
}
print(res3)

結果はどの書き方でも同じです。

[10, 20, 30]
[10, 20, 30]
[10, 20, 30]

参考

The Swift Programming Language - Trailing Closures

← Blog