素早いツアー

Swift.orgにあるオリジナルのA Swift Tourに修正を加えたものです。オリジナルのコンテンツは Apple Inc. によって作成され、 Creative Commons Attribution 4.0 International (CC BY 4.0) Licenseに基づいてライセンスされています。
TensorFlow.org で見る Google Colab で実行する GitHub でソースを表示

伝統的に、新しい言語での最初のプログラムでは「Hello, world!」という単語を出力する必要があるとされています。画面上。 Swift では、これを 1 行で実行できます。

print("Hello, world!")
Hello, world!

C または Objective-C でコードを書いたことがある場合は、この構文に見覚えがあるでしょう。Swift では、このコード行が完全なプログラムです。入出力や文字列処理などの機能のために別のライブラリをインポートする必要はありません。グローバル スコープで記述されたコードはプログラムのエントリ ポイントとして使用されるため、 main()関数は必要ありません。また、すべてのステートメントの最後にセミコロンを記述する必要もありません。

このツアーでは、さまざまなプログラミング タスクを実行する方法を示し、Swift でコードの作成を開始するのに十分な情報を提供します。何か理解できなくても心配する必要はありません。このツアーで紹介された内容はすべて、この本の残りの部分で詳しく説明されています。

単純な値

定数を作成するにはletを使用し、変数を作成するにはvar使用します。定数の値はコンパイル時にわかっている必要はありませんが、値を 1 回だけ割り当てる必要があります。これは、定数を使用して、一度決定した後でさまざまな場所で使用する値に名前を付けることができることを意味します。

var myVariable = 42
myVariable = 50
let myConstant = 42

定数または変数は、割り当てたい値と同じ型でなければなりません。ただし、常に型を明示的に記述する必要はありません。定数または変数を作成するときに値を指定すると、コンパイラーはその型を推測します。上記の例では、 myVariableの初期値が整数であるため、コンパイラは myVariable が整数であると推論します。

初期値では十分な情報が得られない場合 (または初期値がない場合)、変数の後にコロンで区切って型を記述します。注: 浮動小数点数にFloatの代わりにDoubleを使用すると、精度が向上します。これが Swift のデフォルトの浮動小数点型です。

let implicitInteger = 70
let implicitDouble = 70.0
let explicitDouble: Double = 70
// Experiment:
// Create a constant with an explicit type of `Float` and a value of 4.

値が暗黙的に別の型に変換されることはありません。値を別の型に変換する必要がある場合は、目的の型のインスタンスを明示的に作成します。

let label = "The width is "
let width = 94
print(label + String(width))
The width is 94

// Experiment:
// Try removing the conversion to `String` from the last line. What error do you get?

文字列に値を含めるさらに簡単な方法があります。値を括弧内に記述し、括弧の前にバックスラッシュ (``) を記述します。例えば:

let apples = 3
print("I have \(apples) apples.")
I have 3 apples.

let oranges = 5
print("I have \(apples + oranges) pieces of fruit.")
I have 8 pieces of fruit.

// Experiment:
// Use `\()` to include a floating-point calculation in a string and to include someone's name in a
// greeting.

複数行にわたる文字列には、3 つの二重引用符 ( """ ) を使用します。引用符で囲まれた各行の先頭のインデントは、閉じ引用符のインデントと一致する限り削除されます。次に例を示します。

let quotation = """
    Even though there's whitespace to the left,
    the actual lines aren't indented.
        Except for this line.
    Double quotes (") can appear without being escaped.

    I still have \(apples + oranges) pieces of fruit.
    """
print(quotation)
Even though there's whitespace to the left,
the actual lines aren't indented.
    Except for this line.
Double quotes (") can appear without being escaped.

I still have 8 pieces of fruit.

角かっこ ( [] ) を使用して配列と辞書を作成し、角かっこ内にインデックスまたはキーを記述することでそれらの要素にアクセスします。最後の要素の後にはカンマを使用できます。

var shoppingList = ["catfish", "water", "tulips", "blue paint"]
shoppingList[1] = "bottle of water"

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

要素を追加すると、配列は自動的に拡大します。

shoppingList.append("blue paint")
print(shoppingList)
["catfish", "bottle of water", "tulips", "blue paint", "blue paint"]

空の配列または辞書を作成するには、初期化子構文を使用します。

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

型情報を推論できる場合は、たとえば、変数に新しい値を設定するときや関数に引数を渡すときに、空の配列を[]として、空の辞書を[:]として書くことができます。

shoppingList = []
occupations = [:]

制御フロー

ifswitchを使用して条件文を作成し、 for - inforwhile 、およびrepeat - whileを使用してループを作成します。条件変数またはループ変数を囲むかっこはオプションです。体の周囲にブレースが必要です。

let individualScores = [75, 43, 103, 87, 12]
var teamScore = 0
for score in individualScores {
    if score > 50 {
        teamScore += 3
    } else {
        teamScore += 1
    }
}
print(teamScore)
11

ifステートメントでは、条件はブール式である必要があります。これは、 if score { ... }などのコードはエラーであり、ゼロとの暗黙の比較ではないことを意味します。

iflet一緒に使用すると、欠落している可能性のある値を処理できます。これらの値はオプションとして表されます。オプションの値には、値が含まれるか、値が欠落していることを示すnil含まれます。値をオプションとしてマークするには、値の型の後に疑問符 ( ? ) を記述します。

var optionalString: String? = "Hello"
print(optionalString == nil)
false

var optionalName: String? = "John Appleseed"
var greeting = "Hello!"
if let name = optionalName {
    greeting = "Hello, \(name)"
}
print(greeting)
Hello, John Appleseed

// Experiment:
// Change `optionalName` to `nil`. What greeting do you get?
// Add an `else` clause that sets a different greeting if `optionalName` is `nil`.

オプションの値がnilの場合、条件はfalseになり、中括弧内のコードはスキップされます。それ以外の場合、オプションの値はラップ解除され、 letの後の定数に割り当てられます。これにより、ラップ解除された値がコード ブロック内で使用できるようになります。

オプションの値を処理するもう 1 つの方法は、 ??を使用してデフォルト値を指定することです。オペレーター。オプションの値が欠落している場合は、代わりにデフォルト値が使用されます。

let nickName: String? = nil
let fullName: String = "John Appleseed"
print("Hi \(nickName ?? fullName)")
Hi John Appleseed

スイッチは、整数や等価性のテストに限定されず、あらゆる種類のデータとさまざまな比較演算をサポートします。

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.")
}
Is it a spicy red pepper?

// Experiment:
// Try removing the default case. What error do you get?

パターン内でletを使用して、パターンのその部分に一致する値を定数に割り当てる方法に注目してください。

一致する switch case 内のコードを実行した後、プログラムは switch ステートメントを終了します。実行は次のケースに続行されないため、各ケースのコードの最後にあるスイッチから明示的に抜け出す必要はありません。

for - inを使用して、各キーと値のペアに使用する名前のペアを指定して、辞書内の項目を反復処理します。ディクショナリは順序付けされていないコレクションであるため、そのキーと値は任意の順序で反復されます。

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)
25

// Experiment:
// Add another variable to keep track of which kind of number was the largest, as well as what that
// largest number was.

whileを使用して、条件が変わるまでコードのブロックを繰り返します。代わりにループの条件を最後に指定して、ループが少なくとも 1 回実行されるようにすることができます。

var n = 2
while n < 100 {
    n = n * 2
}

print(n)
128

var m = 2
repeat {
    m = m * 2
} while m < 100

print(m)
128

..<を使用してインデックスの範囲を作成するか、明示的な初期化、条件、および増分を記述することで、ループ内にインデックスを保持できます。これら 2 つのループは同じことを行います。

var total = 0
for i in 0..<4 {
    total += i
}

print(total)
6

上限値を省略した範囲を作成するには..<使用し、両方の値を含む範囲を作成するには...を使用します。

機能とクロージャ

関数を宣言するにはfuncを使用します。関数名に続けて括弧内の引数のリストを指定して、関数を呼び出します。 ->を使用して、パラメーターの名前と型を関数の戻り値の型から分離します。

func greet(name: String, day: String) -> String {
    return "Hello \(name), today is \(day)."
}
print(greet(name: "Bob", day: "Tuesday"))
Hello Bob, today is Tuesday.

// Experiment:
// Remove the `day` parameter. Add a parameter to include today’s lunch special in the greeting.

デフォルトでは、関数はパラメータ名を引数のラベルとして使用します。パラメータ名の前にカスタム引数ラベルを書き込むか、引数ラベルを使用しない場合は_を書きます。

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

タプルを使用して複合値を作成します。たとえば、関数から複数の値を返します。タプルの要素は、名前または番号で参照できます。

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)
}
let statistics = calculateStatistics(scores: [5, 3, 100, 3, 9])
print(statistics.sum)
print(statistics.2)
120
120

関数は入れ子にすることができます。ネストされた関数は、外側の関数で宣言された変数にアクセスできます。ネストされた関数を使用すると、長い関数または複雑な関数のコードを編成できます。

func returnFifteen() -> Int {
    var y = 10
    func add() {
        y += 5
    }
    add()
    return y
}
print(returnFifteen())
15

関数はファーストクラスの型です。これは、関数が別の関数を値として返すことができることを意味します。

func makeIncrementer() -> ((Int) -> Int) {
    func addOne(number: Int) -> Int {
        return 1 + number
    }
    return addOne
}
var increment = makeIncrementer()
print(increment(7))
8

関数は、引数の 1 つとして別の関数を取ることができます。

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]
print(hasAnyMatches(list: numbers, condition: lessThanTen))
true

関数は実際にはクロージャの特殊なケースであり、後で呼び出すことができるコードのブロックです。クロージャ内のコードは、実行時にクロージャが別のスコープにある場合でも、クロージャが作成されたスコープで使用可能な変数や関数などにアクセスできます。この例は、ネストされた関数ですでに見ました。コードを中括弧 ( {} ) で囲むことにより、名前なしでクロージャを作成できます。 in使用して、引数と戻り値の型を本体から分離します。

let mappedNumbers = numbers.map({ (number: Int) -> Int in
    let result = 3 * number
    return result
})
print(mappedNumbers)
[60, 57, 21, 36]

// Experiment:
// Rewrite the closure to return zero for all odd numbers.

クロージャをより簡潔に記述するためのオプションがいくつかあります。デリゲートのコールバックなど、クロージャの型がすでにわかっている場合は、パラメータの型、戻り値の型、またはその両方を省略できます。単一ステートメントのクロージャは、その唯一のステートメントの値を暗黙的に返します。

let mappedNumbers2 = numbers.map({ number in 3 * number })
print(mappedNumbers2)
[60, 57, 21, 36]

パラメータを名前ではなく番号で参照できます。この方法は、非常に短いクロージャで特に役立ちます。関数の最後の引数として渡されるクロージャーは、かっこの直後に出現できます。クロージャが関数の唯一の引数である場合は、括弧を完全に省略できます。

let sortedNumbers = numbers.sorted { $0 > $1 }
print(sortedNumbers)
[20, 19, 12, 7]

オブジェクトとクラス

クラスを作成するには、 classの後にクラス名を続けて使用します。クラス内のプロパティ宣言は、クラスのコンテキスト内にあることを除いて、定数または変数の宣言と同じ方法で記述されます。同様に、メソッドと関数の宣言も同じ方法で記述されます。

class Shape {
    var numberOfSides = 0
    func simpleDescription() -> String {
        return "A shape with \(numberOfSides) sides."
    }
}
// Experiment:
// Add a constant property with `let`, and add another method that takes an argument.

クラス名の後にかっこを付けて、クラスのインスタンスを作成します。ドット構文を使用して、インスタンスのプロパティとメソッドにアクセスします。

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

このバージョンのShapeクラスには、インスタンスの作成時にクラスを設定するための初期化子という重要な要素が欠落しています。 initを使用して作成します。

class NamedShape {
    var numberOfSides: Int = 0
    var name: String

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

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

nameプロパティとイニシャライザへのname引数を区別するためにselfどのように使用されるかに注目してください。イニシャライザへの引数は、クラスのインスタンスを作成するときに関数呼び出しのように渡されます。すべてのプロパティには、その宣言 ( numberOfSidesなど) または初期化子 ( nameなど) のいずれかで、値を割り当てる必要があります。

オブジェクトの割り当てが解除される前にクリーンアップを実行する必要がある場合は、 deinitを使用して初期化解除子を作成します。

サブクラスには、クラス名の後にコロンで区切られたスーパークラス名が含まれます。クラスが標準ルート クラスをサブクラス化する必要はないため、必要に応じてスーパークラスを含めたり省略したりできます。

スーパークラスの実装をオーバーライドするサブクラスのメソッドにはoverrideマークが付けられます。 override使用せずに偶然メソッドをオーバーライドすると、コンパイラによってエラーとして検出されます。コンパイラは、スーパークラス内のメソッドを実際にはoverrideしないオーバーライドのあるメソッドも検出します。

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")
print(test.area())
print(test.simpleDescription())
27.040000000000003
A square with sides of length 5.2.

// Experiment:
// - Make another subclass of `NamedShape` called `Circle` that takes a radius and a name as
//   arguments to its initializer.
// - Implement an `area()` and a `simpleDescription()` method on the `Circle` class.

保存される単純なプロパティに加えて、プロパティにはゲッターとセッターを含めることができます。

class EquilateralTriangle: NamedShape {
    var sideLength: Double = 0.0

    init(sideLength: Double, name: String) {
        self.sideLength = sideLength
        super.init(name: name)
        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)
9.3
3.3000000000000003

perimeterのセッターでは、新しい値には暗黙的な名前newValueが付けられます。 setの後に括弧内に明示的な名前を指定できます。

EquilateralTriangleクラスの初期化子には 3 つの異なるステップがあることに注意してください。

  1. サブクラスが宣言するプロパティの値を設定します。

  2. スーパークラスのイニシャライザを呼び出します。

  3. スーパークラスによって定義されたプロパティの値を変更します。メソッド、ゲッター、またはセッターを使用する追加のセットアップ作業もこの時点で行うことができます。

プロパティを計算する必要はないが、新しい値を設定する前後に実行されるコードを提供する必要がある場合は、 willSetおよびdidSetを使用します。提供したコードは、イニシャライザの外部で値が変更されるたびに実行されます。たとえば、以下のクラスは、三角形の辺の長さが常に正方形の辺の長さと同じであることを保証します。

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)
10.0
10.0
50.0

オプションの値を扱うときは、 ?と書くことができます。メソッド、プロパティ、添え字などの操作の前。 ?の前の値がnil?以降はすべては無視され、式全体の値はnilになります。それ以外の場合、オプションの値はラップされず、 ?以降のすべてがラップされます。アンラップされた値に作用します。どちらの場合も、式全体の値はオプションの値です。

let optionalSquare: Square? = Square(sideLength: 2.5, name: "optional square")
print(optionalSquare?.sideLength)
Optional(2.5)

列挙と構造

enumを使用して列挙を作成します。クラスや他のすべての名前付き型と同様に、列挙型にはメソッドを関連付けることができます。

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
print(ace)
let aceRawValue = ace.rawValue
print(aceRawValue)
ace
1

// Experiment:
// Write a function that compares two `Rank` values by comparing their raw values.

デフォルトでは、Swift はゼロから始まり毎回 1 ずつ増加する生の値を割り当てますが、値を明示的に指定することでこの動作を変更できます。上の例では、 Ace には生の値1が明示的に与えられ、残りの生の値が順番に割り当てられます。文字列または浮動小数点数を列挙の生の型として使用することもできます。 rawValueプロパティを使用して、列挙型ケースの生の値にアクセスします。

init?(rawValue:)イニシャライザを使用して、生の値から列挙型のインスタンスを作成します。生の値に一致する列挙型ケースを返すか、一致するRankがない場合はnilを返します。

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

列挙型のケース値は実際の値であり、生の値を別の方法で記述するものではありません。実際、意味のある生の値がない場合は、値を指定する必要はありません。

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()
// Experiment:
// Add a `color()` method to `Suit` that returns "black" for spades and clubs, and returns "red" for
// hearts and diamonds.

列挙型のHeartsケースが上記で参照されている 2 つの方法に注目してください。hearts 定数に値をhearts場合、定数には明示的な型が指定されていないため、列挙型ケースのSuit.Hearts完全名で参照されます。スイッチ内では、 selfの値がスーツであることがすでにわかっているため、列挙ケースは省略形.Heartsで参照されます。値の型がすでにわかっている場合は、いつでも省略形を使用できます。

列挙に生の値がある場合、それらの値は宣言の一部として決定されます。これは、特定の列挙ケースのすべてのインスタンスが常に同じ生の値を持つことを意味します。列挙ケースのもう 1 つの選択肢は、ケースに値を関連付けることです。これらの値はインスタンスの作成時に決定され、列挙ケースのインスタンスごとに異なる値にすることができます。関連する値は、列挙型ケース インスタンスの保存されたプロパティのように動作すると考えることができます。

たとえば、サーバーに日の出と日の入りの時刻をリクエストする場合を考えてみましょう。サーバーは、要求された情報で応答するか、何が問題だったかの説明で応答します。

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)")
}
Sunrise is at 6:00 am and sunset is at 8:09 pm.

// Experiment:
// Add a third case to `ServerResponse` and to the switch.

値をスイッチのケースと照合する一環として、 ServerResponse値から日の出と日の入りの時刻がどのように抽出されるかに注目してください。

構造体を作成するにはstructを使用します。構造体は、メソッドや初期化子など、クラスと同じ動作の多くをサポートします。構造体とクラスの最も重要な違いの 1 つは、構造体はコード内で渡されるときに常にコピーされるのに対し、クラスは参照によって渡されることです。

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()
// Experiment:
// Write a function that returns an array containing a full deck of cards, with one card of each
// combination of rank and suit.

プロトコルと拡張機能

プロトコルを宣言するには、 protocolを使用します。

protocol ExampleProtocol {
    var simpleDescription: String { get }
    mutating func adjust()
}

クラス、列挙型、および構造体はすべてプロトコルを採用できます。

class SimpleClass: ExampleProtocol {
    var simpleDescription: String = "A very simple class."
    var anotherProperty: Int = 69105
    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()
print(b.adjust())
print(b.simpleDescription)
()
A simple structure (adjusted)

// Experiment:
// Add another requirement to `ExampleProtocol`.
// What changes do you need to make to `SimpleClass` and `SimpleStructure` so that they still
// conform to the protocol?

SimpleStructureの宣言で、構造を変更するメソッドをマークするためにmutatingキーワードが使用されていることに注目してください。クラスのメソッドはいつでもクラスを変更できるため、 SimpleClassの宣言では、変更可能としてマークされたメソッドは必要ありません。

extensionを使用して、新しいメソッドや計算されたプロパティなどの機能を既存の型に追加します。拡張機能を使用すると、他の場所で宣言された型、またはライブラリやフレームワークからインポートした型にプロトコル準拠を追加できます。

extension Int: ExampleProtocol {
    public var simpleDescription: String {
        return "The number \(self)"
    }
    public mutating func adjust() {
        self += 42
    }
}
print(7.simpleDescription)
The number 7

// Experiment:
// Write an extension for the `Double` type that adds an `absoluteValue` property.

プロトコル名は、他の名前付きタイプと同様に使用できます。たとえば、タイプは異なるが、すべてが 1 つのプロトコルに準拠するオブジェクトのコレクションを作成できます。型がプロトコル型である値を操作する場合、プロトコル定義外のメソッドは使用できません。

let protocolValue: ExampleProtocol = a
print(protocolValue.simpleDescription)
A very simple class.  Now 100% adjusted.

// Uncomment to see the error.
// protocolValue.anotherProperty

変数protocolValue実行時型がSimpleClassであっても、コンパイラはそれをExampleProtocolの指定された型として扱います。これは、プロトコルへの準拠に加えてクラスが実装するメソッドやプロパティに誤ってアクセスできないことを意味します。

エラー処理

エラーは、 Errorプロトコルを採用する任意の型を使用して表します。

enum PrinterError: Error {
    case outOfPaper
    case noToner
    case onFire
}

エラーをスローするにはthrowを使用し、エラーをスローできる関数をマークするにはthrows使用します。関数でエラーをスローした場合、関数はすぐに戻り、関数を呼び出したコードがエラーを処理します。

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

エラーを処理するにはいくつかの方法があります。 1 つの方法は、 do-catchを使用することです。 doブロック内で、その前に try を記述することで、エラーをスローする可能性のあるコードをマークします。 catchブロック内では、別の名前を付けない限り、エラーには自動的にerror名前が付けられます。

do {
    let printerResponse = try send(job: 1040, toPrinter: "Bi Sheng")
    print(printerResponse)
} catch {
    print(error)
}
Job sent

// Experiment:
// Change the printer name to `"Never Has Toner"`, so that the `send(job:toPrinter:)` function
// throws an error.

特定のエラーを処理する複数のcatchブロックを提供できます。 switch のcaseの後にパターンを記述するのと同じように、 catchの後にパターンを記述します。

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)
}
Job sent

// Experiment:
// Add code to throw an error inside the `do` block.
// What kind of error do you need to throw so that the error is handled by the first `catch` block?
// What about the second and third blocks?

エラーを処理する別の方法は、 try?を使用することです。結果をオプションに変換します。関数がエラーをスローした場合、特定のエラーは破棄され、結果はnilになります。それ以外の場合、結果は関数が返した値を含むオプションです。

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

deferを使用して、関数内の他のすべてのコードの後で、関数が戻る直前に実行されるコード ブロックを記述します。コードは、関数がエラーをスローするかどうかに関係なく実行されます。 deferを使用すると、セットアップ コードとクリーンアップ コードを別の時間に実行する必要がある場合でも、並べて記述することができます。

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
}
print(fridgeContains("banana"))
print(fridgeIsOpen)
false
false

ジェネリック

山括弧内に名前を記述して、汎用関数または型を作成します。

func makeArray<Item>(repeating item: Item, numberOfTimes: Int) -> [Item] {
    var result = [Item]()
    for _ in 0..<numberOfTimes {
        result.append(item)
    }
    return result
}
print(makeArray(repeating: "knock", numberOfTimes: 4))
["knock", "knock", "knock", "knock"]

クラス、列挙、構造体だけでなく、関数やメソッドの汎用形式も作成できます。

// Reimplement the Swift standard library's optional type
enum OptionalValue<Wrapped> {
    case none
    case some(Wrapped)
}
var possibleInteger: OptionalValue<Int> = .none
possibleInteger = .some(100)
print(possibleInteger)
some(100)

型名の後ろにwhereを使用して要件のリストを指定します。たとえば、その型にプロトコルの実装を要求する、2 つの型が同じであることを要求する、またはクラスが特定のスーパークラスを持つことを要求するなどです。

func anyCommonElements<T: Sequence, U: Sequence>(_ lhs: T, _ rhs: U) -> Bool
    where T.Element: Equatable, T.Element == U.Element
{
    for lhsItem in lhs {
        for rhsItem in rhs {
            if lhsItem == rhsItem {
                return true
            }
        }
    }
    return false
}
print(anyCommonElements([1, 2, 3], [3]))
true

<T: Equatable>を記述することは<T> ... where T: Equatable記述することと同じです。