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]