For-In Loops
- you can use the for-in loop to iterate over a sequence, such as items in an array, characters in an string
let names = ["Anna", "Alex", "Brian", "Jack"]
for name in names {
print("hello \(name)")
}
- you can use the for-in loop to iterate over a dictionary to access its key-value pairs. each item in the dictionary returns a (key, value) tuple. decompose the tuple’s members as named constants for better use
let nameOfLegs = ["Spider": 8, "Ant": 6, "Cat": 4]
for (animalName, legCount) in nameOfLegs {
print("\(animalName) has \(legCount) legs")
}
- you can use for-in loops with numeric ranges
for i in 0...3 {
print("the index is \(i)")
}
- use the stride(from:to:by) function to skip the unwanted items
let steps = 5
for mark in stride(from: 0, to: 60, by: steps) {
//0 5 10 ...... 50 55
}
Repeat While
- the repeat-while loop is analogous to do-while loop in C
repeat {
//statement
} while condition
Switch
- the entire switch statement finishes its execution as soon as the first matching case is completed, without requiring an explicit break statement
let someCharacter = "z"
switch someCharacer {
case "a":
print("it is a")
case "b":
print("it is b")
default:
print("some other characters")
}
- the body of each statement must contain at least one executable statement
let someCharacter = "z"
switch someCharacer {
case "a": //error, the case body is empty
case "b":
print("it is b")
default:
print("some other characters")
}
- you can combine the 2 values into a compound case to make a case match both 2 values
let someCharacter = "z"
switch someCharacer {
case "a", "A":
print("it is a or A")
case "b":
print("it is b")
default:
print("some other characters")
}
Interval Matching
- values in switch cases can be checked for their inclusion in an interval
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"
}
- the value in the switch case can be a tuple, whose elements can be values or interval of values, and you can use the underscore character to match any possible value
let num = (1, 1)
switch num {
case (0, 0): print("1")
case (_, 0): print("2")
case (0, _): print("3")
case (-2...2, -2...2): print("4")
default: print("5")
}
Swift allows multiple cases to match the same value, but the first matching case is always used
Value Bindings
- a switch case can name the value or values that the case matches to temporary constants or variables for use in the body of the case
let point = (2, 2)
switch point {
case (let x, 0): print("the point is (\(x), 0)")
case (0, let y): print("the point is (0, \(y))")
case let (x, y): print("the point is (\(x), \(y))")
}
Where
- a switch case can use a where clause to check for the additional condition
let point = (1, -1)
switch point {
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 not on the line x == y or x == -y")
}
Compound Cases
- multiple switch cases that shares the same body can be combined by writing several patterns after case
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")
}
- compound cases can include value bindings
- all of the patterns of a compound case have to include the same set of value bindings, and each binding has to get a value of the same type from all of the patterns in the compound case
Control Transfer Statements
Continue
- the continue statement tells the loop stop what it is doing and start next iteration
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
}
puzzleOutput.append(character)
}
Break
- the break statement can be used to terminate the execution of loop or switch statement
Fallthrough
- In Swift, switch statements don’t fall through the bottom of each case and into the next one. That is, 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. Avoiding default fallthrough means that Swift switch statements are much more concise and predictable than their counterparts in C, and thus they avoid executing multiple switch cases by mistake.
- If you need C-style fallthrough behavior, you can opt in to this behavior on a case-by-case basis with the fallthrough keyword. The example below uses fallthrough to create a textual description of a number.
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."
}
note: The fallthrough keyword does not check the case conditions for the switch case that it causes execution to fall into. 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
label name: while condition {
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).")
}
- Using a guard statement for requirements improves the readability of your code, compared to doing the same check with an if statement. It lets you write the code that’s typically executed without wrapping it in an else block, and it lets you keep the code that handles a violated requirement next to the requirement.
Checking API Availability
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
}