忍者刘米米

1.A Swift Tour

Simple Values

Constant & Variable

  1. Use let to make a constant and var to make a variable.
  2. Type can be inferred by compiler.
1
2
3
var myVariable = 42
myVariable = 50
let myConstant = 42

Explicit declare

1
let explicitDouble: Double = 70

Type Conversion

1
2
3
let label = "The width is "
let width = 94
let widthLabel = label + String(width)

Placeholder

1
2
3
4
let apples = 3
let oranges = 5
let appleSummary = "I have \(apples) apples."
let fruitSummary = "I have \(apples + oranges) pieces of fruit."

Array and dictionary

1
2
3
4
5
6
7
8
var shoppingList = ["catfish", "water", "tulips", "blue paint"]
shoppingList[1] = "bottle of water"

var occupations = [
"Malcolm": "Captain",
"Kaylee": "Mechanic",
]
occupations["Jayne"] = "Public Relations"

Empty array or dictionary

1
2
let emptyArray = [String]()
let emptyDictionary = [String: Float]()

Type inferred

1
2
shoppingList = []
occupations = [:]

Control Flow

  1. Use ifand switch to make conditionals
  2. use for-in, for, while, andrepeat-while to make loops
  3. Parentheses around the condition or loop variable are optional. Braces around the body are required.

For in

1
2
3
4
5
6
7
8
9
10
11
let individualScores = [75, 43, 103, 87, 12]
var teamScore = 0

for score in individualScores {
if score > 50 {
teamScore += 3
} else {
teamScore += 1
}
}
print(teamScore)

For in range condition (..<)

Use ..<to make a range that omits its upper value, and use to make a range that includes both values.

1
2
3
4
5
var total = 0
for i in 0..<4 {
total += i
}
print(total)

For-in iterate dictionary

1
2
3
4
5
6
7
8
9
10
11
12
13
14
let interestingNumbers = [
"Prime": [2, 3, 5, 7, 11, 13],
"Fibonacci": [1, 1, 2, 3, 5, 8],
"Square": [1, 4, 9, 16, 25],
]
var largest = 0
for (kind, numbers) in interestingNumbers {
for number in numbers {
if number > largest {
largest = number
}
}
}
print(largest)

If - let unwrap optionals

An optional value either contains a value or contains nil to indicate that a value is missing

1
2
3
4
5
6
7
8
9
var optionalString: String? = "Hello"
print(optionalString == nil)

var optionalName: String? = "John Appleseed"
var greeting = "Hello!"

if let name = optionalName {
greeting = "Hello, \(name)"
}

Default value for Optional (??)

default value using the ?? operator. If the optional value is missing, the default value is used instead.

1
2
3
let nickName: String? = nil
let fullName: String = "John Appleseed"
let informalGreeting = "Hi \(nickName ?? fullName)"

Switch

  1. Switches support any kind of data
  2. 注意下面的 case 里可以设置let 常量进行计算,这样case 可以满足多多种自定义的条件,而不仅仅是比较相等
1
2
3
4
5
6
7
8
9
10
11
let vegetable = "red pepper"
switch vegetable {
case "celery":
print("Add some raisins and make ants on a log.")
case "cucumber", "watercress":
print("That would make a good tea sandwich.")
case let x where x.hasSuffix("pepper"):
print("Is it a spicy \(x)?")
default:
print("Everything tastes good in soup.")
}

While

while to repeat a block of code until a condition changes

1
2
3
4
5
6
7
8
9
10
11
var n = 2
while n < 100 {
n = n * 2
}
print(n)

var m = 2
repeat {
m = m * 2
} while m < 100
print(m)

Functions and Closures

1
2
3
4
func greet(person: String, day: String) -> String {
return "Hello \(person), today is \(day)."
}
greet(person: "Bob", day: "Tuesday")

Custom argument label

custom argument label before the parameter name, or write _ to use no argument label

1
2
3
4
func greet(_ person: String, on day: String) -> String {
return "Hello \(person), today is \(day)."
}
greet("John", on: "Wednesday")

Return multiple values by tuple

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
func calculateStatistics(scores: [Int]) -> (min: Int, max: Int, sum: Int) {
var min = scores[0]
var max = scores[0]
var sum = 0

for score in scores {
if score > max {
max = score
} else if score < min {
min = score
}
sum += score
}
return (min, max, sum)
}

// The elements of a tuple can be referred to either by name or by number.
let statistics = calculateStatistics(scores: [5, 3, 100, 3, 9])
print(statistics.sum)
print(statistics.2)

Take a variable number of arguments

1
2
3
4
5
6
7
8
9
func sumOf(numbers: Int...) -> Int {
var sum = 0
for number in numbers {
sum += number
}
return sum
}
sumOf()
sumOf(numbers: 42, 597, 12)

Nested functions

1
2
3
4
5
6
7
8
9
10
func returnFifteen() -> Int {
var y = 10
func add() {
// Nested functions have access to variables that were declared in the outer function
y += 5
}
add()
return y
}
returnFifteen()

Function as variable

Return another function as its value.

1
2
3
4
5
6
7
8
func makeIncrementer() -> ((Int) -> Int) {
func addOne(number: Int) -> Int {
return 1 + number
}
return addOne
}
var increment = makeIncrementer()
increment(7)

A function can take another function as one of its arguments.

1
2
3
4
5
6
7
8
9
10
11
12
13
func hasAnyMatches(list: [Int], condition: (Int) -> Bool) -> Bool {
for item in list {
if condition(item) {
return true
}
}
return false
}
func lessThanTen(number: Int) -> Bool {
return number < 10
}
var numbers = [20, 19, 7, 12]
hasAnyMatches(list: numbers, condition: lessThanTen)

Closure

  1. write a closure without a name by surrounding code with braces ({}). Use in to separate the arguments and return type from the body.
  2. The code in a closure has access to things like variables and functions that were available in the scope where the closure was created, even if the closure is in a different scope when it is executed
1
2
3
4
5
6
var numbers = [20, 19, 7, 12]
numbers.map({
(number: Int) -> Int in
let result = 3 * number
return result
})

Single statement closures

1
2
let mappedNumbers = numbers.map({ number in 3 * number })
print(mappedNumbers)
  1. You can refer to parameters by number instead of by name.
  2. A closure passed as the last argument to a function can appear immediately after the parentheses.
  3. When a closure is the only argument to a function, you can omit the parentheses entirely.
1
2
let sortedNumbers = numbers.sorted { $0 > $1 }
print(sortedNumbers)

Objects and Classes

1
2
3
4
5
6
7
8
9
10
class Shape {
var numberOfSides = 0
func simpleDescription() -> String {
return "A shape with \(numberOfSides) sides."
}
}

var shape = Shape()
shape.numberOfSides = 7
var shapeDescription = shape.simpleDescription()

Property value assigned is required

Every property needs a value assigned—either in its declaration (as with numberOfSides) or in the initializer (as with name).

1
2
3
4
5
6
7
8
9
10
11
12
class NamedShape {
var numberOfSides: Int = 0
var name: String

init(name: String) {
self.name = name
}

func simpleDescription() -> String {
return "A shape with \(numberOfSides) sides."
}
}

Subclasses

  1. There is no requirement for classes to subclass any standard root class, so you can include or omit a superclass as needed.
  2. Methods on a subclass that override the superclass’s implementation are marked with override
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
class Square: NamedShape {
var sideLength: Double

init(sideLength: Double, name: String) {
self.sideLength = sideLength
super.init(name: name)
numberOfSides = 4
}

func area() -> Double {
return sideLength * sideLength
}

override func simpleDescription() -> String {
return "A square with sides of length \(sideLength)."
}
}

let test = Square(sideLength: 5.2, name: "my test square")
test.area()
test.simpleDescription()

Getter and setter

Notice the init method

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
class EquilateralTriangle: NamedShape {
var sideLength: Double = 0.0

init(sideLength: Double, name: String) {
// Setting the value of properties that the subclass declares.
self.sideLength = sideLength

// Calling the superclass’s initializer
super.init(name: name)

// Changing the value of properties defined by the superclass.
numberOfSides = 3
}

var perimeter: Double {
get {
return 3.0 * sideLength
}
set {
sideLength = newValue / 3.0
}
}

override func simpleDescription() -> String {
return "An equilateral triangle with sides of length \(sideLength)."
}
}
var triangle = EquilateralTriangle(sideLength: 3.1, name: "a triangle")
print(triangle.perimeter)
triangle.perimeter = 9.9
print(triangle.sideLength)

Computed property

Computed based on the simple property

1
2
3
4
5
6
7
8
var perimeter: Double {
get {
return 3.0 * sideLength
}
set {
sideLength = newValue / 3.0
}
}

WillSet and didSet

  1. If you don’t need to compute the property but still need to provide code that is run before and after setting a new value, use willSet and didSet.
  2. The code you provide is run any time the value changes outside of an initializer.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
// For example, the class below ensures that the side length of its triangle is always the same as the side length of its square

class TriangleAndSquare {
var triangle: EquilateralTriangle {
willSet {
square.sideLength = newValue.sideLength
}
}
var square: Square {
willSet {
triangle.sideLength = newValue.sideLength
}
}
init(size: Double, name: String) {
square = Square(sideLength: size, name: name)
triangle = EquilateralTriangle(sideLength: size, name: name)
}
}

var triangleAndSquare = TriangleAndSquare(size: 10, name: "another test shape")
print(triangleAndSquare.square.sideLength)
print(triangleAndSquare.triangle.sideLength)
triangleAndSquare.square = Square(sideLength: 50, name: "larger square")
print(triangleAndSquare.triangle.sideLength)

Optional chain

  1. If the value before the ? is nil, everything after the ? is ignored and the value of the whole expression is nil.
  2. Otherwise, the optional value is unwrapped, and everything after the ? acts on the unwrapped value.
  3. In both cases, the value of the whole expression is an optional value.
1
2
let optionalSquare: Square? = Square(sideLength: 2.5, name: "optional square")
let sideLength = optionalSquare?.sideLength

Enumerations and Structures

Raw value

  1. By default, Swift assigns the raw values starting at zero and incrementing by one each time
  2. Ace is explicitly given a raw value of 1, and the rest of the raw values are assigned in order.
  3. Use the rawValue property to access the raw value of an enumeration case.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
enum Rank: Int {
case ace = 1
case two, three, four, five, six, seven, eight, nine, ten
case jack, queen, king
func simpleDescription() -> String {
switch self {
case .ace:
return "ace"
case .jack:
return "jack"
case .queen:
return "queen"
case .king:
return "king"
default:
return String(self.rawValue)
}
}
}
let ace = Rank.ace
let aceRawValue = ace.rawValue

init? (rawValue:)

Use theinit?(rawValue:)initializer to make an instance of an enumeration from a raw value

1
2
3
if let convertedRank = Rank(rawValue: 3) {
let threeDescription = convertedRank.simpleDescription()
}

No raw value

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
enum Suit {
case spades, hearts, diamonds, clubs
func simpleDescription() -> String {
switch self {
case .spades:
return "spades"
case .hearts:
return "hearts"
case .diamonds:
return "diamonds"
case .clubs:
return "clubs"
}
}
}
let hearts = Suit.hearts
let heartsDescription = hearts.simpleDescription()

Associated values

these values are determined when you make the instance, and they can be different for each instance of an enumeration case

1
2
3
4
5
6
7
8
9
10
11
12
13
14
enum ServerResponse {
case result(String, String)
case failure(String)
}

let success = ServerResponse.result("6:00 am", "8:09 pm")
let failure = ServerResponse.failure("Out of cheese.")

switch success {
case let .result(sunrise, sunset):
print("Sunrise is at \(sunrise) and sunset is at \(sunset).")
case let .failure(message):
print("Failure... \(message)")
}

Struct

One of the most important differences between structures and classes is that structures are always copied when they are passed around in your code, but classes are passed by reference.

1
2
3
4
5
6
7
8
9
struct Card {
var rank: Rank
var suit: Suit
func simpleDescription() -> String {
return "The \(rank.simpleDescription()) of \(suit.simpleDescription())"
}
}
let threeOfSpades = Card(rank: .three, suit: .spades)
let threeOfSpadesDescription = threeOfSpades.simpleDescription()

Protocols and Extensions

Protocols

Use protocol to declare a protocol.

1
2
3
4
protocol ExampleProtocol {
var simpleDescription: String { get }
mutating func adjust()
}

Classes, enumerations, and structs can all adopt protocols.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
class SimpleClass: ExampleProtocol {
var simpleDescription: String = "A very simple class."
var anotherProperty: Int = 69105

//The declaration of SimpleClass doesn’t need any of its methods marked as mutating because methods on a class can always modify the class.
func adjust() {
simpleDescription += " Now 100% adjusted."
}
}
var a = SimpleClass()
a.adjust()
let aDescription = a.simpleDescription
// ---------------------------------------------------------------------------
struct SimpleStructure: ExampleProtocol {
var simpleDescription: String = "A simple structure"
mutating func adjust() {
simpleDescription += " (adjusted)"
}
}
var b = SimpleStructure()
b.adjust()
let bDescription = b.simpleDescription

Protocol type

  1. use a protocol name just like any other named type
  2. When you work with values whose type is a protocol type, methods outside the protocol definition are not available
  3. Even though the variable protocolValue has a runtime type of SimpleClass, the compiler treats it as the given type of ExampleProtocol. This means that you can’t accidentally access methods or properties that the class implements in addition to its protocol conformance.
1
2
3
4
let protocolValue: ExampleProtocol = a
print(protocolValue.simpleDescription)

// print(protocolValue.anotherProperty) // Uncomment to see the error

Extensions

Use extension to add functionality to an existing type, such as new methods and computed properties

1
2
3
4
5
6
7
8
9
extension Int: ExampleProtocol {
var simpleDescription: String {
return "The number \(self)"
}
mutating func adjust() {
self += 42
}
}
print(7.simpleDescription)

Error Handling

represent errors using any type that adopts the Error protocol.

1
2
3
4
5
enum PrinterError: Error {
case outOfPaper
case noToner
case onFire
}

Throw & Throws

Use throw to throw an error and throws to mark a function that can throw an error. If you throw an error in a function, the function returns immediately and the code that called the function handles the error.

1
2
3
4
5
6
func send(job: Int, toPrinter printerName: String) throws -> String {
if printerName == "Never Has Toner" {
throw PrinterError.noToner
}
return "Job sent"
}

Handle error 1: Do-catch

1
2
3
4
5
6
do {
let printerResponse = try send(job: 1040, toPrinter: "Bi Sheng")
print(printerResponse)
} catch {
print(error)
}

Handle error 2: Multiple catch blocks

1
2
3
4
5
6
7
8
9
10
do {
let printerResponse = try send(job: 1440, toPrinter: "Gutenberg")
print(printerResponse)
} catch PrinterError.onFire {
print("I'll just put this over here, with the rest of the fire.")
} catch let printerError as PrinterError {
print("Printer error: \(printerError).")
} catch {
print(error)
}

Handle errors3: by try?

Another way to handle errors is to use try? to convert the result to an optional. If the function throws an error, the specific error is discarded and the result is nil. Otherwise, the result is an optional containing the value that the function returned.

1
2
let printerSuccess = try? send(job: 1884, toPrinter: "Mergenthaler")
let printerFailure = try? send(job: 1885, toPrinter: "Never Has Toner")

Defer

Use defer to write a block of code that is executed after all other code in the function, just before the function returns. The code is executed regardless of whether the function throws an error. You can use defer to write setup and cleanup code next to each other, even though they need to be executed at different times.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
var fridgeIsOpen = false
let fridgeContent = ["milk", "eggs", "leftovers"]

func fridgeContains(_ food: String) -> Bool {
fridgeIsOpen = true
defer {
fridgeIsOpen = false
}

let result = fridgeContent.contains(food)
return result
}
fridgeContains("banana")
print(fridgeIsOpen)

Generics

Write a name inside angle brackets to make a generic function or type.

1
2
3
4
5
6
7
8
func makeArray<Item>(repeating item: Item, numberOfTimes: Int) -> [Item] {
var result = [Item]()
for _ in 0..<numberOfTimes {
result.append(item)
}
return result
}
makeArray(repeating: "knock", numberOfTimes:4)

You can make generic forms of functions and methods, as well as classes, enumerations, and structures.

1
2
3
4
5
6
7
// Reimplement the Swift standard library's optional type
enum OptionalValue<Wrapped> {
case none
case some(Wrapped)
}
var possibleInteger: OptionalValue<Int> = .none
possibleInteger = .some(100)

Use where right before the body to specify a list of requirements

1
2
3
4
5
6
7
8
9
10
11
12
func anyCommonElements<T: Sequence, U: Sequence>(_ lhs: T, _ rhs: U) -> Bool
where T.Iterator.Element: Equatable, T.Iterator.Element == U.Iterator.Element {
for lhsItem in lhs {
for rhsItem in rhs {
if lhsItem == rhsItem {
return true
}
}
}
return false
}
anyCommonElements([1, 2, 3], [3])

Writing <T: Equatable> is the same as writing <T> ... where T: Equatable>.