What does $0 and $1 mean in Swift Closures?

let sortedNumbers = numbers.sort { $0 > $1 }
print(sortedNumbers)

Can anyone explain, what $0 and $1 means in swift?

More Sample

array.forEach {
actions.append($0)
}
71444 次浏览

$0 is the first parameter passed into the closure. $1 is the second parameter, etc. That closure you showed is shorthand for:

let sortedNumbers = numbers.sort { (firstObject, secondObject) in
return firstObject > secondObject
}

The refer to the first and second arguments of sort. Here, sort compares 2 elements and order them. You can look up Swift official documentation for more info:

Swift automatically provides shorthand argument names to inline closures, which can be used to refer to the values of the closure’s arguments by the names $0, $1, $2, and so on.

It is shorthand argument names.

Swift automatically provides shorthand argument names to inline closures, which can be used to refer to the values of the closure’s arguments by the names $0, $1, $2, and so on.

If you use these shorthand argument names within your closure expression, you can omit the closure’s argument list from its definition, and the number and type of the shorthand argument names will be inferred from the expected function type. The in keyword can also be omitted, because the closure expression is made up entirely of its body:

    reversed = names.sort( { $0 > $1 } )

Here, $0 and $1 refer to the closure’s first and second String arguments.

It represents shorthanded arguments sent into a closure, this example breaks it down:

Swift 4:

var add = { (arg1: Int, arg2: Int) -> Int in
return arg1 + arg2
}
add = { (arg1, arg2) -> Int in
return arg1 + arg2
}
add = { arg1, arg2 in
arg1 + arg2
}
add = {
$0 + $1
}


let result = add(20, 20) // 40

TL;DR

Swift 5.7

$0 and $1 are closure’s first and second Shorthand Argument Names (SAN for short) or implicit parameter names, if you like. The shorthand argument names are automatically provided by Swift. The first argument is referenced by $0, the second argument is referenced by $1, the third one by $2, and so on.

As you know, a Closure is a self-contained block of functionality (a function without name) that can be passed around and used in your code. Closure has different names in other programming languages as well as slight differences in meaning – it's Lambda in Python and Kotlin, or it's Block in C and Objective-C.


Shortening a closure

let coffee: [String] = ["Cappuccino", "Espresso", "Latte", "Ristretto"]

1. Regular function

func backward(_ n1: String, _ n2: String) -> Bool {
return n1 > n2
}
var reverseOrder = coffee.sorted(by: backward)




/* RESULT: ["Ristretto", "Latte", "Espresso", "Cappuccino"] */

2. Inline closure expression

reverseOrder = coffee.sorted {
(n1: String, n2: String) -> Bool in return n1 > n2
}

3. Inferring Type from context in a closure with an implicit return

reverseOrder = coffee.sorted { n1, n2 in n1 > n2 }

4. Shorthand Argument Names

reverseOrder = coffee.sorted { $0 > $1 }


/* $0 and $1 are closure’s first and second String arguments. */

5. Operator methods

reverseOrder = coffee.sorted(by: >)


/* RESULT: ["Ristretto", "Latte", "Espresso", "Cappuccino"] */


Higher Order Function with closure

let companies = ["bmw", "kfc", "ibm", "htc"]


let uppercased = companies.map {
(item: String) -> String in return item.uppercased()
}


print(uppercased)


/* RESULT: ["BMW", "KFC", "IBM", "HTC"] */

Shorthand Argument Name $0

let uppercased = companies.map { $0.uppercased() }


print(uppercased)


/* RESULT: ["BMW", "KFC", "IBM", "HTC"] */


Full closure expression with remainder operator (a.k.a. modulo)

let numbers: [Int] = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]


let filteredNumbers = numbers.filter {
(arg: Int) -> Bool in return (arg % 2) == 0
}


print(filteredNumbers)


/* RESULT: [2, 4, 6, 8, 10] */

Shorthand Argument Name with remainder operator

let numbers: [Int] = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]


let filteredNumbers = numbers.filter { ($0 % 2) == 0 }


print(filteredNumbers)


/* RESULT: [2, 4, 6, 8, 10] */


SAN in variadic functions

Variadic functions are ones that accept any number of parameters. One of the most famous variadic methods is print(...). Shorthand Argument Names are perfect for variadic functions.

fileprivate func dessert(_ fruits: String...) -> Bool {


return fruits.contains { $0 == "Apple" }
}


let contains = dessert("Mango", "Durian", "Papaya")


print(contains)


/* RESULT:  false */


$0 for existential Sequence type

Here, allSatisfy() method indicates whether every element of a sequence satisfies a given predicate or not. The Any type represents values of any type. Existential any type is used for storing an any kind of value which conforms to a specific protocol. Also, I used here a shorthand syntax for optional binding (shadowing an existing constant).

let inputData: [Any]? = [1, 2, 3, "Hello"]


func satisfiesOrNot(_ inputData: any Sequence) -> Bool {
inputData.allSatisfy { $0 is Int }
}


if let inputData {
satisfiesOrNot(inputData)
}


/* RESULT:  false */


Repeating $0

let cubedNumber = { $0 * $0 * $0 } (25)


print(cubedNumber)


/* RESULT:  25^3 = 15625 */


$0 in closure capturing

If you use external values inside a closure, Swift captures them.

func trainer(_ said: String) -> (String) -> String {
return {
return "¡\(said) \($0)!"
}
}


let announcement = trainer("Bienvenido a mis cursos")
announcement("RealityKit 2022")


/* RESULT:  "¡Bienvenido a mis cursos RealityKit 2022!"  */

Shorthand Argument Names $0, $1, $2

let math: (Int8, Int8, Int8) -> Int8 = { $0 + $1 - $2 }


func feedClosure() -> (Int8, Int8, Int8) -> Int8 {
return math
}
feedClosure()(10, 20, 100)


/* RESULT:  (10 + 20 - 100) = -70 */


SANs $0, $1, $2, $3, $4

let factorial = { $0 * $1 * $2 * $3 * $4 } (1, 2, 3, 4, 5)


print(factorial)


/* RESULT:  5! = 120 */


Key path expression

Starting from Swift 5.2+ you can access parameters of every instance via key path expression:

struct Lighter {
let manufacturer: String
let refillable: Bool
}


let zippo = Lighter(manufacturer: "Zippo", refillable: true)
let cricket = Lighter(manufacturer: "Cricket", refillable: false)


let lighters: [Lighter] = [zippo, cricket]


let refillableOnes = lighters.map(\.refillable)


print(refillableOnes)


/* RESULT:  [true, false] */

Of course, you can alternatively use a familiar syntax:

Regular syntax – $0.property:

let refillableOnes = lighters.map { $0.refillable }


print(refillableOnes)


/* RESULT:  [true, false] */


SAN with a subscript

(If you want to know how to implement negative indexing in arrays, read this post please.)

let arrays: [[String]] = [["Hello","Hola","你好"], ["world","mundo","世界"]]


let helloWorld = arrays.compactMap { $0[0] }


print(helloWorld)


/* RESULT:  ["Hello", "world"] */

One more example with key shortcut (Swift 5.6) and a subscript:

let dictionaries: [[_ : Any?]] = [[1: "x"], [2: nil], [3: true]]


let values = dictionaries.compactMap { $0[$0.startIndex].value }


print(values)


/* RESULT:  ["x", true] */

Or look at the example of unordered set:

let collection: Set<String> = ["One", "", "Three"]


collection.map {
switch $0.isEmpty {
case true:
print("Empty")
case false:
print("Element \($0) isn't empty")
}
}


/*   RESULT:   "Element Three isn't empty"  */
/*             "Empty"                      */
/*             "Element One isn't empty"    */


SAN in a completion handler

let completionHandler: ((Bool) -> Void)? = {
if $0 {
print("It is true, sister...")
} else {
print("False")
}
}
completionHandler?(true)


/* RESULT:  It is true, sister... */

Regular syntax, however, is as following:

let completionHandler: ((Bool) -> Void)? = { sayTheTruth in
if sayTheTruth {
print("It is true, sister...")
} else {
print("False")
}
}
completionHandler?(false)


/* RESULT:  False */


SAN in ForEach structure in SwiftUI

let columns: [GridItem] = Array(repeating: .init(.fixed(70)), count: 5)


var body: some View {
ScrollView {
LazyVGrid(columns: columns) {
ForEach((1...10), id: \.self) {


Text("\($0)").frame(maxWidth: .infinity)
}
}
}
}


/*   RESULT:   1  2  3  4  5   */
/*             6  7  8  9  10  */


Operator method vs SAN

Operator Method:

let records: [Int] = [110, 108, 107, 109, 108]


public func averageSpeed(records: [Int]) throws -> Int {
let average = records.reduce(0, +) / records.count
return average
}
try averageSpeed(records: records)


/* RESULT:  108 */

Shorthand Argument Names $0 and $1:

public func averageSpeed(records: [Int]) throws -> Int {
let average = records.reduce(0) { $0 + $1 } / records.count
return average
}
try averageSpeed(records: records)


/* RESULT:  108 */


$0 as the resulting value

.onChanged and .onEnded modifiers perform an @escaping closure when a SwiftUI gesture changed or ended.

@State private var rotate: Angle = .zero


var myGesture: some Gesture {
RotationGesture()
.onChanged { rotate = $0 }
.onEnded { angle in rotate = angle }
}


var body: some View {
Rectangle()
.rotationEffect(rotate)
.gesture(myGesture)
}


Swift vs Kotlin vs Python

Also, let's see how Kotlin's lambda is similar to Swift's closure:

Swift

let element: [String] = ["Argentum","Aurum","Platinum"]


let characterCount = element.map { $0.count }


print(characterCount)


/* RESULT:  [8, 5, 8] */

Kotlin

Kotlin's lambda expression has just one parameter with implicit name: it. In other words, if you have a function literal with exactly one parameter you don’t need to define that parameter explicitly, you can use it instead (like $0 in Swift).

val element = listOf("Argentum","Aurum","Platinum")


val characterCount = element.map { it.length }


println(characterCount)


/* RESULT:  [8, 5, 8] */

But in Python there's no equivalent of Shorthand Argument Name.

Python

element = ["Argentum","Aurum","Platinum"]


characterCount = list(map(lambda x: len(x), element))


print(characterCount)


# RESULT:  [8, 5, 8]

In Addition with @Bobby's Answer I would like to Add an Example

var add: (Int,Int,Int)->Int
add = {
//So here the $0 is first argument $1 is second argument $2 is third argument
return $0 + $1 + $2
//The above statement can also be written as $0 + $1 + $2 i.e is return is optional
}


let result = add(20, 30, 40)
print(result) // Prints 90

$0, $1, and so on represent items of a collection when performing actions on them.

For instance, to print a list of numbers, you can use the built-in forEach() function and $0 like this:

let numbers = [1, 2, 3, 4, 5]
numbers.forEach { print($0) }

Here the $0 represents each number in the array at a time.

🛑 $0’s and $1’s in Swift

Before explaining what $0’s and $1’s are, let’s see an example.

Say you have an array of numbers, and you want to print them. You can do it with a for loop:

let numbers = [1, 2, 3, 4, 5]
for number in numbers {
print(number)
}

But, there is another alternative using forEach with a $0:

numbers.forEach { print($0) }

Let’s inspect the code to see what it does:

  • It takes the numbers array and calls forEach to run code for each number element in the array.
  • The forEach method accepts a closure that takes a number argument. In this case, the closure is { print($0) }. This closure is called for each number in the array one by one.
  • On each call, $0 represents a different number argument from the numbers array.

🛑 A More Detailed Explanation

Let’s create a closure and assign it to a variable called printer. This closure takes a number as its argument and prints it out:

var printer = { (n: Int) -> Void in
print(n)
}
// Test calling printer closure on a number 3 for example:
printer(3) // prints 3 - it works
// Now, use printer to print numbers (from the previous example):
numbers.forEach(printer) // prints 1, 2, 3, 4, 5

You can simplify this closure: In the end, you use this closure to print an array of integers. Thus, Swift can automatically infer the argument type (integer) without explicitly specifying it. Thus, printer can be simplified to:

printer = { n in
print(n)
}
// Test: printing numbers still works:
numbers.forEach(printer) // prints 1, 2, 3, 4, 5

Now, here is where the $0’s come in: In Swift, it is also possible to omit the parameter name(s) in closures. When doing this, you can refer to the omitted arguments with $0, $1, $2. Let’s simplify the printer closure by omitting the number parameter:

printer = { print($0) }
// forEach accepts a closure:
numbers.forEach(printer) // prints 1, 2, 3, 4, 5

Finally, nothing forces you to assign the printer to a variable called printer. You can use the closure anonymously to achieve the same behavior:

numbers.forEach { print($0) } // prints 1, 2, 3, 4, 5

This is the line you saw earlier.

Create an array where all the numbers in the numbers array are raised to the second power using map method.

let numbers = [1, 2, 3, 4, 5]
let pow_nums = numbers.map { $0 * $0 }
pow_nums.forEach { print($0) } // Prints 1, 4, 9, 16, 25

Code Happy ;)