このページは Cloud Translation API によって翻訳されました。
Switch to English

スウィフトツアー

Swift.orgのオリジナルのA Swiftツアーを変更して適応 。オリジナルのコンテンツはApple Inc.によって作成されました。CreativeCommons 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を使用letて定数を作成し、 varを使用して変数を作成します。定数の値はコンパイル時に既知である必要はありませんが、値を1回だけ割り当てる必要があります。つまり、定数を使用して、一度決定した値を多くの場所で使用することができます。

var myVariable = 42
myVariable = 50
let myConstant = 42

定数または変数は、割り当てたい値と同じ型でなければなりません。ただし、常に型を明示的に記述する必要はありません。定数または変数を作成するときに値を指定すると、コンパイラーはその型を推測できます。上記の例では、コンパイラーは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
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
"I have \(apples) apples."
"I have 3 apples."

let oranges = 5
"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"
occupations
▿ 3 elements
  ▿ 0 : 2 elements

    - key : "Malcolm"
    - value : "Captain"
  ▿ 1 : 2 elements
    - key : "Kaylee"
    - value : "Mechanic"
  ▿ 2 : 2 elements
    - key : "Jayne"
    - value : "Public Relations"

要素を追加すると、配列は自動的に大きくなります。

shoppingList.append("blue paint")
shoppingList
▿ 5 elements

  - 0 : "catfish"
  - 1 : "bottle of water"
  - 2 : "tulips"
  - 3 : "blue paint"
  - 4 : "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
    }
}
teamScore
11

ifステートメントでは、条件式はブール式である必要があります。つまり、 if score { ... }がエラーであり、暗黙的にゼロと比較されることはありません。

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

var optionalString: String? = "Hello"
optionalString == nil
false

var optionalName: String? = "John Appleseed"
var greeting = "Hello!"
if let name = optionalName {
    greeting = "Hello, \(name)"
}
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後に定数に割り当てられます。これにより、ラップ解除された値がコードブロック内で使用可能になります。

オプションの値を処理する別の方法は、 ??オペレーター。オプションの値がない場合は、代わりにデフォルト値が使用されます。

let nickName: String? = nil
let fullName: String = "John Appleseed"
"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ケース内のコードを実行した後、プログラムは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
        }
    }
}
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を使用whileて、状態が変化するまでコードのブロックを繰り返します。代わりに、ループの条件を最後にして、ループが少なくとも1回実行されるようにすることができます。

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

n
128

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

m
128

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

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

total
6

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

関数とクロージャー

funcを使用してfuncを宣言します。かっこ内の引数のリストを名前に続けて関数を呼び出します。 ->を使用して、パラメーターの名前とタイプを関数の戻りタイプから分離します。

func greet(name: String, day: String) -> String {
    return "Hello \(name), today is \(day)."
}
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)."
}
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
}
returnFifteen()
15

関数はファーストクラスのタイプです。つまり、関数は別の関数をその値として返すことができます。

func makeIncrementer() -> ((Int) -> Int) {
    func addOne(number: Int) -> Int {
        return 1 + number
    }
    return addOne
}
var increment = makeIncrementer()
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]
hasAnyMatches(list: numbers, condition: lessThanTen)
true

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

numbers.map({ (number: Int) -> Int in
    let result = 3 * number
    return result
})
▿ 4 elements

  - 0 : 60
  - 1 : 57
  - 2 : 21
  - 3 : 36

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

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

let mappedNumbers = numbers.map({ number in 3 * number })
print(mappedNumbers)
[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."
    }
}

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

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

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

スーパークラスの実装をオーバーライドするサブクラスのメソッドは、overrideでマークされていoverrideせずにメソッドを誤って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")
test.area()
test.simpleDescription()
"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. スーパークラスで定義されたプロパティの値を変更します。メソッド、ゲッター、セッターを使用する追加のセットアップ作業も、この時点で実行できます。

プロパティを計算する必要はないが、新しい値を設定する前後に実行されるコードを提供する必要がある場合は、 willSetdidSet使用します。指定したコードは、初期化子の外で値が変更されるたびに実行されます。たとえば、以下のクラスでは、三角形の辺の長さが常に正方形の辺の長さと同じになるようにします。

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")
optionalSquare?.sideLength
▿ Optional<Double>

  - some : 2.5

列挙と構造

enumを使用して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 raw値が明示的に与えられ、残りのraw値が順番に割り当てられます。列挙型の生の型として文字列または浮動小数点数を使用することもできます。 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列挙の場合は、上記に言及されている:に値を割り当てるときhearts定数、列挙ケースSuit.Hearts定数は明示的な型が指定されていないため、その完全な名前で参照されます。スイッチ内部では、列挙型のケースは省略形.Hearts参照されます。これは、 selfの値がすでにスーツであることがわかっているためです。値のタイプが既知の場合はいつでも省略形を使用できます。

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

たとえば、サーバーに日の出と日の入り時刻を要求する場合を考えてみます。サーバーは、要求された情報で応答するか、問題点の説明で応答します。

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を作成するには、 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を宣言します。

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()
b.adjust()
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の宣言でSimpleStructureキーワードを使用して、構造を変更するメソッドをマークしているmutating注意してください。クラスのメソッドは常にクラスを変更できるため、 SimpleClassの宣言には、変異としてマークされたメソッドは必要ありません。

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

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

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

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

let protocolValue: ExampleProtocol = a
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し、 throwsを使用してエラーを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-catchdoブロック内で、エラーをスローする可能性のあるコードを、その前にtryと記述してマークします。 catchブロック内では、別の名前を付けない限り、エラーには自動的にerrorという名前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ブロックを提供できます。スイッチの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?を使用するtry?結果をオプションに変換します。関数がエラーをスローした場合、特定のエラーは破棄され、結果はnilます。それ以外の場合、結果は、関数が返した値を含むオプションです。

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

deferを使用して、関数内の他のすべてのコードの後で、関数が戻る直前に実行されるコードのブロックを記述します。関数がエラーをスローするかどうかに関係なく、コードが実行されます。異なるタイミングで実行する必要がある場合でも、 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
}
fridgeContains("banana")
fridgeIsOpen
false

ジェネリック

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

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)
▿ 4 elements

  - 0 : "knock"
  - 1 : "knock"
  - 2 : "knock"
  - 3 : "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を使用して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
}
anyCommonElements([1, 2, 3], [3])
true

<T: Equatable>を書くことは、 <T> ... where T: Equatableを書くことと同じ<T> ... where T: Equatableです。