专注、坚持

Learning Swift 2.1 - Note & Code

2015.09.19 by kingcos
Preface · 序
本文为已归档的历史博客内容,其内容可能随着时间发生已经改变。
This article is archived from my previous blog, so the content maybe have changed now.。
Based on [The Swift Programming Language](https://developer.apple.com/library/ios/documentation/Swift/Conceptual/Swift_Programming_Language/) Swift 2 Edition

Intro: This document couldn’t be absolutely correct. Please tell me about the mistakes. Skip Welcome to Swift.

Info:


Language Guide


Enumerations

Enumerations in Swift are first-class types in their own right. They adopt many features traditionally supported only by classes, such as computed properties to provide additional information about the enumeration’s current value, and instance methods to provide functionality related to the values the enumeration represents. Enumerations can also define initializers to provide an initial member value; can be extended to expand their functionality beyond their original implementation; and can conform to protocols to provide standard functionality.

Enumeration Syntax && Matching Enumeration Values with a Switch Statement

In the CompassPoint example above, North, South, East and West do not implicitly equal 0, 1, 2 and 3. Instead, the different enumeration members are fully-fledged values in their own right, with an explicitly-defined type of CompassPoint.

In Swift, their names (such as CompassPoint) should start with a capital letter.

enum bestWebsites {
    case v2ex, dgtle, sspai, maimieng
}

var bw = bestWebsites.v2ex
bw = .maimieng
switch bw {
case .v2ex:
    print("Way to explore.")
case .dgtle:
    print("About digtle life.")
case .sspai:
    print("About news of iOS & Android.")
case .maimieng:
    print("My blog")
}

Associated Values

enum Apple {
    case hardware(String, Int, String)
    case software(String, Int)
}

var product2015 = Apple.hardware("iPhone", 5288, "16 G")
product2015 = .software("Apple Music", 10)

switch product2015 {
case .hardware(let productName, let price, let deviceRom):
    print("The device name is \(productName), the price is \(price), the device rom is \(deviceRom).")
case var .software(productName, feesPerMonth):
    print("The software name is \(productName), the fees per month is \(feesPerMonth)¥.")
}

// DISPLAY:
// The software name is Apple Music, the fees per month is 10¥.

Raw Values

Associated values are set when you create a new constant or variable based on one of the enumeration’s members, and can be different each time you do so.

enum platformOfProgrammingLanguage: String {
    case Android =  "Java"
    case iOS = "Swift"
}

Implicitly Assigned Raw Values & Initializing from a Raw Value

enum platformOfProgrammingLanguage: String {
    case Android
    case iOS
}

print(platformOfProgrammingLanguage.iOS.rawValue)
print(platformOfProgrammingLanguage(rawValue: "C++"))

// DISPLAY:
// iOS
// nil

Recursive Enumerations


Closures

Global functions are closures that have a name and do not capture any values. Nested functions are closures that have a name and can capture values from their enclosing function. Closure expressions are unnamed closures written in a lightweight(adj.轻便的) syntax that can capture values from their surrounding context.

Closure Expressions

let website = ["v2ex", "maimieng", "apple", "github"]
The Sort Method
func test1(s1: String, s2: String) -> Bool {
    return s1 > s2
}

print(website.sort(test1))

// DISPLAY:
// ["v2ex", "maimieng", "github", "apple"]
Closure Expression Syntax

{ (parameters) -> returnType in statements }

var p = website.sort({ (s1: String, s2: String) -> Bool in
    return s1 > s2
})
Inferring Type From Context
var p = website.sort( { s1, s2 in return s1 > s2 } )```

##### Implicit Returns from Single-Expression Closures

```swift
var p = website.sort( { s1, s2 in s1 > s2 } )
Shorthand Argument Names
var p = website.sort( { $0 > $1 } )
Operator Functions
var p = website.sort(>)

Trailing(v.跟踪) Closures

var p = website.sort() { $0 > $1 }

var q = website.sort { $0 > $1 }
let digitNames = [
    0: "Zero", 1: "One", 2: "Two",   3: "Three", 4: "Four",
    5: "Five", 6: "Six", 7: "Seven", 8: "Eight", 9: "Nine"
]
let numbers = [16, 58, 510]

var s = numbers.map {
    (var number) -> String in
    var output = ""
    while number > 0 {
        output = digitNames[number % 10]! + output
        number /= 10
    }
    return output
}

print(s)

// DISPLAY:
// ["OneSix", "FiveEight", "FiveOneZero"]

Capturing Values & Closures Are Reference Types

func makeIncrementor(forIncrement amount: Int) -> () -> Int {
    var runningTotal = 0
    func incrementor() -> Int {
        runningTotal += amount
        return runningTotal
    }
    return incrementor
}
let incrementByTen = makeIncrementor(forIncrement: 10)
let incrementBySeven = makeIncrementor(forIncrement: 7)
let alsoIncrementByTen = incrementByTen

print(makeIncrementor(forIncrement: 10)())
print(makeIncrementor(forIncrement: 10)())
print(incrementByTen())
print(incrementByTen())

print(incrementBySeven())
print(incrementBySeven())

print(incrementByTen())

print(alsoIncrementByTen())

// DISPLAY:
// 10
// 10
// 10
// 20
// 7
// 14
// 30
// 40

Autoclosures

DIFFICULT TO UNDERSTAND!

An autoclosure lets you delay evaluation, because the code inside isn’t run until you call the closure.

var customersInLine = ["Chris", "Alex", "Ewa", "Barry", "Daniella"]
let nextCustomer = { customersInLine.removeAtIndex(0) }
print(customersInLine.count)

print("Now serving \(nextCustomer())!")
print(customersInLine.count)

func serveNextCustomer(customer: () -> String) {
    print("Now serving \(customer())!")
}
serveNextCustomer( { customersInLine.removeAtIndex(0) } )
print(customersInLine.count)

func serveNextCustomer(@autoclosure customer: () -> String) {
    print("Now serving \(customer())!")
}
serveNextCustomer(customersInLine.removeAtIndex(0))
print(customersInLine.count)

var customerClosures: [() -> String] = []
func collectCustomerClosures(@autoclosure(escaping) customer: () -> String) {
    customerClosures.append(customer)
}
collectCustomerClosures(customersInLine.removeAtIndex(0))
collectCustomerClosures(customersInLine.removeAtIndex(0))

print("Collected \(customerClosures.count) closures.")

for customerClosure in customerClosures {
    print("Now serving \(customerClosure())!")
}

// DISPLAY:
// 5
// Now serving Chris!
// 4
// Now serving Alex!
// 3
// Now serving Ewa!
// 2
// Collected 2 closures.
// Now serving Barry!
// Now serving Daniella!

Functions

Defining and Calling Functions

func website(a: String) -> String {
    return "http://" + a + ".com"
}

print(website("maimieng"))

// DISPLAY:
// http://maimieng.com

Function Parameters and Return Values

Functions Without Parameters
func sayHelloWorld() -> String {
    return "hello, world"
}
print(sayHelloWorld())

// DISPLAY:
// hello, world

Functions With Multiple Parameters

When calling a function with more than one parameter, any argument after the first is labeled according to its corresponding parameter name.

func swap(a: Int, b: Int) -> (Int, Int) {
    return (b, a)
}

print(swap(1, b: 2))

// DISPLAY:
// (2, 1)
Functions Without Return Values
func spell(s: String) {
    for c in s.characters {
        print(c)
    }
}

print(spell("iOS"))

// DISPLAY:
// i
// O
// S
// ()

func count(s: String) -> Int {
    return s.characters.count
}

func c(s: String) {
    count(s)
}

print(count("Swift"))
print(c("Swift"))

// DISPLAY:
// 5
// ()
Functions with Multiple Return Values & Optional Tuple Return Types
func findMinMax(array: [Int]) -> (min: Int, max: Int)? {
    if array.isEmpty { return nil }
    var cMin = array[0]
    var cMax = array[0]
    for value in array[1..<array.count] {
        if value < cMin {
            cMin = value
        } else if value > cMax {
            cMax = value
        }
    }
    return (cMin, cMax)
}

print(findMinMax([3, 1, 4, 1, 5, 9]))
print(findMinMax([]))

// DISPLAY:
// Optional((1, 9))
// nil

Function Parameter Names

Specifying External Parameter Names

If you provide an external parameter name for a parameter, that external name must always be used when you call the function.

func sayHello(to person: String, and anotherPerson: String) -> String {
    return "Hello \(person) and \(anotherPerson)!"
}
print(sayHello(to: "Bill", and: "Ted"))

// DISPLAY:
// Hello Bill and Ted!
Omitting External Parameter Names

Because the first parameter omits its external parameter name by default, explicitly writing an underscore is extraneous(adj.无关的).

func someFunction(firstParameterName: Int, _ secondParameterName: Int) {
}
someFunction(1, 2)
Default Parameter Values
func test(a: Int = 1) {
    print(a)
}

test()
test(5)

// DISPLAY:
// 1
// 5
Variadic(adj.可变的) Parameters

A function may have at most one variadic parameter.

func total(numbers: Double...) -> Double {
    var t: Double = 0
    for n in numbers {
        t += n
    }
    return t / Double(numbers.count)
}
print(total(1, 2, 3, 4, 5))
print(total(3, 8.25, 18.75))

// DISPLAY:
// 3.0
// 10.0
Constant and Variable Parameters
func website(var s: String) -> String {
    s += ".com"
    return s
}

print(website("maimieng"))

// DISPLAY:
// maimieng.com
In-Out Parameters

In-out parameters cannot have default values, and variadic parameters cannot be marked as inout. If you mark a parameter as inout, it cannot also be marked as var or let.

func swap(inout a: Int, inout b: Int) {
    let t = a
    a = b
    b = t
}

var a = 1
var b = 2
swap(&a, &b)

print("(\(a), \(b))")

// DISPLAY:
// (2, 1)

Function Types

Using Function Types
func total(a: Int, _ b: Int) -> Int {
    return a + b
}

var t: (Int, Int) -> Int = total
var tl = total

print(t(1, 1))
print(tl(2, 2))

// DISPLAY:
// 2
// 4
Function Types as Parameter Types
func total(a: Int, _ b: Int) -> Int {
    return a + b
}

func printT(total: (Int, Int) -> Int, _ a: Int, _ b: Int) {
    print(total(a, b))
}

printT(total, 1, 1)

// DISPLAY:
// 2
Function Types as Return Types
func com(var a: String) -> String {
    a += ".com"
    return a
}

func http(a: String) -> String {
    let b = "http://" + a
    return b
}

func website(a: String) -> String {
    if !a.hasPrefix("http://") {
        return http(a)
    }
    if !a.hasSuffix(".com") {
        return com(a)
    }
    return "CORRECT"
}

print(website("http://maimieng"))

// DISPLAY:
// http://maimieng.com

Nested Functions

Nested functions are hidden from the outside world by default, but can still be called and used by their enclosing function. An enclosing function can also return one of its nested functions to allow the nested function to be used in another scope.

func website(a: String) -> String {
    
    func http(a: String) -> String {
        let b = "http://" + a
        return b
    }
    
    func com(var a: String) -> String {
        a += ".com"
        return a
    }
    
    if !a.hasPrefix("http://") {
        return http(a)
    }
    if !a.hasSuffix(".com") {
        return com(a)
    }
    return "CORRECT"
}

print(website("http://maimieng"))

// DISPLAY:
// http://maimieng.com

Control Flow

For Loops

For-In & For
// 1...9 equals to 1..<10
for i in 1...9 {
    for j in 1...9 {
        if (i >= j) {
            print("\(j) * \(i) = \(i * j) ", terminator: "")
        }
    }
    print("")
}
// DISPLAY:
// 1 * 1 = 1 
// 1 * 2 = 2 2 * 2 = 4 
// 1 * 3 = 3 2 * 3 = 6 3 * 3 = 9 
// 1 * 4 = 4 2 * 4 = 8 3 * 4 = 12 4 * 4 = 16 
// 1 * 5 = 5 2 * 5 = 10 3 * 5 = 15 4 * 5 = 20 5 * 5 = 25 
// 1 * 6 = 6 2 * 6 = 12 3 * 6 = 18 4 * 6 = 24 5 * 6 = 30 6 * 6 = 36 
// 1 * 7 = 7 2 * 7 = 14 3 * 7 = 21 4 * 7 = 28 5 * 7 = 35 6 * 7 = 42 7 * 7 = 49 
// 1 * 8 = 8 2 * 8 = 16 3 * 8 = 24 4 * 8 = 32 5 * 8 = 40 6 * 8 = 48 7 * 8 = 56 8 * 8 = 64 
// 1 * 9 = 9 2 * 9 = 18 3 * 9 = 27 4 * 9 = 36 5 * 9 = 45 6 * 9 = 54 7 * 9 = 63 8 * 9 = 72 9 * 9 = 81 
var i = 0
for _ in 1...5 {
    print(i++)
}

for var a = 0; a < 3; a++ {
    print(a)
}

// DISPLAY:
// 0
// 1
// 2
// 3
// 4
// 0
// 1
// 2

While Loops

While & Repeat-While
var i = 0
while (i++ < 3) {
    print(i)
}

var i = 0
repeat {
    print(i)
} while (i++ < 3)

// DISPLAY:
// 1
// 2
// 3
// 0
// 1
// 2
// 3

Conditional Statements

If

Omitted.

Switch
let c: Character = "i"
switch c {
    case "a", "e", "i", "o", "u":
    print("c is a vowel.")
    default:
    print("c is not a vowel.")
}
No Implicit Fallthrough

Omitted.

Interval Matching
let a = 123
switch a {
    case 1...9:
    print("a is small.")
    case 10...99:
    print("a is medium.")
    case 100...199:
    print("a is big.")
    default:
    print("a is not sure.")
}

// DISPLAY:
// a is big.
Tuples
let cs = (1, 1)
switch cs {
case (0, 0):
    print("(0, 0) is center.")
case (_, 0):
    print("(\(cs.0), 0)")
case (0, _):
    print("(0, \(cs.1))")
case (-2...2, -2...2):
    print("(\(cs.0), \(cs.1)) is inside.")
default:
    print("(\(cs.0), \(cs.1)) is not sure.")
}

// DISPLAY:
// (1, 1) is inside.
Value Bindings
let cs = (2, 1)
switch cs {
case (let x, 0):
    print("\(x)")
case (0, let y):
    print("\(y)")
case let (x, y):
    print("(\(x), \(y))")
}

// DISPLAY:
// (2, 1)
Where
let cs = (1, -1)
switch cs {
case let (x, y) where x == y:
    print("(\(x), \(y)) x == y")
case let (x, y) where x == -y:
    print("(\(x), \(y)) x == -y")
case let (x, y):
    print("(\(x), \(y))")
}

// DISPLAY:
// (1, -1) x == -y

Control Transfer Statements

Continue & Break

Like C, omitted.

Fallthrough

Like C.

let i = 5
var d = "The number \(i) is"
switch i {
case 2, 3, 5, 7, 11, 13, 17, 19:
    d += " a prime number, and also"
    fallthrough
default:
    d += " an integer."
}
print(d)

// DISPLAY:
// The number 5 is a prime number, and also an integer.
Labeled Statements

label name: while condition { statements } in switch & while

Early Exit

func test() {
    let d = ["hello": "hi"]
    guard d.isEmpty else {
        print("c isn't empty.")
        return
    }
}
test()

// DISPLAY:
// c isn't empty.

Checking API Availability

3

if #available(iOS 9, *) {
    print("Yes")
} else {
    print("No")
}

// DISPLAY:
// Yes

Collection Types

1

Swift provides three primary collection types, known as arrays, sets, and dictionaries, for storing collections of values.

  1. Arrays are ordered collections of values.
  2. Sets are unordered collections of unique values.
  3. Dictionaries are unordered collections of key-value associations.

Arrays, sets, and dictionaries in Swift are always clear about the types of values and keys that they can store. This means that you cannot insert a value of the wrong type into a collection by mistake. It also means you can be confident about the type of values you will retrieve from a collection.

Swift’s array, set, and dictionary types are implemented as generic(adj.范型) collections.

Mutability(n.易变性) 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 is 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.

It is good practice to create immutable collections in all cases where the collection does not need to change. Doing so enables the Swift compiler to optimize the performance of the collections you create.

Arrays

Swift’s Array type is bridged to Foundation’s NSArray class.

Array Type Shorthand(n.速记) Syntax

Array<Element> & [Element]

Creating an Empty Array
var a = [Int]()
print(a.count)

a.append(3)
print(a)
a = []
print(a,count)

// DISPLAY:
// 0
// [3]
// 0
Creating an Array with a Default Value
var a = [Double](count: 3, repeatedValue: 0)
print(a)

// DISPLAY:
// [0.0, 0.0, 0.0]
Creating an Array by Adding Two Arrays Together
var a = [Double](count: 3, repeatedValue: 0)
var b = [Double](count: 2, repeatedValue: 1.5)
var c = b + a
print(c)

// DISPLAY:
// [1.5, 1.5, 0.0, 0.0, 0.0]
Creating an Array with an Array Literal
var s: [String] = ["mai", "mieng"]
var str = ["mai", "mieng"]
if s == str {
    print("s == str")
}

// DISPLAY:
// s == str
Accessing and Modifying an Array
var s: [String] = ["mai", "mieng"]

print(s.count)
print(s.isEmpty)

s.append(".com")
s += ["v2ex", ".com"]

s[3] = "sspai"
print(s[3])

s[3...4] = []
print(s)

s.insert("http://", atIndex: 0)
print(s)


print(s.removeAtIndex(0))
print(s.removeLast())

print(s)

// DISPLAY:
// 2
// false
// sspai
// ["mai", "mieng", ".com"]
// ["http://", "mai", "mieng", ".com"]
// http://
// .com
// ["mai", "mieng"]
Iterating(v.重复) Over an Array
var s: [String] = ["mai", "mieng"]

for str in s {
    print(str)
}

for (index, value) in s.enumerate() {
    print("(\(index), \(value))")
}

// DISPLAY:
// mai
// mieng
// (0, mai)
// (1, mieng)

Sets

A set stores distinct values of the same type in a collection with no defined ordering. You can use a set instead of an array when the order of items is not important, or when you need to ensure that an item only appears once.

Swift’s Set type is bridged to Foundation’s NSSet class.

Hash Values for Set Types

Omitted.

Set Type Syntax

Set<Element>

Creating and Initializing an Empty Set
var s = Set<Character>()
print(s.count)

s.insert("!")
print(s.count)

s = []
print(s.count)

// DISPLAY:
// 0
// 1
// 0
Creating a Set with an Array Literal
var s: Set<Int> = [1, 2, 3, 4]
var set: Set = ["mai", "mieng"]
Accessing and Modifying a Set
var s: Set = [1, 2, 3, 4]
print(s.count)

if !s.isEmpty {
    print("s is empty.");
}

s.insert(5)
print(s.count)

print(s.remove(3))

print(s.remove(3))

s.removeAll()
print(s.count)

if s.contains(3) {
    print("s contains 3.")
} else {
    print("s doesn't contain 3.")
}

// DISPLAY:
// 4
// s is empty.
// 5
// Optional(3)
// nil
// 0
// s doesn't contain 3
Iterating Over a Set
var s: Set = [1, 2, 3, 4]

for set in s {
    print("\(set)")
}

for set in s.sort() {
    print("\(set)")
}

// DISPLAY:
// 2
// 3
// 1
// 4
// 1
// 2
// 3
// 4

Performing Set Operations

Fundamental Set Operations

2

var a: Set = [1, 2, 3, 4, 5]
var b: Set = [2, 4, 6]
var c: Set = [1, 3, 5]

print(a.exclusiveOr(b).sort())
print(a.intersect(c).sort())
print(b.union(c).sort())

print(a.subtract(b).sort())
print(b.subtract(a).sort())

// DISPLAY:
// [1, 3, 5, 6]
// [1, 3, 5]
// [1, 2, 3, 4, 5, 6]
// [1, 3, 5]
// [6]
Set Membership and Equality
var a: Set = ["😂", "😊"]
var b: Set = ["😊", "😄"]
var c: Set = ["😂"]

print(a == b)
print(c.isSubsetOf(a))
print(a.isSupersetOf(a))

print(c.isDisjointWith(b))
print(c.isStrictSubsetOf(a))
print(a.isStrictSubsetOf(a))

print(a.isStrictSupersetOf(c))
print(a.isStrictSupersetOf(a))

// DISPLAY:
// false
// true
// true
// true
// true
// false
// true
// false

Dictionaries

Swift’s Dictionary type is bridged to Foundation’s NSDictionary class.

Dictionary Type Shorthand Syntax

Dictionary<Key, Value> & [Key: Value]

Creating an Empty Dictionary
var d = [Int: String]()

d[404] = "not found"
print(d.count)

d = [:]
print(d.count)

// DISPLAY:
// 1
// 0
Creating a Dictionary with a Dictionary Literal
var d: [Int: String] = [404: "not found", 502: "bad gateway"]
var dy = ["height": 180, "weight": 60]
Accessing and Modifying a Dictionary
var dy = ["height": 180, "weight": 60]

print(dy.count)

dy["wage"] = 0
print(dy)

dy["height"] = 185
print(dy)

print(dy.updateValue(65, forKey: "weight"))

dy["wage"] = nil
print(dy)

print(dy.removeValueForKey("age"))

// DISPLAY:
// 2
// ["height": 180, "weight": 60, "wage": 0]
// ["height": 185, "weight": 60, "wage": 0]
// Optional(60)
// ["height": 185, "weight": 65]
// nil
Iterating Over a Dictionary

To iterate over the keys or values of a dictionary in a specific order, use the sort() method on its keys or values property.

var dy = ["age": 20, "height": 180, "weight": 60, "wage": 0]

for (des, value) in dy {
    print("(" + des + ", \(value))")
}

for des in dy.keys {
    print(des)
}

for value in dy.values {
    print(value)
}

var dyDes = [String](dy.keys)
var dyValue = [Int](dy.values)

// DISPLAY:
// (height, 180)
// (age, 20)
// (weight, 60)
// (wage, 0)
// height
// age
// weight
// wage
// 180
// 20
// 60
// 0

// ---
for d in dy.enumerate() {
    print(d)
}
// ---
// DISPLAY:
// (0, ("height", 180))
// (1, ("age", 20))
// (2, ("weight", 60))
// (3, ("wage", 0))

Strings and Characters

Swift’s String type is bridged with Foundation’s NSString class. If you are working with the Foundation framework in Cocoa, the entire NSString API is available to call on any String value you create when type cast to NSString, as described in AnyObject. You can also use a String value with any API that requires an NSString instance.

String Literals(n.字面量)

let testString = "maimieng"

Initializing an Empty String

var s = ""
var str = String()
s = "hello"
if Str.isEmpty {
    Str = "hi"
}
print(s + " " + str)

var Str: String
Str = "" // Initialize
if Str.isEmpty {
    Str = "Hi!"
}
print(Str)
// DISPLAY:
// hello hi
// Hi!

String Mutability(n.易变性)

var myWebsite = "maimieng"
myWebsite += ".com"

Strings Are Value Types

Behind the scenes, Swift’s compiler optimizes(v.优化) string usage so that actual copying takes place only when absolutely necessary. This means you always get great performance when working with strings as value types.

Working with Characters

for c in "Laugh: 😂".characters {
    print(c)
}
let testCharacter: Character = "."
var charArray: [Character] = ["s", "w", "i", "f", "t"]
print(charArray)
print(String(charArray))
// DISPLAY:
// L
// a
// u
// g
// h
// :
//  
// 😂
// ["s", "w", "i", "f", "t"]
// swift

Concatenating(n.连接) Strings and Characters

You can’t append a String or Character to an existing Character variable, because a Character value must contain a single character only.

var a = "mai"
var b = "mieng"
var c: Character = "."
var d = a + b
a += b
d.append(c)
print(a)
print(d)
// DISPLAY:
// maimieng
// maimieng.

String Interpolation(n.插入)

The expressions you write inside parentheses(n.圆括号) within an interpolated(adj.插入的) string cannot contain an unescaped double quote (") or backslash (\), and cannot contain a carriage return(回车) or line feed(换行).

let multiplier = 3
let message = "\(multiplier) times 2.5 is \(Double(multiplier) * 2.5)"
print(message)
// DISPLAY:
// 3 times 2.5 is 7.5

Unicode

Swift’s String and Character types are fully Unicode-compliant, as described in this section.

Unicode Scalars(n.标量)

Behind the scenes, Swift’s native String type is built from Unicode scalar values. A Unicode scalar is a unique(adj.唯一的) 21-bit number for a character or modifier(n.修饰符), such as U+0061 for LATIN(adj.拉丁的) SMALL LETTER A ("a"), or U+1F425 for FRONT-FACING BABY CHICK ("🐥").

NOTE A Unicode scalar is any Unicode code point in the range U+0000 to U+D7FF inclusive(adj.包括的) or U+E000 to U+10FFFF inclusive. Unicode scalars do not include the Unicode surrogate(n.替代品) pair code points, which are the code points in the range U+D800 to U+DFFF inclusive.

Note that not all 21-bit Unicode scalars are assigned to a character—some scalars are reserved for future assignment. Scalars that have been assigned to a character typically also have a name, such as LATIN SMALL LETTER A and FRONT-FACING BABY CHICK in the examples above.

Special Characters in String Literals

String literals can include the following special characters:

  • The escaped special characters \0 (null character), \\ (backslash), \t (horizontal(adj.水平的) tab), \n (line feed), \r (carriage return), \" (double quote) and \' (single quote)
  • An arbitrary(adj.武断的) Unicode scalar, written as \u{n}, where n is a 1–8 digit hexadecimal number with a value equal to a valid Unicode code point

The code below shows four examples of these special characters. The wiseWords constant contains two escaped double quote characters. The dollarSign, blackHeart, and sparklingHeart constants demonstrate the Unicode scalar format:

let wiseWords = "\"Imagination is more important than knowledge\" - Einstein"
let dollarSign = "\u{24}"
let blackHeart = "\u{2665}"
let sparklingHeart = "\u{1F496}"
print(wiseWords)
print(dollarSign)
print(blackHeart)
print(sparklingHeart)
// DISPLAY:
// "Imagination is more important than knowledge" - Einstein
// $
// ♥
// 💖
Extended Grapheme(n.字形) Clusters(n.群集)

Every instance of Swift’s Character type represents a single extended grapheme cluster. An extended grapheme cluster is a sequence of one or more Unicode scalars that (when combined) produce a single human-readable character.

Here’s an example. The letter é can be represented as the single Unicode scalar é (LATIN SMALL LETTER E WITH ACUTE(n.重读), or U+00E9). However, the same letter can also be represented as a pair of scalars—a standard letter e (LATIN SMALL LETTER E, or U+0065), followed by the COMBINING ACUTE ACCENT scalar (U+0301). The COMBINING ACUTE ACCENT scalar is graphically applied to the scalar that precedes(v.优先) it, turning an e into an é when it is rendered(v.成为) by a Unicode-aware text-rendering system.

In both cases, the letter é is represented as a single Swift Character value that represents an extended grapheme cluster. In the first case, the cluster contains a single scalar; in the second case, it is a cluster of two scalars:

let eAcute: Character = "\u{E9}"
let combinedEAcute: Character = "\u{65}\u{301}"
print(eAcute)
print(combinedEAcute)
// DISPLAY:
// é
// é

Extended grapheme clusters are a flexible way to represent many complex script characters as a single Character value. For example, Hangul(n.韩语) syllables(n.音节) from the Korean alphabet can be represented as either a precomposed(adj.预先构成的) or decomposed(adj.已分解的) sequence. Both of these representations qualify as a single Character value in Swift:

let precomposed: Character = "\u{D55C}"
let decomposed: Character = "\u{1112}\u{1161}\u{11AB}"
print(precomposed)
print(decomposed)
// DISPLAY:
// 한
// 한

Extended grapheme clusters enable scalars for enclosing marks (such as COMBINING ENCLOSING CIRCLE, or U+20DD) to enclose other Unicode scalars as part of a single Character value:

let enclosedEAcute: Character = "\u{E9}\u{20DD}"
print(enclosedEAcute)
// DISPLAY:
// é⃝

Unicode scalars for regional indicator symbols can be combined in pairs to make a single Character value, such as this combination of REGIONAL INDICATOR SYMBOL LETTER U (U+1F1FA) and REGIONAL INDICATOR SYMBOL LETTER S (U+1F1F8):

let regionalIndicatorForUS: Character = "\u{1F1FA}\u{1F1F8}"
print(regionalIndicatorForUS)
// DISPLAY:
// 🇺🇸

Counting Characters

Note that Swift’s use of extended grapheme clusters for Character values means that string concatenation and modification may not always affect a string’s character count.

NOTE Extended grapheme clusters can be composed of one or more Unicode scalars. This means that different characters—and different representations of the same character—can require different amounts of memory to store. Because of this, characters in Swift do not each take up the same amount of memory within a string’s representation. As a result, the number of characters in a string cannot be calculated without iterating(v.遍历) through the string to determine its extended grapheme cluster boundaries. If you are working with particularly long string values, be aware that the characters property must iterate over the Unicode scalars in the entire string in order to determine the characters for that string.

The count of the characters returned by the characters property is not always the same as the length property of an NSString that contains the same characters. The length of an NSString is based on the number of 16-bit code units within the string’s UTF-16 representation and not the number of Unicode extended grapheme clusters within the string.

let myWebsite = "我的网址是:maimieng.com 😂"
print(myWebsite.characters.count)
// DISPLAY:
// 20

var enclosedEAcute = "\u{E9}"
print(enclosedEAcute.characters.count)
enclosedEAcute += "\u{20DD}"
print(enclosedEAcute.characters.count)
// DISPLAY:
// 1
// 1

Accessing and Modifying a String

String Indices(n.索引)
let myWebsite = "maimieng.com"
print(myWebsite[myWebsite.startIndex])
print(myWebsite[myWebsite.endIndex.predecessor()])
print(myWebsite[myWebsite.startIndex.successor()])

var index = myWebsite.startIndex.advancedBy(8)
print(myWebsite[index])

for index in myWebsite.characters.indices {
    print("\(myWebsite[index])", terminator: "_")
}

// DISPLAY:
// m
// m
// a
// .
// m_a_i_m_i_e_n_g_._c_o_m_

// INCORRECT CODE:
// print(myWebsite[myWebsite.endIndex - 1])
// The 'endIndex' isn't type 'int' but 'index'.
// print(myWebsite[myWebsite.endIndex])
// print(myWebsite[myWebsite.startIndex.predecessor()])
// Runtime error: Beyond the string's range.
Inserting and Removing
var myWebsite = "maimiengcom"
myWebsite.insert(".", atIndex: myWebsite.startIndex.advancedBy(8))
print(myWebsite)

myWebsite.insertContentsOf("http://".characters, at: myWebsite.startIndex)
print(myWebsite)

myWebsite.removeAtIndex(myWebsite.endIndex.advancedBy(-4))
print(myWebsite)

let range = myWebsite.endIndex.advancedBy(-3)..<myWebsite.endIndex
myWebsite.removeRange(range)
print(myWebsite)

// DISPLAY:
// maimieng.com
// http://maimieng.com
// http://maimiengcom
// http://maimieng

Comparing Strings

String and Character Equality

String and character comparisons in Swift are not locale-sensitive(adj.本地敏感的).(意思是 Swift 中的字符和字符串无论在哪个语言区域,其表现和功能都是相同的)

== & !=:

var a = "hello"
var b = "hello"

if a == b {
    print("a == b")
}
// DISPLAY:
// a == b

Two String values (or two Character values) are considered equal if their extended grapheme clusters are canonically(adv.按标准地) equivalent. Extended grapheme clusters are canonically equivalent if they have the same linguistic(adj.语言学的) meaning and appearance, even if they are composed from different Unicode scalars behind the scenes.

For example, LATIN SMALL LETTER E WITH ACUTE (U+00E9) is canonically equivalent to LATIN SMALL LETTER E (U+0065) followed by COMBINING ACUTE ACCENT (U+0301). Both of these extended grapheme clusters are valid ways to represent the character é, and so they are considered to be canonically equivalent:

let eAcuteQuestion = "\u{E9}"

let combinedEAcuteQuestion = "\u{65}\u{301}"

if eAcuteQuestion == combinedEAcuteQuestion {
    print(eAcuteQuestion + " == " + combinedEAcuteQuestion)
}
// DISPLAY:
// é == é

Conversely, LATIN CAPITAL LETTER A (U+0041, or "A"), as used in English, is not equivalent to CYRILLIC CAPITAL LETTER A (U+0410, or "А"), as used in Russian. The characters are visually similar, but do not have the same linguistic meaning:

let latinCapitalLetterA:Character = "\u{41}"

let cyrillicCapitalLetterA:Character = "\u{0410}"

if latinCapitalLetterA != cyrillicCapitalLetterA {
    var a = "", b = ""
    a.append(cyrillicCapitalLetterA)
    b.append(latinCapitalLetterA)
    print(a + " != " + b)
}
// DISPLAY:
// А != A
Prefix and Suffix Equality

The hasPrefix(_:) and hasSuffix(_:) methods perform a character-by-character canonical equivalence comparison between the extended grapheme clusters in each string.

let iPhone = [
    "2007: iPhone",
    "2008: iPhone 3G",
    "2009: iPhone 3GS",
    "2010: iPhone 4",
    "2011: iPhone 4s",
    "2012: iPhone 5",
    "2013: iPhone 5c",
    "2013: iPhone 5s",
    "2014: iPhone 6",
    "2014: iPhone 6 Plus",
    "2015: iPhone 6s",
    "2015: iPhone 6s Plus"
]

var hasC = 0
var in2015 = 0
for i in iPhone {
    if i.hasSuffix("c") {
        hasC++
    }
    if i.hasPrefix("2015") {
        in2015++
    }
}
print(hasC)
print(in2015)

// DISPLAY:
// 1
// 2

Unicode Representations of Strings

Omitted.


Basic Operators

Terminology(n.术语)

Unary(adj.一元的), binary(adj.二元的), or ternary(adj.三元的) operators.

int a = 10
int b = -a

a ? b : c

Assignment Operator

Assignment operator does not return a value.

let a = 10
var v = 5
v = a

let (x, y) = (1, 2)
print("(\(x), \(y))")
// DISPLAY:
// (1, 2)

// if (x = y) {
//  ... 
// }
// Bug: 
// x = y does not return a value.

Arithmetic(n.算术) Operators

let a = 1
let b = 5

var c: Int
c = a + b
print(c)

c = a - b
print(c)

c = a * b
print(c)

var d = Double(a) / Double(b)
print(d)
// DISPLAY:
// 6
// -4
// 5
// 0.2
Remainder(n.求余) Operator

To determine the answer for a % b, the % operator calculates the following equation and returns remainder as its output:

a = (b x some multiplier) + remainder

var a = 2
var b = 5
var c = -4

print(a % b)
print(c % b)

// DISPLAY:
// 2
// -4
Floating-Point Remainder Calculations
var d = 0.3
var e = 0.8

print(e % d)
// DISPLAY:
// 0.2
Increment and Decrement Operators
var a = 1
print(a++)
print(++a)
// DISPLAY:
// 1
// 3
Unary Minus Operator & Unary Plus Operator
var a = -1
a = -a
print(a)
print(+a)
// DISPLAY:
// 1
// 1

Compound(adj.复合的) Assignment Operators

print(1 == 5)
print(1 != 5)
print(1 >= 5)
print(1 <= 5)
print(2 > 3)
print(2 < 3)
// DISPLAY:
// false
// true
// false
// true
// false
// true

Ternary Conditional Operator

let a = true
let b = a ? 1 : 0
print(b)

let c: Int
if a {
    c = 1
} else {
    c = 0
}
print(c)
// DISPLAY:
// 1
// 1

Nil Coalescing(n.合并) Operator

The nil coalescing operator (a ?? b) unwraps an optional a if it contains a value, or returns a default value b if a is nil.

The expression a is always of an optional type. The expression b must match the type that is stored inside a.

let defaultGreeting = "Hello!"
var myGreeting: String?
print(myGreeting ?? defaultGreeting)
myGreeting = "Hi!"
print(myGreeting ?? defaultGreeting)
// DISPLAY:
// Hello!
// Hi!

Range Operators

Closed Range Operator & Half-Open Range Operator

for-in loop:

The closed range operator (a...b) defines a range that runs from a to b, and includes the values a and b. The half-open range operator (a..<b) defines a range that runs from a to b, but does not include b.

// 1...9 equals to 1..<10
for i in 1...9 {
    for j in 1...9 {
        if (i >= j) {
            print("\(j) * \(i) = \(i * j) ", terminator: "")
        }
    }
    print("")
}
// DISPLAY:
// 1 * 1 = 1 
// 1 * 2 = 2 2 * 2 = 4 
// 1 * 3 = 3 2 * 3 = 6 3 * 3 = 9 
// 1 * 4 = 4 2 * 4 = 8 3 * 4 = 12 4 * 4 = 16 
// 1 * 5 = 5 2 * 5 = 10 3 * 5 = 15 4 * 5 = 20 5 * 5 = 25 
// 1 * 6 = 6 2 * 6 = 12 3 * 6 = 18 4 * 6 = 24 5 * 6 = 30 6 * 6 = 36 
// 1 * 7 = 7 2 * 7 = 14 3 * 7 = 21 4 * 7 = 28 5 * 7 = 35 6 * 7 = 42 7 * 7 = 49 
// 1 * 8 = 8 2 * 8 = 16 3 * 8 = 24 4 * 8 = 32 5 * 8 = 40 6 * 8 = 48 7 * 8 = 56 8 * 8 = 64 
// 1 * 9 = 9 2 * 9 = 18 3 * 9 = 27 4 * 9 = 36 5 * 9 = 45 6 * 9 = 54 7 * 9 = 63 8 * 9 = 72 9 * 9 = 81 

Logical Operators

Logical NOT Operator
let a = true
if !a {
    print("true")
} else {
    print("false")
}
// DISPLAY:
// false
Logical AND Operator & Logical OR Operator
let a = true
let b = false

if a && b {
    print("true")
} else if (a || b) {
    print("false")
}
// DISPLAY:
// false
Combining Logical Operators

The Swift logical operators && and || are left-associative, meaning that compound expressions with multiple logical operators evaluate the leftmost subexpression first.

let a = true
let b = !a // false
let c = a && b // false
let d = a || b // true
if a && b || c && d {
    print("true")
} else {
    print("false")
}
// DISPLAY:
// false
Explicit(adj.明确的) Parentheses(n.小括号)
let a = true
let b = !a // false
let c = a && b // false
let d = a || b // true
if (a || b) && (c || d) {
    print("true")
} else {
    print("false")
}
// DISPLAY:
// true

The Basics

Constants and Variables

Declaring Constants and Variables
var i = 3
var Pi = 3.14, c = "V"

let myWebsite = "maimieng.com"
Type Annotations
var sumOfStu: Int
var newColor, silver, gold, space_gray: String
newColor = "Rose Gold"
Naming Constants and Variables
let π = 3.14
let 😂 = "大笑"
var 网站 = "maimieng"

网站 += ".com"

let `let` = 1

// INCORRECT CODE:
// π = 3
// R: π is a constant that can't change its value.
Printing Constants and Variables
var myMac = "MacBook Pro"
let myMacSize = 13
print(myMac + " \(myMacSize)")
// DISPLAY:
// MacBook Pro 13

Comments

// Comment of one line.

/* Comment around several lines. */

Semicolons

let Sem = ";"; print(Sem)
// DISPLAY:
// ;

Integers

Integer Bounds(n.范围)
let minValue = Int64.min
let maxValue = UInt8.max
Int

On a 32-bit platform, Int is the same size as Int32. On a 64-bit platform, Int is the same size as Int64.

UInt

On a 32-bit platform, UInt is the same size as UInt32. On a 64-bit platform, UInt is the same size as UInt64.

Int is preferred.

Floating-Point Numbers

Double represents a 64-bit floating-point number. Float represents a 32-bit floating-point number.

Double is preferred.

Double has a precision(n.明确) of at least 15 decimal digits. Float can be as little as 6 decimal digits.

Type Safety and Type Inference

let myPi = 3 + 0.14
// myPi is a double value.

Numeric(adj.数值型的) Literals

A decimal number, with no prefix A binary number, with a 0b prefix An octal(adj.八进制的) number, with a 0o prefix A hexadecimal(adj.十六进制的) number, with a 0x prefix

var myAge = 20
var binaryAge = 0b10100
var octalAge = 0o24
var hexadecimalAge = 0x14

let velocityOfLight = 3e9
let testNum = 0xFp-2
// testNum = 3.75

var moreThanOneThousand = 001_000.102_4

Numeric Type Conversion

Integer Conversion
// INCORRECT CODE:
// var tooBig: UInt8 = UInt8.max + 1
// R: Beyond the range of UInt8.

var a: Int16 = 250
var b: Int8 = -1
var c = a + Int16(b)

// INCORRECT CODE:
// var d = a + b
// R: a & b are not the same type.
// var e = Int8(a) + b
// R: It's not safe to convert type from Int16 to Int8.
Integer and Floating-Point Conversion
var a = 0.5
var b = 2
var c = a + Double(b)
// c's value is 2.5.
var d = Int(a) + b
// d's value is 2.
var e = 2 + 0.5
// INCORRECT CODE:
// var f = a + b
// R: a & b are not the same type.

Type Aliases

typealias ElementType = Int
var a: ElementType
a = 5

Booleans

let iAmAGoodMan = true
if iAmAGoodMan {
    print("You, too.")
} else {
    print("You're wrong.")
}
if 2 >= 1 {
 print("We are good at math.")
}
// INCORRECT CODE:
// if 1 {
//  ...
// }
// R: The boolean values are only "true" & "false".

Tuples

let http404Error = (404, "Not Found")
let (statusCode, _) = http404Error
var stuInfo = (stuNum: 1415925, stuName: "V")
print(http404Error.0)
print(statusCode)
print("The status code is \(statusCode).")
print("The student number is \(stuInfo.stuNum).")
// DISPLAY:
// 404
// 404
// The status code is 404.
// The student number is 1415925.

Optionals

nil(n.无)
var moneyOfMine: Int? = 500
moneyOfMine = nil
If Statements and Forced Unwrapping

We can access its underlying(n.位于其下) value by adding an exclamation(n.感叹) mark (!) to the end of the optional’s name.

var mySaving: Int? = 1000
if mySaving != nil {
    print("I still have ¥\(mySaving!).")
} else {
    print("My saving is nil.")
}
// DISPLAY:
// I still have ¥ 1000.
Optional Binding
let possibleNumber = "123"
let impossibleNumber = "maimieng"
if let actualNumber = Int(possibleNumber) {
    print("\(possibleNumber) has an integer value.")
}
if let actualNumber = Int(impossibleNumber) {
    print("\(impossibleNumber) has an integer value.")
} else {
    print("\(impossibleNumber) could not be converted to an integer.")
}
// DISPLAY:
// 123 has an integer value.
// maimieng could not be converted to an integer.

if let firstNumber = Int("4"), secondNumber = Int("42") where firstNumber < secondNumber {
    print("\(firstNumber) < \(secondNumber)")
}
// DISPLAY:
// 4 < 42

We can include multiple optional bindings in a single if statement and use a where clause(n.分句) to check for a Boolean condition.

Implicitly Unwrapped Optionals
let possibleString: String? = "An optional string."
let forcedString: String = possibleString!

let assumedString: String! = "An implicitly unwrapped optional string."
let implicitString: String = assumedString

if possibleString != nil {
    print(possibleString!)
}

if assumedString != nil {
    print(assumedString)
}
// DISPLAY:
// An optional string.
// An implicitly unwrapped optional string.

if let definiteString = possibleString {
    print(definiteString)
}

if let definiteString = assumedString {
    print(definiteString)
}
// DISPLAY:
// An optional string.
// An implicitly unwrapped optional string.

Do not use an implicitly(adv.含蓄地) unwrapped(v.解包) optional when there is a possibility of a variable becoming nil at a later point. Always use a normal optional type if you need to check for a nil value during the lifetime of a variable.

Error Handling

If no error is thrown(v.抛出), the eatASandwich() function is called. If an error is thrown and it matches the Error.OutOfCleanDishes case, then the washDishes() function will be called. If an error is thrown and it matches the Error.MissingIngredients case, then the buyGroceries(_:) function is called with the associated [String] value captured(v.捕获) by the catch pattern.

func makeASandwich() throws {
    // ...
}

do {
    try makeASandwich()
    eatASandwich()
} catch Error.OutOfCleanDishes {
    washDishes()
} catch Error.MissingIngredients(let ingredients) {
    buyGroceries(ingredients)
}
// This code is just an example.

A do statement creates a new containing scope, which allows errors to be propagated(v.传送) to one or more catch clauses.

Assertions(n.断言)

Debugging with Assertions
let myAge = -1
assert(myAge >= 0, "Age should be bigger t WRONG han 0.")
// Bug: 
// assertion failed: Age should be bigger than 0.: file /Users/MaiMieng/Documents/test1/main.swift, line 12
(lldb) 
When to Use Assertions

An integer subscript(adj.下标的) index is passed to a custom subscript implementation, but the subscript index value could be too low or too high. A value is passed to a function, but an invalid value means that the function cannot fulfill(v.实现) its task. An optional value is currently nil, but a non-nil value is essential(adj.基本的) for subsequent(adj.随后的) code to execute(v.运行) successfully.