When I first saw the Swift guard statement during Apple’s Platform State of the Union, I couldn’t quite understand why I would ever use it. So what is it? The tl;dr is as follows:
Like an if statement, guard executes statements based on a Boolean value of an expression. Unlike an if statement, guard statements only run if the conditions are not met. You can think of guard more like an Assert, but rather than crashing, you can gracefully exit.
Even after seeing some examples, I only saw it as a confusing way to accomplish what we already could with Optional Binding or with if-else statements alone.
It wasn’t until I started discussing it over this Twitter conversation that I realized there are actually some interesting benefits of using such syntax.
Diving In
Let’s take a simple example comparing current techniques vs using the new guard statement:
func fooManualCheck(x: Int?) { if x == nil || x <= 0 { // Value requirements not met, do something return }
// Do stuff with x x!.description }
This is the most basic Objective-C style way to make sure a value exists and that it meets a condition. Now this works fine, but has a couple flaws:
You’re checking for a condition that you don’t want, rather than checking for the value you do want. Code becomes confusing when you have a bunch of checks like this. What you’re hoping for here is that your condition actually doesn’t pass.
You also need to force unwrap the optional value after the condition fails.
Swift gave us a way to clean this up and fix some of these flaws through Optional Binding:
func fooBinding(x: Int?) { if let x = x where x > 0 { // Do stuff with x x.description }
// Value requirements not met, do something }
This removes both of the flaws that the first function had, but adds a new one. Here you’re putting your desired code within all the conditions, rather than afterward. You might not immediately see a problem with this, but you could imagine how confusing it could become if it was nested with numerous conditions that all needed to be met before running your statements.
The way to clean this up is to do each of your checks first, and exit if any aren’t met. This allows easy understanding of what conditions will make this function exit.
I’ve heard this called the Bouncer Pattern, which makes a lot of sense. You want to get rid of the bad cases before they get in the door. It also allows you to think about one case at a time, rather than figuring out how all combinations need to work together.
Here enters the guard statement:
func fooGuard(x: Int?) { guard let x = x where x > 0 else { // Value requirements not met, do something return }
// Do stuff with x x.description }
Using guard solves all 3 of the issues mentioned above:
Checking for the condition you do want, not the one you don’t. This again is similar to an assert. If the condition is not met, guard‘s else statement is run, which breaks out of the function.
If the condition passes, the optional variable here is automatically unwrapped for you within the scope that the guard statement was called – in this case, the fooGuard(_:) function. This is an important, yet notably strange feature that really makes the guard statement useful.
You are checking for bad cases early, making your function more readable and easier to maintain.
The cool thing is that this same pattern holds true for non-optional values as well:
func fooNonOptionalGood(x: Int) { guard x > 0 else { // Value requirements not met, do something return }
// Do stuff with x }
func fooNonOptionalBad(x: Int) { if x <= 0 { // Value requirements not met, do something return }
// Do stuff with x }
Wrapping Up
I hope this simple example shows how you could start using guard immediately in your Swift code to make your function/methods more clear. It’s easy for us to immediately judge the value of a new feature until you give it a chance and see what it can or can’t do for you.
Going from Objective-C to Swift is a huge change, not only to syntax, but how you have to look at architecting your code. You can only benefit from this awesome new language if you actively change your mindset while writing code by expanding your everyday patterns and style.
If you find anything else interesting let me know, this syntax is brand new to me too.
This is called Optional Binding. Quoting from the Basics -> Optionals -> Optional Binding section of the Swift Language Guide.
You use optional binding to find out whether an optional contains a value, and if so, to make that value available as a temporary constant or variable. Optional binding can be used with if and while statements to check for a value inside an optional, and to extract that value into a constant or variable, as part of a single action.
What this if let construct is doing is checking if someOptional is exists. If it isn’t nil/.Nonethen it “binds” the Optional value to a constant.
if let constantName = someOptional {
...
}
This can be thought of as:
if someOptional != nil {
let constantName = someOptional!
....
}
When I first saw the Swift guard statement during Apple’s Platform State of the Union, I couldn’t quite understand why I would ever use it. So what is it? The tl;dr is as follows:
Like an if statement, guard executes statements based on a Boolean value of an expression. Unlike an if statement, guard statements only run if the conditions are not met. You can think of guard more like an Assert, but rather than crashing, you can gracefully exit.
Even after seeing some examples, I only saw it as a confusing way to accomplish what we already could with Optional Binding or with if-else statements alone.
It wasn’t until I started discussing it over this Twitter conversation that I realized there are actually some interesting benefits of using such syntax.
Diving In
Let’s take a simple example comparing current techniques vs using the new guard statement:
func fooManualCheck(x: Int?) { if x == nil || x <= 0 { // Value requirements not met, do something return }
// Do stuff with x x!.description }
This is the most basic Objective-C style way to make sure a value exists and that it meets a condition. Now this works fine, but has a couple flaws:
You’re checking for a condition that you don’t want, rather than checking for the value you do want. Code becomes confusing when you have a bunch of checks like this. What you’re hoping for here is that your condition actually doesn’t pass.
You also need to force unwrap the optional value after the condition fails.
Swift gave us a way to clean this up and fix some of these flaws through Optional Binding:
func fooBinding(x: Int?) { if let x = x where x > 0 { // Do stuff with x x.description }
// Value requirements not met, do something }
This removes both of the flaws that the first function had, but adds a new one. Here you’re putting your desired code within all the conditions, rather than afterward. You might not immediately see a problem with this, but you could imagine how confusing it could become if it was nested with numerous conditions that all needed to be met before running your statements.
The way to clean this up is to do each of your checks first, and exit if any aren’t met. This allows easy understanding of what conditions will make this function exit.
I’ve heard this called the Bouncer Pattern, which makes a lot of sense. You want to get rid of the bad cases before they get in the door. It also allows you to think about one case at a time, rather than figuring out how all combinations need to work together.
Here enters the guard statement:
func fooGuard(x: Int?) { guard let x = x where x > 0 else { // Value requirements not met, do something return }
// Do stuff with x x.description }
Using guard solves all 3 of the issues mentioned above:
Checking for the condition you do want, not the one you don’t. This again is similar to an assert. If the condition is not met, guard‘s else statement is run, which breaks out of the function.
If the condition passes, the optional variable here is automatically unwrapped for you within the scope that the guard statement was called – in this case, the fooGuard(_:) function. This is an important, yet notably strange feature that really makes the guard statement useful.
You are checking for bad cases early, making your function more readable and easier to maintain.
The cool thing is that this same pattern holds true for non-optional values as well:
func fooNonOptionalGood(x: Int) { guard x > 0 else { // Value requirements not met, do something return }
// Do stuff with x }
func fooNonOptionalBad(x: Int) { if x <= 0 { // Value requirements not met, do something return }
// Do stuff with x }
Wrapping Up
I hope this simple example shows how you could start using guard immediately in your Swift code to make your function/methods more clear. It’s easy for us to immediately judge the value of a new feature until you give it a chance and see what it can or can’t do for you.
Going from Objective-C to Swift is a huge change, not only to syntax, but how you have to look at architecting your code. You can only benefit from this awesome new language if you actively change your mindset while writing code by expanding your everyday patterns and style.
If you find anything else interesting let me know, this syntax is brand new to me too.
This is called Optional Binding. Quoting from the Basics -> Optionals -> Optional Binding section of the Swift Language Guide.
You use optional binding to find out whether an optional contains a value, and if so, to make that value available as a temporary constant or variable. Optional binding can be used with if and while statements to check for a value inside an optional, and to extract that value into a constant or variable, as part of a single action.
What this if let construct is doing is checking if someOptional is exists. If it isn’t nil/.Nonethen it “binds” the Optional value to a constant.
if let constantName = someOptional {
...
}
This can be thought of as:
if someOptional != nil {
let constantName = someOptional!
....
}
When I first saw the Swift guard statement during Apple’s Platform State of the Union, I couldn’t quite understand why I would ever use it. So what is it? The tl;dr is as follows:
Like an if statement, guard executes statements based on a Boolean value of an expression. Unlike an if statement, guard statements only run if the conditions are not met. You can think of guard more like an Assert, but rather than crashing, you can gracefully exit.
Even after seeing some examples, I only saw it as a confusing way to accomplish what we already could with Optional Binding or with if-else statements alone.
It wasn’t until I started discussing it over this Twitter conversation that I realized there are actually some interesting benefits of using such syntax.
Diving In
Let’s take a simple example comparing current techniques vs using the new guard statement:
func fooManualCheck(x: Int?) { if x == nil || x <= 0 { // Value requirements not met, do something return }
// Do stuff with x x!.description }
This is the most basic Objective-C style way to make sure a value exists and that it meets a condition. Now this works fine, but has a couple flaws:
You’re checking for a condition that you don’t want, rather than checking for the value you do want. Code becomes confusing when you have a bunch of checks like this. What you’re hoping for here is that your condition actually doesn’t pass.
You also need to force unwrap the optional value after the condition fails.
Swift gave us a way to clean this up and fix some of these flaws through Optional Binding:
func fooBinding(x: Int?) { if let x = x where x > 0 { // Do stuff with x x.description }
// Value requirements not met, do something }
This removes both of the flaws that the first function had, but adds a new one. Here you’re putting your desired code within all the conditions, rather than afterward. You might not immediately see a problem with this, but you could imagine how confusing it could become if it was nested with numerous conditions that all needed to be met before running your statements.
The way to clean this up is to do each of your checks first, and exit if any aren’t met. This allows easy understanding of what conditions will make this function exit.
I’ve heard this called the Bouncer Pattern, which makes a lot of sense. You want to get rid of the bad cases before they get in the door. It also allows you to think about one case at a time, rather than figuring out how all combinations need to work together.
Here enters the guard statement:
func fooGuard(x: Int?) { guard let x = x where x > 0 else { // Value requirements not met, do something return }
// Do stuff with x x.description }
Using guard solves all 3 of the issues mentioned above:
Checking for the condition you do want, not the one you don’t. This again is similar to an assert. If the condition is not met, guard‘s else statement is run, which breaks out of the function.
If the condition passes, the optional variable here is automatically unwrapped for you within the scope that the guard statement was called – in this case, the fooGuard(_:) function. This is an important, yet notably strange feature that really makes the guard statement useful.
You are checking for bad cases early, making your function more readable and easier to maintain.
The cool thing is that this same pattern holds true for non-optional values as well:
func fooNonOptionalGood(x: Int) { guard x > 0 else { // Value requirements not met, do something return }
// Do stuff with x }
func fooNonOptionalBad(x: Int) { if x <= 0 { // Value requirements not met, do something return }
// Do stuff with x }
Wrapping Up
I hope this simple example shows how you could start using guard immediately in your Swift code to make your function/methods more clear. It’s easy for us to immediately judge the value of a new feature until you give it a chance and see what it can or can’t do for you.
Going from Objective-C to Swift is a huge change, not only to syntax, but how you have to look at architecting your code. You can only benefit from this awesome new language if you actively change your mindset while writing code by expanding your everyday patterns and style.
If you find anything else interesting let me know, this syntax is brand new to me too.
This is called Optional Binding. Quoting from the Basics -> Optionals -> Optional Binding section of the Swift Language Guide.
You use optional binding to find out whether an optional contains a value, and if so, to make that value available as a temporary constant or variable. Optional binding can be used with if and while statements to check for a value inside an optional, and to extract that value into a constant or variable, as part of a single action.
What this if let construct is doing is checking if someOptional is exists. If it isn’t nil/.Nonethen it “binds” the Optional value to a constant.
if let constantName = someOptional {
...
}
This can be thought of as:
if someOptional != nil {
let constantName = someOptional!
....
}
You use the for–in loop to iterate over a sequence, such as items in an array, ranges of numbers, or characters in a string.
let names = [“Anna”, “Alex”, “Brian”, “Jack”] for name in names { print(“Hello, (name)!”) } // Hello, Anna! // Hello, Alex! // Hello, Brian! // Hello, Jack!
You can also iterate over a dictionary to access its key-value pairs. Each item in the dictionary is returned as a (key, value) tuple when the dictionary is iterated, and you can decompose the (key, value) tuple’s members as explicitly named constants for use within the body of the for–in loop. In the code example below, the dictionary’s keys are decomposed into a constant called animalName, and the dictionary’s values are decomposed into a constant called legCount.
let numberOfLegs = [“spider”: 8, “ant”: 6, “cat”: 4] for (animalName, legCount) in numberOfLegs { print(“(animalName)s have (legCount) legs”) } // ants have 6 legs // spiders have 8 legs // cats have 4 legs
for index in 1…5 { print(“(index) times 5 is (index * 5)”) } // 1 times 5 is 5 // 2 times 5 is 10 // 3 times 5 is 15 // 4 times 5 is 20 // 5 times 5 is 25
If you don’t need each value from a sequence, you can ignore the values by using an underscore in place of a variable name.
let base = 3 let power = 10 var answer = 1 for _ in 1…power { answer *= base } print(“(base) to the power of (power) is (answer)”) // Prints “3 to the power of 10 is 59049”
let minutes = 60 for tickMark in 0..<minutes { // render the tick mark each minute (60 times) }
stride(from:to:by:) function
let minuteInterval = 5 for tickMark in stride(from: 0, to: minutes, by: minuteInterval) { // render the tick mark every 5 minutes (0, 5, 10, 15 … 45, 50, 55) }
Closed ranges are also available, by using stride(from:through:by:) instead:
let hours = 12 let hourInterval = 3 for tickMark in stride(from: 3, through: hours, by: hourInterval) { // render the tick mark every 3 hours (3, 6, 9, 12) }
While Loops
while evaluates its condition at the start of each pass through the loop.
repeat–while evaluates its condition at the end of each pass through the loop.
While
A while loop starts by evaluating a single condition. If the condition is true, a set of statements is repeated until the condition becomes false.
while square < finalSquare { // roll the dice
}
Repeat-While
The other variation of the while loop, known as the repeat–while loop, performs a single pass through the loop block first, before considering the loop’s condition. It then continues to repeat the loop until the condition is false.
repeat { // move up or down for a snake or ladder
} while square < finalSquare
Conditional Statements
If
if temperatureInFahrenheit <= 32 { print(“It’s very cold. Consider wearing a scarf.”) } else if temperatureInFahrenheit >= 86 { print(“It’s really warm. Don’t forget to wear sunscreen.”) } else { print(“It’s not that cold. Wear a t-shirt.”) } // Prints “It’s really warm. Don’t forget to wear sunscreen.”
Switch
let someCharacter: Character = “z” switch someCharacter { case “a”: print(“The first letter of the alphabet”) case “z”: print(“The last letter of the alphabet”) default: print(“Some other character”) } // Prints “The last letter of the alphabet”
No Implicit Fallthrough
In contrast with switch statements in C and Objective-C, switch statements in Swift do not fall through the bottom of each case and into the next one by default. Instead, the entire switch statement finishes its execution as soon as the first matching switch case is completed, without requiring an explicit breakstatement.
NOTE
Although break is not required in Swift, you can use a break statement to match and ignore a particular case or to break out of a matched case before that case has completed its execution.
The body of each case must contain at least one executable statement. It is not valid to write the following code, because the first case is empty:
let anotherCharacter: Character = “a” switch anotherCharacter { case “a”: // Invalid, the case has an empty body case “A”: print(“The letter A”) default: print(“Not the letter A”) } // This will report a compile-time error.
To make a switch with a single case that matches both "a" and "A", combine the two values into a compound case, separating the values with commas.
let anotherCharacter: Character = “a” switch anotherCharacter { case “a”, “A”: print(“The letter A”) default: print(“Not the letter A”) } // Prints “The letter A”
Interval Matching
let approximateCount = 62 let countedThings = “moons orbiting Saturn” let naturalCount: String switch approximateCount { case 0: naturalCount = “no” case 1..<5: naturalCount = “a few” case 5..<12: naturalCount = “several” case 12..<100: naturalCount = “dozens of” case 100..<1000: naturalCount = “hundreds of” default: naturalCount = “many” } print(“There are (naturalCount) (countedThings).”) // Prints “There are dozens of moons orbiting Saturn.”
Tuples
You can use tuples to test multiple values in the same switch statement. use the underscore character (_), also known as the wildcard pattern, to match any possible value.
let somePoint = (1, 1) switch somePoint { case (0, 0): print(“(somePoint) is at the origin”) case (_, 0): print(“(somePoint) is on the x-axis”) case (0, _): print(“(somePoint) is on the y-axis”) case (-2…2, -2…2): print(“(somePoint) is inside the box”) default: print(“(somePoint) is outside of the box”) } // Prints “(1, 1) is inside the box”
n fact, the point (0, 0) could match all four of the cases in this example. However, if multiple matches are possible, the first matching case is always used. The point (0, 0) would match case (0, 0) first, and so all other matching cases would be ignored.
Value Bindings
let anotherPoint = (2, 0) switch anotherPoint { case (let x, 0): print(“on the x-axis with an x value of (x)”) case (0, let y): print(“on the y-axis with a y value of (y)”) case let (x, y): print(“somewhere else at ((x), (y))”) } // Prints “on the x-axis with an x value of 2”
After the temporary constants are declared, they can be used within the case’s code block.
This switch statement does not have a default case. The final case, case let (x, y), declares a tuple of two placeholder constants that can match any value. Because anotherPoint is always a tuple of two values, this case matches all possible remaining values, and a default case is not needed to make the switch statement exhaustive.
Where
A switch case can use a where clause to check for additional conditions.
let yetAnotherPoint = (1, -1) switch yetAnotherPoint { case let (x, y) where x == y: print(“((x), (y)) is on the line x == y”) case let (x, y) where x == -y: print(“((x), (y)) is on the line x == -y”) case let (x, y): print(“((x), (y)) is just some arbitrary point”) } // Prints “(1, -1) is on the line x == -y”
Compound Cases
Multiple switch cases that share the same body can be combined by writing several patterns after case, with a comma between each of the patterns.
let someCharacter: Character = “e” switch someCharacter { case “a”, “e”, “i”, “o”, “u”: print(“(someCharacter) is a vowel”) case “b”, “c”, “d”, “f”, “g”, “h”, “j”, “k”, “l”, “m”, “n”, “p”, “q”, “r”, “s”, “t”, “v”, “w”, “x”, “y”, “z”: print(“(someCharacter) is a consonant”) default: print(“(someCharacter) is not a vowel or a consonant”) } // Prints “e is a vowel”
let stillAnotherPoint = (9, 0) switch stillAnotherPoint { case (let distance, 0), (0, let distance): print(“On an axis, (distance) from the origin”) default: print(“Not on an axis”) } // Prints “On an axis, 9 from the origin”
Control Transfer Statements
continue
break
fallthrough
return
throw
Continue
The continue statement tells a loop to stop what it is doing and start again at the beginning of the next iteration through the loop.
let puzzleInput = “great minds think alike” var puzzleOutput = “” let charactersToRemove: [Character] = [“a”, “e”, “i”, “o”, “u”, “ ”] for character in puzzleInput { if charactersToRemove.contains(character) { continue } else { puzzleOutput.append(character) } } print(puzzleOutput) // Prints “grtmndsthnklk”
Break
The break statement ends execution of an entire control flow statement immediately. The break statement can be used inside a switch or loop statement when you want to terminate the execution of the switch or loop statement earlier than would otherwise be the case.
Break in a Loop Statement
When used inside a loop statement, break ends the loop’s execution immediately and transfers control to the code after the loop’s closing brace (}). No further code from the current iteration of the loop is executed, and no further iterations of the loop are started.
Break in a Switch Statement
When used inside a switch statement, break causes the switch statement to end its execution immediately and to transfer control to the code after the switch statement’s closing brace (}).
Fallthrough
In Swift,the entire switch statement completes its execution as soon as the first matching case is completed. By contrast, C requires you to insert an explicit break statement at the end of every switch case to prevent fallthrough
let integerToDescribe = 5 var description = “The number (integerToDescribe) is” switch integerToDescribe { case 2, 3, 5, 7, 11, 13, 17, 19: description += “ a prime number, and also” fallthrough default: description += “ an integer.” } print(description) // Prints “The number 5 is a prime number, and also an integer.”
NOTE
The fallthrough keyword simply causes code execution to move directly to the statements inside the next case (or default case) block, as in C’s standard switch statement behavior.
Labeled Statements
gameLoop: while square != finalSquare { diceRoll += 1 if diceRoll == 7 { diceRoll = 1 } switch square + diceRoll { case finalSquare: // diceRoll will move us to the final square, so the game is over break gameLoop case let newSquare where newSquare > finalSquare: // diceRoll will move us beyond the final square, so roll again continue gameLoop default: // this is a valid move, so find out its effect square += diceRoll square += board[square] } } print(“Game over!”)
Early Exit
A guard statement, like an if statement, executes statements depending on the Boolean value of an expression. You use a guard statement to require that a condition must be true in order for the code after the guard statement to be executed. Unlike an if statement, a guard statement always has an else clause—the code inside the else clause is executed if the condition is not true.
func greet(person: [String: String]) { guard let name = person[“name”] else { return }
print(“Hello (name)!”)
guard let location = person[“location”] else { print(“I hope the weather is nice near you.”) return }
print(“I hope the weather is nice in (location).”) } greet(person: [“name”: “John”]) // Prints “Hello John!” // Prints “I hope the weather is nice near you.” greet(person: [“name”: “Jane”, “location”: “Cupertino”]) // Prints “Hello Jane!” // Prints “I hope the weather is nice in Cupertino.”
If the guard statement’s condition is met, code execution continues after the guard statement’s closing brace. Any variables or constants that were assigned values using an optional binding as part of the condition are available for the rest of the code block that the guard statement appears in.
If that condition is not met, the code inside the else branch is executed. That branch must transfer control to exit the code block in which the guard statement appears. It can do this with a control transfer statement such as return, break, continue, or throw, or it can call a function or method that doesn’t return, such as fatalError(_:file:line:).
Swift has built-in support for checking API availability, which ensures that you don’t accidentally use APIs that are unavailable on a given deployment target.
You use an availability condition in an if or guard statement to conditionally execute a block of code.
if #available(iOS 10, macOS 10.12, *) { // Use iOS 10 APIs on iOS, and use macOS 10.12 APIs on macOS } else { // Fall back to earlier iOS and macOS APIs }
if #available(platform name version, …, *) { statements to execute if the APIs are available } else { fallback statements to execute if the APIs are unavailable }
You use the for–in loop to iterate over a sequence, such as items in an array, ranges of numbers, or characters in a string.
let names = [“Anna”, “Alex”, “Brian”, “Jack”] for name in names { print(“Hello, (name)!”) } // Hello, Anna! // Hello, Alex! // Hello, Brian! // Hello, Jack!
You can also iterate over a dictionary to access its key-value pairs. Each item in the dictionary is returned as a (key, value) tuple when the dictionary is iterated, and you can decompose the (key, value) tuple’s members as explicitly named constants for use within the body of the for–in loop. In the code example below, the dictionary’s keys are decomposed into a constant called animalName, and the dictionary’s values are decomposed into a constant called legCount.
let numberOfLegs = [“spider”: 8, “ant”: 6, “cat”: 4] for (animalName, legCount) in numberOfLegs { print(“(animalName)s have (legCount) legs”) } // ants have 6 legs // spiders have 8 legs // cats have 4 legs
for index in 1…5 { print(“(index) times 5 is (index * 5)”) } // 1 times 5 is 5 // 2 times 5 is 10 // 3 times 5 is 15 // 4 times 5 is 20 // 5 times 5 is 25
If you don’t need each value from a sequence, you can ignore the values by using an underscore in place of a variable name.
let base = 3 let power = 10 var answer = 1 for _ in 1…power { answer *= base } print(“(base) to the power of (power) is (answer)”) // Prints “3 to the power of 10 is 59049”
let minutes = 60 for tickMark in 0..<minutes { // render the tick mark each minute (60 times) }
stride(from:to:by:) function
let minuteInterval = 5 for tickMark in stride(from: 0, to: minutes, by: minuteInterval) { // render the tick mark every 5 minutes (0, 5, 10, 15 … 45, 50, 55) }
Closed ranges are also available, by using stride(from:through:by:) instead:
let hours = 12 let hourInterval = 3 for tickMark in stride(from: 3, through: hours, by: hourInterval) { // render the tick mark every 3 hours (3, 6, 9, 12) }
While Loops
while evaluates its condition at the start of each pass through the loop.
repeat–while evaluates its condition at the end of each pass through the loop.
While
A while loop starts by evaluating a single condition. If the condition is true, a set of statements is repeated until the condition becomes false.
while square < finalSquare { // roll the dice
}
Repeat-While
The other variation of the while loop, known as the repeat–while loop, performs a single pass through the loop block first, before considering the loop’s condition. It then continues to repeat the loop until the condition is false.
repeat { // move up or down for a snake or ladder
} while square < finalSquare
Conditional Statements
If
if temperatureInFahrenheit <= 32 { print(“It’s very cold. Consider wearing a scarf.”) } else if temperatureInFahrenheit >= 86 { print(“It’s really warm. Don’t forget to wear sunscreen.”) } else { print(“It’s not that cold. Wear a t-shirt.”) } // Prints “It’s really warm. Don’t forget to wear sunscreen.”
Switch
let someCharacter: Character = “z” switch someCharacter { case “a”: print(“The first letter of the alphabet”) case “z”: print(“The last letter of the alphabet”) default: print(“Some other character”) } // Prints “The last letter of the alphabet”
No Implicit Fallthrough
In contrast with switch statements in C and Objective-C, switch statements in Swift do not fall through the bottom of each case and into the next one by default. Instead, the entire switch statement finishes its execution as soon as the first matching switch case is completed, without requiring an explicit breakstatement.
NOTE
Although break is not required in Swift, you can use a break statement to match and ignore a particular case or to break out of a matched case before that case has completed its execution.
The body of each case must contain at least one executable statement. It is not valid to write the following code, because the first case is empty:
let anotherCharacter: Character = “a” switch anotherCharacter { case “a”: // Invalid, the case has an empty body case “A”: print(“The letter A”) default: print(“Not the letter A”) } // This will report a compile-time error.
To make a switch with a single case that matches both "a" and "A", combine the two values into a compound case, separating the values with commas.
let anotherCharacter: Character = “a” switch anotherCharacter { case “a”, “A”: print(“The letter A”) default: print(“Not the letter A”) } // Prints “The letter A”
Interval Matching
let approximateCount = 62 let countedThings = “moons orbiting Saturn” let naturalCount: String switch approximateCount { case 0: naturalCount = “no” case 1..<5: naturalCount = “a few” case 5..<12: naturalCount = “several” case 12..<100: naturalCount = “dozens of” case 100..<1000: naturalCount = “hundreds of” default: naturalCount = “many” } print(“There are (naturalCount) (countedThings).”) // Prints “There are dozens of moons orbiting Saturn.”
Tuples
You can use tuples to test multiple values in the same switch statement. use the underscore character (_), also known as the wildcard pattern, to match any possible value.
let somePoint = (1, 1) switch somePoint { case (0, 0): print(“(somePoint) is at the origin”) case (_, 0): print(“(somePoint) is on the x-axis”) case (0, _): print(“(somePoint) is on the y-axis”) case (-2…2, -2…2): print(“(somePoint) is inside the box”) default: print(“(somePoint) is outside of the box”) } // Prints “(1, 1) is inside the box”
n fact, the point (0, 0) could match all four of the cases in this example. However, if multiple matches are possible, the first matching case is always used. The point (0, 0) would match case (0, 0) first, and so all other matching cases would be ignored.
Value Bindings
let anotherPoint = (2, 0) switch anotherPoint { case (let x, 0): print(“on the x-axis with an x value of (x)”) case (0, let y): print(“on the y-axis with a y value of (y)”) case let (x, y): print(“somewhere else at ((x), (y))”) } // Prints “on the x-axis with an x value of 2”
After the temporary constants are declared, they can be used within the case’s code block.
This switch statement does not have a default case. The final case, case let (x, y), declares a tuple of two placeholder constants that can match any value. Because anotherPoint is always a tuple of two values, this case matches all possible remaining values, and a default case is not needed to make the switch statement exhaustive.
Where
A switch case can use a where clause to check for additional conditions.
let yetAnotherPoint = (1, -1) switch yetAnotherPoint { case let (x, y) where x == y: print(“((x), (y)) is on the line x == y”) case let (x, y) where x == -y: print(“((x), (y)) is on the line x == -y”) case let (x, y): print(“((x), (y)) is just some arbitrary point”) } // Prints “(1, -1) is on the line x == -y”
Compound Cases
Multiple switch cases that share the same body can be combined by writing several patterns after case, with a comma between each of the patterns.
let someCharacter: Character = “e” switch someCharacter { case “a”, “e”, “i”, “o”, “u”: print(“(someCharacter) is a vowel”) case “b”, “c”, “d”, “f”, “g”, “h”, “j”, “k”, “l”, “m”, “n”, “p”, “q”, “r”, “s”, “t”, “v”, “w”, “x”, “y”, “z”: print(“(someCharacter) is a consonant”) default: print(“(someCharacter) is not a vowel or a consonant”) } // Prints “e is a vowel”
let stillAnotherPoint = (9, 0) switch stillAnotherPoint { case (let distance, 0), (0, let distance): print(“On an axis, (distance) from the origin”) default: print(“Not on an axis”) } // Prints “On an axis, 9 from the origin”
Control Transfer Statements
continue
break
fallthrough
return
throw
Continue
The continue statement tells a loop to stop what it is doing and start again at the beginning of the next iteration through the loop.
let puzzleInput = “great minds think alike” var puzzleOutput = “” let charactersToRemove: [Character] = [“a”, “e”, “i”, “o”, “u”, “ ”] for character in puzzleInput { if charactersToRemove.contains(character) { continue } else { puzzleOutput.append(character) } } print(puzzleOutput) // Prints “grtmndsthnklk”
Break
The break statement ends execution of an entire control flow statement immediately. The break statement can be used inside a switch or loop statement when you want to terminate the execution of the switch or loop statement earlier than would otherwise be the case.
Break in a Loop Statement
When used inside a loop statement, break ends the loop’s execution immediately and transfers control to the code after the loop’s closing brace (}). No further code from the current iteration of the loop is executed, and no further iterations of the loop are started.
Break in a Switch Statement
When used inside a switch statement, break causes the switch statement to end its execution immediately and to transfer control to the code after the switch statement’s closing brace (}).
Fallthrough
In Swift,the entire switch statement completes its execution as soon as the first matching case is completed. By contrast, C requires you to insert an explicit break statement at the end of every switch case to prevent fallthrough
let integerToDescribe = 5 var description = “The number (integerToDescribe) is” switch integerToDescribe { case 2, 3, 5, 7, 11, 13, 17, 19: description += “ a prime number, and also” fallthrough default: description += “ an integer.” } print(description) // Prints “The number 5 is a prime number, and also an integer.”
NOTE
The fallthrough keyword simply causes code execution to move directly to the statements inside the next case (or default case) block, as in C’s standard switch statement behavior.
Labeled Statements
gameLoop: while square != finalSquare { diceRoll += 1 if diceRoll == 7 { diceRoll = 1 } switch square + diceRoll { case finalSquare: // diceRoll will move us to the final square, so the game is over break gameLoop case let newSquare where newSquare > finalSquare: // diceRoll will move us beyond the final square, so roll again continue gameLoop default: // this is a valid move, so find out its effect square += diceRoll square += board[square] } } print(“Game over!”)
Early Exit
A guard statement, like an if statement, executes statements depending on the Boolean value of an expression. You use a guard statement to require that a condition must be true in order for the code after the guard statement to be executed. Unlike an if statement, a guard statement always has an else clause—the code inside the else clause is executed if the condition is not true.
func greet(person: [String: String]) { guard let name = person[“name”] else { return }
print(“Hello (name)!”)
guard let location = person[“location”] else { print(“I hope the weather is nice near you.”) return }
print(“I hope the weather is nice in (location).”) } greet(person: [“name”: “John”]) // Prints “Hello John!” // Prints “I hope the weather is nice near you.” greet(person: [“name”: “Jane”, “location”: “Cupertino”]) // Prints “Hello Jane!” // Prints “I hope the weather is nice in Cupertino.”
If the guard statement’s condition is met, code execution continues after the guard statement’s closing brace. Any variables or constants that were assigned values using an optional binding as part of the condition are available for the rest of the code block that the guard statement appears in.
If that condition is not met, the code inside the else branch is executed. That branch must transfer control to exit the code block in which the guard statement appears. It can do this with a control transfer statement such as return, break, continue, or throw, or it can call a function or method that doesn’t return, such as fatalError(_:file:line:).
Swift has built-in support for checking API availability, which ensures that you don’t accidentally use APIs that are unavailable on a given deployment target.
You use an availability condition in an if or guard statement to conditionally execute a block of code.
if #available(iOS 10, macOS 10.12, *) { // Use iOS 10 APIs on iOS, and use macOS 10.12 APIs on macOS } else { // Fall back to earlier iOS and macOS APIs }
if #available(platform name version, …, *) { statements to execute if the APIs are available } else { fallback statements to execute if the APIs are unavailable }
You use the for–in loop to iterate over a sequence, such as items in an array, ranges of numbers, or characters in a string.
let names = [“Anna”, “Alex”, “Brian”, “Jack”] for name in names { print(“Hello, (name)!”) } // Hello, Anna! // Hello, Alex! // Hello, Brian! // Hello, Jack!
You can also iterate over a dictionary to access its key-value pairs. Each item in the dictionary is returned as a (key, value) tuple when the dictionary is iterated, and you can decompose the (key, value) tuple’s members as explicitly named constants for use within the body of the for–in loop. In the code example below, the dictionary’s keys are decomposed into a constant called animalName, and the dictionary’s values are decomposed into a constant called legCount.
let numberOfLegs = [“spider”: 8, “ant”: 6, “cat”: 4] for (animalName, legCount) in numberOfLegs { print(“(animalName)s have (legCount) legs”) } // ants have 6 legs // spiders have 8 legs // cats have 4 legs
for index in 1…5 { print(“(index) times 5 is (index * 5)”) } // 1 times 5 is 5 // 2 times 5 is 10 // 3 times 5 is 15 // 4 times 5 is 20 // 5 times 5 is 25
If you don’t need each value from a sequence, you can ignore the values by using an underscore in place of a variable name.
let base = 3 let power = 10 var answer = 1 for _ in 1…power { answer *= base } print(“(base) to the power of (power) is (answer)”) // Prints “3 to the power of 10 is 59049”
let minutes = 60 for tickMark in 0..<minutes { // render the tick mark each minute (60 times) }
stride(from:to:by:) function
let minuteInterval = 5 for tickMark in stride(from: 0, to: minutes, by: minuteInterval) { // render the tick mark every 5 minutes (0, 5, 10, 15 … 45, 50, 55) }
Closed ranges are also available, by using stride(from:through:by:) instead:
let hours = 12 let hourInterval = 3 for tickMark in stride(from: 3, through: hours, by: hourInterval) { // render the tick mark every 3 hours (3, 6, 9, 12) }
While Loops
while evaluates its condition at the start of each pass through the loop.
repeat–while evaluates its condition at the end of each pass through the loop.
While
A while loop starts by evaluating a single condition. If the condition is true, a set of statements is repeated until the condition becomes false.
while square < finalSquare { // roll the dice
}
Repeat-While
The other variation of the while loop, known as the repeat–while loop, performs a single pass through the loop block first, before considering the loop’s condition. It then continues to repeat the loop until the condition is false.
repeat { // move up or down for a snake or ladder
} while square < finalSquare
Conditional Statements
If
if temperatureInFahrenheit <= 32 { print(“It’s very cold. Consider wearing a scarf.”) } else if temperatureInFahrenheit >= 86 { print(“It’s really warm. Don’t forget to wear sunscreen.”) } else { print(“It’s not that cold. Wear a t-shirt.”) } // Prints “It’s really warm. Don’t forget to wear sunscreen.”
Switch
let someCharacter: Character = “z” switch someCharacter { case “a”: print(“The first letter of the alphabet”) case “z”: print(“The last letter of the alphabet”) default: print(“Some other character”) } // Prints “The last letter of the alphabet”
No Implicit Fallthrough
In contrast with switch statements in C and Objective-C, switch statements in Swift do not fall through the bottom of each case and into the next one by default. Instead, the entire switch statement finishes its execution as soon as the first matching switch case is completed, without requiring an explicit breakstatement.
NOTE
Although break is not required in Swift, you can use a break statement to match and ignore a particular case or to break out of a matched case before that case has completed its execution.
The body of each case must contain at least one executable statement. It is not valid to write the following code, because the first case is empty:
let anotherCharacter: Character = “a” switch anotherCharacter { case “a”: // Invalid, the case has an empty body case “A”: print(“The letter A”) default: print(“Not the letter A”) } // This will report a compile-time error.
To make a switch with a single case that matches both "a" and "A", combine the two values into a compound case, separating the values with commas.
let anotherCharacter: Character = “a” switch anotherCharacter { case “a”, “A”: print(“The letter A”) default: print(“Not the letter A”) } // Prints “The letter A”
Interval Matching
let approximateCount = 62 let countedThings = “moons orbiting Saturn” let naturalCount: String switch approximateCount { case 0: naturalCount = “no” case 1..<5: naturalCount = “a few” case 5..<12: naturalCount = “several” case 12..<100: naturalCount = “dozens of” case 100..<1000: naturalCount = “hundreds of” default: naturalCount = “many” } print(“There are (naturalCount) (countedThings).”) // Prints “There are dozens of moons orbiting Saturn.”
Tuples
You can use tuples to test multiple values in the same switch statement. use the underscore character (_), also known as the wildcard pattern, to match any possible value.
let somePoint = (1, 1) switch somePoint { case (0, 0): print(“(somePoint) is at the origin”) case (_, 0): print(“(somePoint) is on the x-axis”) case (0, _): print(“(somePoint) is on the y-axis”) case (-2…2, -2…2): print(“(somePoint) is inside the box”) default: print(“(somePoint) is outside of the box”) } // Prints “(1, 1) is inside the box”
n fact, the point (0, 0) could match all four of the cases in this example. However, if multiple matches are possible, the first matching case is always used. The point (0, 0) would match case (0, 0) first, and so all other matching cases would be ignored.
Value Bindings
let anotherPoint = (2, 0) switch anotherPoint { case (let x, 0): print(“on the x-axis with an x value of (x)”) case (0, let y): print(“on the y-axis with a y value of (y)”) case let (x, y): print(“somewhere else at ((x), (y))”) } // Prints “on the x-axis with an x value of 2”
After the temporary constants are declared, they can be used within the case’s code block.
This switch statement does not have a default case. The final case, case let (x, y), declares a tuple of two placeholder constants that can match any value. Because anotherPoint is always a tuple of two values, this case matches all possible remaining values, and a default case is not needed to make the switch statement exhaustive.
Where
A switch case can use a where clause to check for additional conditions.
let yetAnotherPoint = (1, -1) switch yetAnotherPoint { case let (x, y) where x == y: print(“((x), (y)) is on the line x == y”) case let (x, y) where x == -y: print(“((x), (y)) is on the line x == -y”) case let (x, y): print(“((x), (y)) is just some arbitrary point”) } // Prints “(1, -1) is on the line x == -y”
Compound Cases
Multiple switch cases that share the same body can be combined by writing several patterns after case, with a comma between each of the patterns.
let someCharacter: Character = “e” switch someCharacter { case “a”, “e”, “i”, “o”, “u”: print(“(someCharacter) is a vowel”) case “b”, “c”, “d”, “f”, “g”, “h”, “j”, “k”, “l”, “m”, “n”, “p”, “q”, “r”, “s”, “t”, “v”, “w”, “x”, “y”, “z”: print(“(someCharacter) is a consonant”) default: print(“(someCharacter) is not a vowel or a consonant”) } // Prints “e is a vowel”
let stillAnotherPoint = (9, 0) switch stillAnotherPoint { case (let distance, 0), (0, let distance): print(“On an axis, (distance) from the origin”) default: print(“Not on an axis”) } // Prints “On an axis, 9 from the origin”
Control Transfer Statements
continue
break
fallthrough
return
throw
Continue
The continue statement tells a loop to stop what it is doing and start again at the beginning of the next iteration through the loop.
let puzzleInput = “great minds think alike” var puzzleOutput = “” let charactersToRemove: [Character] = [“a”, “e”, “i”, “o”, “u”, “ ”] for character in puzzleInput { if charactersToRemove.contains(character) { continue } else { puzzleOutput.append(character) } } print(puzzleOutput) // Prints “grtmndsthnklk”
Break
The break statement ends execution of an entire control flow statement immediately. The break statement can be used inside a switch or loop statement when you want to terminate the execution of the switch or loop statement earlier than would otherwise be the case.
Break in a Loop Statement
When used inside a loop statement, break ends the loop’s execution immediately and transfers control to the code after the loop’s closing brace (}). No further code from the current iteration of the loop is executed, and no further iterations of the loop are started.
Break in a Switch Statement
When used inside a switch statement, break causes the switch statement to end its execution immediately and to transfer control to the code after the switch statement’s closing brace (}).
Fallthrough
In Swift,the entire switch statement completes its execution as soon as the first matching case is completed. By contrast, C requires you to insert an explicit break statement at the end of every switch case to prevent fallthrough
let integerToDescribe = 5 var description = “The number (integerToDescribe) is” switch integerToDescribe { case 2, 3, 5, 7, 11, 13, 17, 19: description += “ a prime number, and also” fallthrough default: description += “ an integer.” } print(description) // Prints “The number 5 is a prime number, and also an integer.”
NOTE
The fallthrough keyword simply causes code execution to move directly to the statements inside the next case (or default case) block, as in C’s standard switch statement behavior.
Labeled Statements
gameLoop: while square != finalSquare { diceRoll += 1 if diceRoll == 7 { diceRoll = 1 } switch square + diceRoll { case finalSquare: // diceRoll will move us to the final square, so the game is over break gameLoop case let newSquare where newSquare > finalSquare: // diceRoll will move us beyond the final square, so roll again continue gameLoop default: // this is a valid move, so find out its effect square += diceRoll square += board[square] } } print(“Game over!”)
Early Exit
A guard statement, like an if statement, executes statements depending on the Boolean value of an expression. You use a guard statement to require that a condition must be true in order for the code after the guard statement to be executed. Unlike an if statement, a guard statement always has an else clause—the code inside the else clause is executed if the condition is not true.
func greet(person: [String: String]) { guard let name = person[“name”] else { return }
print(“Hello (name)!”)
guard let location = person[“location”] else { print(“I hope the weather is nice near you.”) return }
print(“I hope the weather is nice in (location).”) } greet(person: [“name”: “John”]) // Prints “Hello John!” // Prints “I hope the weather is nice near you.” greet(person: [“name”: “Jane”, “location”: “Cupertino”]) // Prints “Hello Jane!” // Prints “I hope the weather is nice in Cupertino.”
If the guard statement’s condition is met, code execution continues after the guard statement’s closing brace. Any variables or constants that were assigned values using an optional binding as part of the condition are available for the rest of the code block that the guard statement appears in.
If that condition is not met, the code inside the else branch is executed. That branch must transfer control to exit the code block in which the guard statement appears. It can do this with a control transfer statement such as return, break, continue, or throw, or it can call a function or method that doesn’t return, such as fatalError(_:file:line:).
Swift has built-in support for checking API availability, which ensures that you don’t accidentally use APIs that are unavailable on a given deployment target.
You use an availability condition in an if or guard statement to conditionally execute a block of code.
if #available(iOS 10, macOS 10.12, *) { // Use iOS 10 APIs on iOS, and use macOS 10.12 APIs on macOS } else { // Fall back to earlier iOS and macOS APIs }
if #available(platform name version, …, *) { statements to execute if the APIs are available } else { fallback statements to execute if the APIs are unavailable }
Swift’s array, set, and dictionary types are implemented as generic collections. For more on generic types and collections, see Generics.
Mutability of Collections
If you create an array, a set, or a dictionary, and assign it to a variable, the collection that is created will be mutable. This means that you can change (or mutate) the collection after it’s created by adding, removing, or changing items in the collection. If you assign an array, a set, or a dictionary to a constant, that collection is immutable, and its size and contents cannot be changed.
Arrays
Array Type Shorthand Syntax
만드는법
Array<Element>, where Element is the type of values the array is allowed to store.
[Element]
Creating an Empty Array
var someInts = [Int]() print(“someInts is of type [Int] with (someInts.count) items.”) // Prints “someInts is of type [Int] with 0 items.”
Alternatively, if the context already provides type information, such as a function argument or an already typed variable or constant, you can create an empty array with an empty array literal, which is written as [] (an empty pair of square brackets):
someInts.append(3) // someInts now contains 1 value of type Int someInts = [] // someInts is now an empty array, but is still of type [Int]
Creating an Array with a Default Value
creating an array of a certain size with all of its values set to the same default value.
var threeDoubles = Array(repeating: 0.0, count: 3) // threeDoubles is of type [Double], and equals [0.0, 0.0, 0.0]
Creating an Array by Adding Two Arrays Together
var anotherThreeDoubles = Array(repeating: 2.5, count: 3) // anotherThreeDoubles is of type [Double], and equals [2.5, 2.5, 2.5] var sixDoubles = threeDoubles + anotherThreeDoubles // sixDoubles is inferred as [Double], and equals [0.0, 0.0, 0.0, 2.5, 2.5, 2.5]
Creating an Array with an Array Literal
[value 1, value 2, value 3]
var shoppingList: [String] = [“Eggs”, “Milk”] // shoppingList has been initialized with two initial items
Thanks to Swift’s type inference, you don’t have to write the type of the array if you’re initializing it with an array literal containing values of the same type. The initialization of shoppingList could have been written in a shorter form instead:
var shoppingList = [“Eggs”, “Milk”]
Accessing and Modifying an Array
You access and modify an array through its methods and properties, or by using subscript syntax.
print(“The shopping list contains (shoppingList.count) items.”) // Prints “The shopping list contains 2 items.”
if shoppingList.isEmpty { print(“The shopping list is empty.”) } else { print(“The shopping list is not empty.”) } // Prints “The shopping list is not empty.”
shoppingList.append(“Flour”) // shoppingList now contains 3 items, and someone is making pancakes
Retrieve a value from the array by using subscript syntax, passing the index of the value you want to retrieve within square brackets immediately after the name of the array:
var firstItem = shoppingList[0] // firstItem is equal to “Eggs”
NOTE
Arrays in Swift are always zero-indexed.
subscript syntax to change an existing value at a given index:
shoppingList[0] = “Six eggs” // the first item in the list is now equal to “Six eggs” rather than “Eggs”
You can also use subscript syntax to change a range of values at once, even if the replacement set of values has a different length than the range you are replacing. The following example replaces "Chocolate Spread", "Cheese", and "Butter" with "Bananas" and "Apples":
shoppingList[4…6] = [“Bananas”, “Apples”] // shoppingList now contains 6 items
insert(_:at:) method:
shoppingList.insert(“Maple Syrup”, at: 0) // shoppingList now contains 7 items // “Maple Syrup” is now the first item in the list
This call to the insert(_:at:) method inserts a new item with a value of "Maple Syrup" at the very beginning of the shopping list, indicated by an index of 0.
remove(at:) method
let mapleSyrup = shoppingList.remove(at: 0) // the item that was at index 0 has just been removed // shoppingList now contains 6 items, and no Maple Syrup // the mapleSyrup constant is now equal to the removed “Maple Syrup” string
NOTE
If you try to access or modify a value for an index that is outside of an array’s existing bounds, you will trigger a runtime error. You can check that an index is valid before using it by comparing it to the array’s countproperty. The largest valid index in an array is count - 1 because arrays are indexed from zero—however, when count is 0 (meaning the array is empty), there are no valid indexes.
removeLast() method
let apples = shoppingList.removeLast() // the last item in the array has just been removed // shoppingList now contains 5 items, and no apples // the apples constant is now equal to the removed “Apples” string
Iterating Over an Array
for item in shoppingList { print(item) } // Six eggs // Milk // Flour // Baking Powder // Bananas
If you need the integer index of each item as well as its value, use the enumerated() method to iterate over the array instead. For each item in the array, the enumerated() method returns a tuple composed of an integer and the item. The integers start at zero and count up by one for each item; if you enumerate over a whole array, these integers match the items’ indices. You can decompose the tuple into temporary constants or variables as part of the iteration:
A set stores distinct values of the same type in a collection with no defined ordering.
NOTE
Swift’s Set type is bridged to Foundation’s NSSet class.
Hash Values for Set Types
A type must be hashable in order to be stored in a set—that is, the type must provide a way to compute a hash value for itself. A hash value is an Int value that is the same for all objects that compare equally, such that if a == b, it follows that a.hashValue == b.hashValue.
All of Swift’s basic types (such as String, Int, Double, and Bool) are hashable by default, and can be used as set value types or dictionary key types. Enumeration case values without associated values (as described in Enumerations) are also hashable by default.
NOTE
You can use your own custom types as set value types or dictionary key types by making them conform to the Hashable protocol from Swift’s standard library. Types that conform to the Hashable protocol must provide a gettable Int property called hashValue. The value returned by a type’s hashValue property is not required to be the same across different executions of the same program, or in different programs.
Because the Hashable protocol conforms to Equatable, conforming types must also provide an implementation of the equals operator (==). The Equatable protocol requires any conforming implementation of == to be an equivalence relation. That is, an implementation of == must satisfy the following three conditions, for all values a, b, and c:
a == a (Reflexivity)
a == b implies b == a (Symmetry)
a == b && b == c implies a == c (Transitivity)
Set Type Syntax
The type of a Swift set is written as Set<Element>, where Element is the type that the set is allowed to store. Unlike arrays, sets do not have an equivalent shorthand form.
Creating and Initializing an Empty Set
var letters = Set<Character>() print(“letters is of type Set<Character> with (letters.count) items.”) // Prints “letters is of type Set<Character> with 0 items.”
NOTE
The type of the letters variable is inferred to be Set<Character>, from the type of the initializer.
Alternatively, if the context already provides type information, such as a function argument or an already typed variable or constant, you can create an empty set with an empty array literal:
letters.insert(“a”) // letters now contains 1 value of type Character letters = [] // letters is now an empty set, but is still of type Set<Character> 위에서 이미 character라고 정해졌기 때문에 여기서 또 명시할필요는 없다.
Creating a Set with an Array Literal
var favoriteGenres: Set<String> = [“Rock”, “Classical”, “Hip hop”] // favoriteGenres has been initialized with three initial items
The favoriteGenres variable is declared as “a set of String values”, written as Set<String>. Because this particular set has specified a value type of String.
A set type cannot be inferred from an array literal alone, so the type Set must be explicitly declared. However, because of Swift’s type inference, you don’t have to write the type of the set if you’re initializing it with an array literal containing values of the same type. (데이터타입은 명시하지 않아도 되지만 set이라고는 명시해야 된다.)
var favoriteGenres: Set = [“Rock”, “Classical”, “Hip hop”]
Accessing and Modifying a Set
You access and modify a set through its methods and properties.
count property:
print(“I have (favoriteGenres.count) favorite music genres.”) // Prints “I have 3 favorite music genres.”
isEmpty property
if favoriteGenres.isEmpty { print(“As far as music goes, I’m not picky.”) } else { print(“I have particular music preferences.”) } // Prints “I have particular music preferences.”
insert(_:) method:
favoriteGenres.insert(“Jazz”) // favoriteGenres now contains 4 items
remove(_:) method, which removes the item if it’s a member of the set, and returns the removed value, or returns nil if the set did not contain it.
all items in a set can be removed with its removeAll() method.
if let removedGenre = favoriteGenres.remove(“Rock”) { print(“(removedGenre)? I’m over it.”) } else { print(“I never much cared for that.”) } // Prints “Rock? I’m over it.”
contains(_:) method.
if favoriteGenres.contains(“Funk”) { print(“I get up on the good foot.”) } else { print(“It’s too funky in here.”) } // Prints “It’s too funky in here.”
Iterating Over a Set
for genre in favoriteGenres { print(“(genre)”) } // Jazz // Hip hop // Classical
Swift’s Set type does not have a defined ordering. To iterate over the values of a set in a specific order, use the sorted() method, which returns the set’s elements as an array sorted using the < operator.
for genre in favoriteGenres.sorted() { print(“(genre)”) } // Classical // Hip hop // Jazz
Performing Set Operations
Fundamental Set Operations
Use the intersection(_:) method to create a new set with only the values common to both sets.
Use the symmetricDifference(_:) method to create a new set with values in either set, but not both.
Use the union(_:) method to create a new set with all of the values in both sets.
Use the subtracting(_:) method to create a new set with values not in the specified set.
let oddDigits: Set = [1, 3, 5, 7, 9] let evenDigits: Set = [0, 2, 4, 6, 8] let singleDigitPrimeNumbers: Set = [2, 3, 5, 7] oddDigits.union(evenDigits).sorted() // [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] oddDigits.intersection(evenDigits).sorted() // [] oddDigits.subtracting(singleDigitPrimeNumbers).sorted() // [1, 9] oddDigits.symmetricDifference(singleDigitPrimeNumbers).sorted() // [1, 2, 9]
Set Membership and Equality
Set a is a superset of set b, because a contains all elements in b. Conversely, set b is a subset of set a.
Use the “is equal” operator (==) to determine whether two sets contain all of the same values.
Use the isSubset(of:) method to determine whether all of the values of a set are contained in the specified set.
Use the isSuperset(of:) method to determine whether a set contains all of the values in a specified set.
Use the isStrictSubset(of:) or isStrictSuperset(of:) methods to determine whether a set is a subset or superset, but not equal to, a specified set.
Use the isDisjoint(with:) method to determine whether two sets have no values in common.
let houseAnimals: Set = [“🐶”, “🐱”] let farmAnimals: Set = [“🐮”, “🐔”, “🐑”, “🐶”, “🐱”] let cityAnimals: Set = [“🐦”, “🐭”] houseAnimals.isSubset(of: farmAnimals) // true farmAnimals.isSuperset(of: houseAnimals) // true farmAnimals.isDisjoint(with: cityAnimals) // true
Dictionaries
A dictionary stores associations between keys of the same type and values of the same type in a collection with no defined ordering. Each value is associated with a unique key.
NOTE
Swift’s Dictionary type is bridged to Foundation’s NSDictionary class.
Dictionary Type Shorthand Syntax
Dictionary<Key, Value>
NOTE
A dictionary Key type must conform to the Hashable protocol, like a set’s value type.
You can also write the type of a dictionary in shorthand form as [Key: Value].
Creating an Empty Dictionary
var namesOfIntegers = [Int: String]() // namesOfIntegers is an empty [Int: String] dictionary
If the context already provides type information, you can create an empty dictionary with an empty dictionary literal, which is written as [:] (a colon inside a pair of square brackets):
namesOfIntegers[16] = “sixteen” // namesOfIntegers now contains 1 key-value pair namesOfIntegers = [:] // namesOfIntegers is once again an empty dictionary of type [Int: String]
Accessing and Modifying a Dictionary
You access and modify a dictionary through its methods and properties, or by using subscript syntax.
read-only count property:
print(“The airports dictionary contains (airports.count) items.”) // Prints “The airports dictionary contains 2 items.”
isEmpty property
if airports.isEmpty { print(“The airports dictionary is empty.”) } else { print(“The airports dictionary is not empty.”) } // Prints “The airports dictionary is not empty.”
assign a new value
airports[“LHR”] = “London” // the airports dictionary now contains 3 items
change the value
airports[“LHR”] = “London Heathrow” // the value for “LHR” has been changed to “London Heathrow”
updateValue(_:forKey:) method to set or update the value for a particular key. updateValue(_:forKey:) method sets a value for a key if none exists, or updates the value if that key already exists. Unlike a subscript, however, the updateValue(_:forKey:) method returns the old value after performing an update.
The updateValue(_:forKey:) method returns an optional value of the dictionary’s value type. For a dictionary that stores String values, for example, the method returns a value of type String?, or “optional String”. This optional value contains the old value for that key if one existed before the update, or nil if no value existed:
if let oldValue = airports.updateValue(“Dublin Airport”, forKey: “DUB”) { print(“The old value for DUB was (oldValue).”) } // Prints “The old value for DUB was Dublin.”
if let airportName = airports[“DUB”] { print(“The name of the airport is (airportName).”) } else { print(“That airport is not in the airports dictionary.”) } // Prints “The name of the airport is Dublin Airport.”
remove a key-value pair from a dictionary by assigning a value of nil for that key:
airports[“APL”] = “Apple International” // “Apple International” is not the real airport for APL, so delete it
airports[“APL”] = nil // APL has now been removed from the dictionary
Alternatively, remove a key-value pair from a dictionary with the removeValue(forKey:) method. This method removes the key-value pair if it exists and returns the removed value, or returns nil if no value existed:
if let removedValue = airports.removeValue(forKey: “DUB”) { print(“The removed airport’s name is (removedValue).”) } else { print(“The airports dictionary does not contain a value for DUB.”) } // Prints “The removed airport’s name is Dublin Airport.”
Iterating Over a Dictionary
You can iterate over the key-value pairs in a dictionary with a for–in loop. Each item in the dictionary is returned as a (key, value) tuple, and you can decompose the tuple’s members into temporary constants or variables as part of the iteration:
for (airportCode, airportName) in airports { print(“(airportCode): (airportName)”) } // YYZ: Toronto Pearson // LHR: London Heathrow
keys and values properties:
for airportCode in airports.keys { print(“Airport code: (airportCode)”) } // Airport code: YYZ // Airport code: LHR for airportName in airports.values { print(“Airport name: (airportName)”) } // Airport name: Toronto Pearson // Airport name: London Heathrow
let airportCodes = [String](airports.keys) // airportCodes is [“YYZ”, “LHR”]
let airportNames = [String](airports.values) // airportNames is [“Toronto Pearson”, “London Heathrow”]
Swift’s Dictionary type does not have a defined ordering. To iterate over the keys or values of a dictionary in a specific order, use the sorted() method on its keys or values property.