Skip to content

制御フロー

分岐、ループ、早期終了を使用してコードを構造化します。

Swiftは様々な制御フロー文を提供します。これには、タスクを複数回実行するためのwhileループ、特定の条件に基づいて異なるコードの分岐を実行するためのifguardswitch文、そしてコードの実行フローを別のポイントに転送するためのbreakcontinueなどの文が含まれます。Swiftは配列、辞書、範囲、文字列、その他のシーケンスを簡単に反復処理できるfor-inループを提供します。また、現在のスコープを離れるときに実行されるコードをラップするdefer文も提供します。

Swiftのswitch文は、多くのC言語系の言語の対応するものよりもはるかに強力です。ケースは、区間の一致、タプル、特定の型へのキャストなど、多くの異なるパターンに一致させることができます。switchケースで一致した値は、ケースの本体内で使用するために一時的な定数や変数にバインドでき、各ケースのwhere句を使用して複雑な一致条件を表現できます。

For-Inループ

for-inループを使用して、配列のアイテム、数値の範囲、文字列の文字などのシーケンスを反復処理します。

この例では、for-inループを使用して配列のアイテムを反復処理します。

swift
let names = ["Anna", "Alex", "Brian", "Jack"]
for name in names {
    print("Hello, \(name)!")
}
// Hello, Anna!
// Hello, Alex!
// Hello, Brian!
// Hello, Jack!

辞書を反復処理して、そのキーと値のペアにアクセスすることもできます。辞書内の各アイテムは、辞書が反復処理されるときに(key, value)タプルとして返され、for-inループの本体内で使用するために(key, value)タプルのメンバーを明示的に名前付き定数として分解できます。以下のコード例では、辞書のキーはanimalNameという定数に分解され、辞書の値はlegCountという定数に分解されます。

swift
let numberOfLegs = ["spider": 8, "ant": 6, "cat": 4]
for (animalName, legCount) in numberOfLegs {
    print("\(animalName)s have \(legCount) legs")
}
// cats have 4 legs
// ants have 6 legs
// spiders have 8 legs

辞書の内容は本質的に順序付けされておらず、それらを反復処理する際に取得される順序は保証されません。特に、辞書にアイテムを挿入する順序は、それらが反復処理される順序を定義しません。配列と辞書の詳細については、コレクションタイプを参照してください。

数値範囲を使用してfor-inループを使用することもできます。この例では、5の倍数の最初のいくつかのエントリを出力します。

swift
for index in 1...5 {
    print("\(index) times 5 is \(index * 5)")
}
// 1 times 5 is 5
// 2 times 5 is 10
// 3 times 5 is 15
// 4 times 5 is 20
// 5 times 5 is 25

反復処理されるシーケンスは、閉じた範囲演算子(...)を使用して示されるように、1から5までの数値の範囲です。indexの値は範囲内の最初の数値(1)に設定され、ループ内のステートメントが実行されます。この場合、ループには1つのステートメントしか含まれておらず、現在のindexの値に対して5の倍数のエントリを出力します。ステートメントが実行された後、indexの値は範囲内の2番目の値(2)に更新され、再びprint(_:separator:terminator:)関数が呼び出されます。このプロセスは、範囲の終わりに達するまで続きます。

上記の例では、indexはループの各反復の開始時に自動的に設定される定数です。そのため、indexは使用される前に宣言する必要はありません。ループ宣言に含まれるだけで暗黙的に宣言され、let宣言キーワードは必要ありません。

シーケンスからの各値が必要ない場合は、変数名の代わりにアンダースコアを使用して値を無視できます。

swift
let base = 3
let power = 10
var answer = 1
for _ in 1...power {
    answer *= base
}
print("\(base) to the power of \(power) is \(answer)")
// Prints "3 to the power of 10 is 59049"

上記の例では、ある数値を別の数値の累乗に計算します(この場合、3の10乗)。1(つまり、3の0乗)の開始値を3で10回掛け合わせ、1から10までの閉じた範囲を使用して計算します。この計算では、ループを通過するたびに個々のカウンター値は不要です。コードは単にループを正しい回数実行します。ループ変数の代わりに使用されるアンダースコア文字(_)は、個々の値を無視し、ループの各反復中の現在の値にアクセスできません。

いくつかの状況では、両端点を含む閉じた範囲を使用したくない場合があります。時計の文字盤に毎分の目盛りを描くことを考えてみてください。0分から始めて60個の目盛りを描きたい場合、下限を含み上限を含まない半開範囲演算子(..<)を使用します。範囲の詳細については、範囲演算子を参照してください。

swift
let minutes = 60
for tickMark in 0..<minutes {
    // 毎分の目盛りを描画する(60回)
}

一部のユーザーは、UIに少ない目盛りを希望するかもしれません。彼らは5分ごとに1つの目盛りを好むかもしれません。stride(from:to:by:)関数を使用して不要な目盛りをスキップします。

swift
let minuteInterval = 5
for tickMark in stride(from: 0, to: minutes, by: minuteInterval) {
    // 5分ごとに目盛りを描画する(0、5、10、15 ... 45、50、55)
}

閉じた範囲も使用できます。stride(from:through:by:)を使用します。

swift
let hours = 12
let hourInterval = 3
for tickMark in stride(from: 3, through: hours, by: hourInterval) {
    // 3時間ごとに目盛りを描画する(3、6、9、12)
}

上記の例では、for-inループを使用して範囲、配列、辞書、文字列を反復処理します。ただし、Sequenceプロトコルに準拠している限り、独自のクラスやコレクションタイプを含む任意のコレクションを反復処理するためにこの構文を使用できます。

Whileループ

whileループは、条件がfalseになるまで一連のステートメントを実行します。これらの種類のループは、最初の反復が始まる前に反復回数が不明な場合に最適です。Swiftは2種類のwhileループを提供します。

  • whileは、ループの各パスの開始時に条件を評価します。
  • repeat-whileは、ループの各パスの終了時に条件を評価します。

While

whileループは、単一の条件を評価することから始まります。条件がtrueの場合、一連のステートメントが条件がfalseになるまで繰り返されます。

以下は、whileループの一般的な形式です。

swift
while <#condition#> {
   <#statements#>
}

この例では、シンプルなスゴロクゲーム(Chutes and Laddersとも呼ばれる)をプレイします。

ゲームのルールは次のとおりです。

  • ボードには25のマスがあり、目標は25番目のマスに到達することです。
  • プレイヤーの開始マスは「マス0」で、ボードの左下隅のすぐ外にあります。
  • 各ターンで6面のサイコロを振り、その数だけマスを進みます。
  • ターンがはしごの下で終わると、はしごを上ります。
  • ターンがヘビの頭で終わると、ヘビを下ります。

ゲームボードはInt値の配列で表されます。そのサイズはfinalSquareという定数に基づいており、配列を初期化するために使用され、後の例で勝利条件を確認するためにも使用されます。プレイヤーはボードの外、「マス0」から始まるため、ボードは25ではなく26のゼロのInt値で初期化されます。

swift
let finalSquare = 25
var board = [Int](repeating: 0, count: finalSquare + 1)

次に、いくつかのマスに特定の値を設定して、ヘビやはしごを表します。はしごの下にあるマスにはボードを上るための正の数値があり、ヘビの頭にあるマスにはボードを下るための負の数値があります。

swift
board[03] = +08; board[06] = +11; board[09] = +09; board[10] = +02
board[14] = -10; board[19] = -11; board[22] = -02; board[24] = -08

マス3には、マス11に移動するはしごの下があります。これを表すために、board[03]は+08に等しく、これは3と11の差に相当します。値とステートメントを整列させるために、単項プラス演算子(+i)が単項マイナス演算子(-i)とともに明示的に使用され、10未満の数値はゼロでパディングされます。(どちらのスタイリスティックな技法も厳密には必要ありませんが、コードをより整然とします。)

swift
var square = 0
var diceRoll = 0
while square < finalSquare {
    // サイコロを振る
    diceRoll += 1
    if diceRoll == 7 { diceRoll = 1 }
    // 振った数だけ進む
    square += diceRoll
    if square < board.count {
        // まだボード上にいる場合、ヘビやはしごのために上下に移動する
        square += board[square]
    }
}
print("ゲームオーバー!")

上記の例では、非常にシンプルな方法でサイコロを振ります。ランダムな数値を生成する代わりに、diceRollの値を0から始めます。whileループを通過するたびに、diceRollは1ずつ増加し、その後、値が大きすぎるかどうかを確認します。この戻り値が7に等しい場合、サイコロの目は大きすぎるため、値が1にリセットされます。結果は、常に1、2、3、4、5、6、1、2という一連のdiceRoll値です。

サイコロを振った後、プレイヤーはdiceRollマス進みます。サイコロの目がプレイヤーを25番目のマスを超えて移動させる可能性があります。このシナリオに対処するために、コードはsquareがボード配列のcountプロパティより小さいかどうかを確認します。squareが有効な場合、現在のsquare値にboard[square]に格納されている値が追加され、プレイヤーははしごやヘビの上下に移動します。

注:このチェックが行われない場合、board[square]はボード配列の範囲外の値にアクセスしようとし、ランタイムエラーが発生します。

現在のwhileループの実行が終了し、ループの条件が再度実行されるかどうかを確認します。プレイヤーが25番目のマスに到達または超えた場合、ループの条件はfalseと評価され、ゲームは終了します。

この場合、whileループが適切です。ゲームの長さはwhileループの開始時には明確ではありません。代わりに、特定の条件が満たされるまでループが実行されます。

Repeat-While

whileループのもう一つのバリエーションであるrepeat-whileループは、ループブロックを最初に1回実行し、その後、ループの条件を考慮します。その後、条件がfalseになるまでループを繰り返します。

注:Swiftのrepeat-whileループは、他の言語のdo-whileループに相当します。

以下は、repeat-whileループの一般的な形式です。

swift
repeat {
   <#statements#>
} while <#condition#>

以下は、whileループではなくrepeat-whileループとして書かれたスゴロクゲームの例です。finalSquareboardsquare、およびdiceRollの値は、whileループと同じ方法で初期化されます。

swift
let finalSquare = 25
var board = [Int](repeating: 0, count: finalSquare + 1)
board[03] = +08; board[06] = +11; board[09] = +09; board[10] = +02
board[14] = -10; board[19] = -11; board[22] = -02; board[24] = -08
var square = 0
var diceRoll = 0

このバージョンのゲームでは、ループの最初のアクションとしてはしごやヘビを確認します。ボード上のはしごはプレイヤーを直接25番目のマスに移動させることはないため、はしごを上ることでゲームに勝つことはできません。したがって、ループの最初のアクションとしてヘビやはしごを確認することは安全です。

ゲームの開始時に、プレイヤーは「マス0」にいます。board[0]は常に0であり、影響はありません。

swift
repeat {
    // ヘビやはしごのために上下に移動する
    square += board[square]
    // サイコロを振る
    diceRoll += 1
    if diceRoll == 7 { diceRoll = 1 }
    // 振った数だけ進む
    square += diceRoll
} while square < finalSquare
print("ゲームオーバー!")

コードがヘビやはしごを確認した後、サイコロが振られ、プレイヤーはdiceRollマス進みます。現在のループの実行が終了します。

ループの条件(while square < finalSquare)は以前と同じですが、今回はループの最初の実行が終了するまで評価されません。上記のrepeat-whileループの構造は、前の例のwhileループよりもこのゲームに適しています。上記のrepeat-whileループでは、square += board[square]は常にループのwhile条件がsquareがまだボード上にあることを確認した直後に実行されます。この動作により、前述のゲームのwhileループバージョンで見られた配列境界チェックの必要性がなくなります。

条件文

特定の条件に基づいて異なるコードの部分を実行することはしばしば有用です。エラーが発生したときに追加のコードを実行したり、値が高すぎたり低すぎたりしたときにメッセージを表示したりすることができます。これを行うには、コードの一部を条件付きにします。

Swiftは、コードに条件付き分岐を追加するための2つの方法を提供します:if文とswitch文です。通常、if文を使用して、少数の可能な結果を持つ単純な条件を評価します。switch文は、複数の可能な組み合わせを持つより複雑な条件に適しており、パターンマッチングが適切なコード分岐を選択するのに役立つ状況で役立ちます。

If

最も単純な形式では、if文には単一のif条件があります。その条件がtrueの場合にのみ、一連のステートメントを実行します。

swift
var temperatureInFahrenheit = 30
if temperatureInFahrenheit <= 32 {
    print("とても寒いです。スカーフを着用することを検討してください。")
}
// "とても寒いです。スカーフを着用することを検討してください。"と出力されます。

上記の例では、温度が32度以下(氷点)かどうかを確認します。そうであれば、メッセージが表示されます。そうでない場合、メッセージは表示されず、if文の閉じ括弧の後にコードの実行が続行されます。

if文は、if条件がfalseの場合の代替の一連のステートメントを提供できます。これらのステートメントはelseキーワードで示されます。

swift
temperatureInFahrenheit = 40
if temperatureInFahrenheit <= 32 {
    print("とても寒いです。スカーフを着用することを検討してください。")
} else {
    print("それほど寒くありません。Tシャツを着てください。")
}
// "それほど寒くありません。Tシャツを着てください。"と出力されます。

これらの2つの分岐のいずれかが常に実行されます。温度が40度に上昇したため、スカーフを着用するほど寒くなくなり、代わりにelse分岐がトリガーされます。

追加の条件を考慮するために、複数のif文を連鎖させることができます。

swift
temperatureInFahrenheit = 90
if temperatureInFahrenheit <= 32 {
    print("とても寒いです。スカーフを着用することを検討してください。")
} else if temperatureInFahrenheit >= 86 {
    print("本当に暖かいです。日焼け止めを忘れないでください。")
} else {
    print("それほど寒くありません。Tシャツを着てください。")
}
// "本当に暖かいです。日焼け止めを忘れないでください。"と出力されます。

ここでは、特に暖かい温度に対応するために追加のif文が追加されました。最終的なelse句は残り、あまり暖かくも寒くもない温度に対する応答を出力します。

最終的なelse句はオプションであり、条件のセットが完全である必要がない場合は省略できます。

swift
temperatureInFahrenheit = 72
if temperatureInFahrenheit <= 32 {
    print("とても寒いです。スカーフを着用することを検討してください。")
} else if temperatureInFahrenheit >= 86 {
    print("本当に暖かいです。日焼け止めを忘れないでください。")
}

温度がif条件をトリガーするほど寒くなく、else if条件をトリガーするほど暖かくないため、メッセージは表示されません。

Swiftは、値を設定する際に使用できるifの短縮表記を提供します。たとえば、次のコードを考えてみてください。

swift
let temperatureInCelsius = 25
let weatherAdvice: String

if temperatureInCelsius <= 0 {
    weatherAdvice = "とても寒いです。スカーフを着用することを検討してください。"
} else if temperatureInCelsius >= 30 {
    weatherAdvice = "本当に暖かいです。日焼け止めを忘れないでください。"
} else {
    weatherAdvice = "それほど寒くありません。Tシャツを着てください。"
}

print(weatherAdvice)
// "それほど寒くありません。Tシャツを着てください。"と出力されます。

ここでは、各分岐がweatherAdvice定数の値を設定し、if文の後に出力されます。

代替の構文を使用すると、if式としてこのコードをより簡潔に書くことができます。

swift
let weatherAdvice = if temperatureInCelsius <= 0 {
    "とても寒いです。スカーフを着用することを検討してください。"
} else if temperatureInCelsius >= 30 {
    "本当に暖かいです。日焼け止めを忘れないでください。"
} else {
    "それほど寒くありません。Tシャツを着てください。"
}

print(weatherAdvice)
// "それほど寒くありません。Tシャツを着てください。"と出力されます。

このif式バージョンでは、各分岐に単一の値が含まれています。分岐の条件がtrueの場合、その分岐の値がif式全体の値として使用され、weatherAdviceの代入に使用されます。すべてのif分岐には対応するelse if分岐またはelse分岐があり、いずれかの分岐が常に一致し、if式が常に値を生成することが保証されます。

代入の構文がif式の外側で始まるため、各分岐内でweatherAdvice =を繰り返す必要はありません。代わりに、if式の各分岐はweatherAdviceの3つの可能な値のいずれかを生成し、代入はその値を使用します。

すべてのif式の分岐には同じ型の値が含まれている必要があります。Swiftは各分岐の型を個別にチェックするため、nilのような値は複数の型で使用でき、Swiftがif式の型を自動的に決定するのを妨げます。代わりに、型を明示的に指定する必要があります。たとえば:

swift
let freezeWarning: String? = if temperatureInCelsius <= 0 {
    "氷点下です。氷に注意してください!"
} else {
    nil
}

上記のコードでは、if式の1つの分岐には文字列値があり、もう1つの分岐にはnil値があります。nil値は任意のオプション型の値として使用できるため、freezeWarningがオプションの文字列であることを明示的に記述する必要があります。

この型情報を提供する別の方法は、freezeWarningの明示的な型を提供する代わりに、nilの明示的な型を提供することです。

swift
let freezeWarning = if temperatureInCelsius <= 0 {
    "氷点下です。氷に注意してください!"
} else {
    nil as String?
}

if式は、エラーをスローしたり、fatalError(_:file:line:)のような関数を呼び出したりして、予期しない失敗に対応できます。たとえば:

swift
let weatherAdvice = if temperatureInCelsius > 100 {
    throw TemperatureError.boiling
} else {
    "適切な温度です。"
}

この例では、if式は予測温度が100°Cを超えているかどうかを確認します。これほど高温の場合、if式はテキストの要約を返す代わりに.boilingエラーをスローします。このif式はエラーをスローする可能性がありますが、その前にtryを書く必要はありません。エラー処理の詳細については、エラー処理を参照してください。

代入の右側にif式を使用するだけでなく、関数やクロージャが返す値としても使用できます。

Switch

switch文は、値を考慮し、それをいくつかの可能な一致パターンと比較します。その後、最初に一致したパターンに基づいて適切なコードブロックを実行します。switch文は、複数の潜在的な状態に対応するためのif文の代替手段を提供します。

最も単純な形式では、switch文は値を同じ型の1つ以上の値と比較します。

swift
switch <#some value to consider#> {
case <#value 1#>:
    <#respond to value 1#>
case <#value 2#>,
    <#value 3#>:
    <#respond to value 2 or 3#>
default:
    <#otherwise, do something else#>
}

すべてのswitch文は、caseキーワードで始まる複数の可能なケースで構成されます。特定の値と比較するだけでなく、Swiftは各ケースがより複雑な一致パターンを指定するためのいくつかの方法を提供します。これらのオプションは、この章の後半で説明します。

if文の本体と同様に、各ケースは個別のコード実行の分岐です。switch文は、どの分岐を選択するかを決定します。この手順は、考慮されている値に基づいて切り替えることとして知られています。

すべてのswitch文は網羅的でなければなりません。つまり、考慮されている型のすべての可能な値がswitchケースのいずれかによって一致する必要があります。すべての可能な値に対してケースを提供することが適切でない場合、明示的に対処されていない値をカバーするためにdefaultケースを定義できます。このdefaultケースはdefaultキーワードで示され、常に最後に表示されなければなりません。

この例では、単一の小文字の文字someCharacterを考慮するためにswitch文を使用します。

swift
let someCharacter: Character = "z"
switch someCharacter {
case "a":
    print("ラテンアルファベットの最初の文字")
case "z":
    print("ラテンアルファベットの最後の文字")
default:
    print("その他の文字")
}
// "ラテンアルファベットの最後の文字"と出力されます。

switch文の最初のケースは、英語アルファベットの最初の文字aに一致し、2番目のケースは最後の文字zに一致します。switchはすべての可能な文字に対してケースを持つ必要があるため、このswitch文はaz以外のすべての文字に一致するdefaultケースを使用します。この規定により、switch文が網羅的であることが保証されます。

if文と同様に、switch文にも式形式があります。

swift
let anotherCharacter: Character = "a"
let message = switch anotherCharacter {
case "a":
    "ラテンアルファベットの最初の文字"
case "z":
    "ラテンアルファベットの最後の文字"
default:
    "その他の文字"
}

print(message)
// "ラテンアルファベットの最初の文字"と出力されます。

この例では、switch式の各ケースにanotherCharacterが一致した場合に使用されるmessageの値が含まれています。switchは常に網羅的であるため、代入する値が常に存在します。

if式と同様に、エラーをスローしたり、fatalError(_:file:line:)のような関数を呼び出したりして、値を提供する代わりに特定のケースに対応できます。switch式を代入の右側に使用するだけでなく、関数やクロージャが返す値としても使用できます。

暗黙的なフォールスルーなし

C言語やObjective-Cのswitch文とは対照的に、Swiftのswitch文はデフォルトで各ケースの下部をフォールスルーして次のケースに入ることはありません。代わりに、最初に一致したswitchケースが完了すると、switch文全体の実行が終了し、明示的なbreak文を必要とせずに次のケースに進むことはありません。これにより、switch文はC言語のものよりも安全で使いやすくなり、誤って複数のswitchケースを実行することを避けることができます。

注:Swiftではbreakは必須ではありませんが、特定のケースを一致させて無視するためにbreak文を使用したり、一致したケースが実行を完了する前にそのケースを終了するためにbreak文を使用することができます。詳細については、switch文でのbreakを参照してください。

各ケースの本体には少なくとも1つの実行可能なステートメントが含まれている必要があります。最初のケースが空であるため、次のコードは有効ではありません。

swift
let anotherCharacter: Character = "a"
switch anotherCharacter {
case "a": // 無効、ケースの本体が空です
case "A":
    print("文字A")
default:
    print("文字Aではありません")
}
// これはコンパイル時エラーを報告します。

C言語のswitch文とは異なり、このswitch文は"a""A"の両方に一致しません。むしろ、case "a":が実行可能なステートメントを含まないため、コンパイル時エラーが報告されます。このアプローチにより、1つのケースから別のケースへの偶発的なフォールスルーが防止され、意図が明確な安全なコードが作成されます。

"a""A"の両方に一致する単一のケースを作成するには、カンマで区切って2つの値を複合ケースに結合します。

swift
let anotherCharacter: Character = "a"
switch anotherCharacter {
case "a", "A":
    print("文字A")
default:
    print("文字Aではありません")
}
// "文字A"と出力されます。

読みやすさのために、複合ケースは複数行にわたって記述することもできます。複合ケースの詳細については、複合ケースを参照してください。

注:特定のswitchケースの最後に明示的にフォールスルーするには、fallthroughキーワードを使用します。詳細については、フォールスルーを参照してください。

区間マッチング

switchケースの値は、その値が区間に含まれているかどうかをチェックできます。この例では、任意のサイズの数値の自然言語カウントを提供するために数値区間を使用します。

swift
let approximateCount = 62
let countedThings = "土星の衛星"
let naturalCount: String
switch approximateCount {
case 0:
    naturalCount = "なし"
case 1..<5:
    naturalCount = "少数の"
case 5..<12:
    naturalCount = "いくつかの"
case 12..<100:
    naturalCount = "数十の"
case 100..<1000:
    naturalCount = "数百の"
default:
    naturalCount = "多数の"
}
print("土星の衛星は\(naturalCount)あります。")
// "土星の衛星は数十あります。"と出力されます。

上記の例では、approximateCountswitch文で評価されます。各ケースはその値を数値または区間と比較します。approximateCountの値が12から100の間にあるため、naturalCountには"数十の"という値が割り当てられ、実行はswitch文から転送されます。

タプル

タプルを使用して、同じswitch文で複数の値をテストできます。タプルの各要素は、異なる値または値の区間と比較できます。代わりに、アンダースコア文字(_)を使用して、任意の値に一致させることもできます。

以下の例では、(x, y)点を単純なタプル型(Int, Int)として表現し、次のグラフ上で分類します。

swift
let somePoint = (1, 1)
switch somePoint {
case (0, 0):
    print("\(somePoint)は原点にあります")
case (_, 0):
    print("\(somePoint)はx軸上にあります")
case (0, _):
    print("\(somePoint)はy軸上にあります")
case (-2...2, -2...2):
    print("\(somePoint)はボックス内にあります")
default:
    print("\(somePoint)はボックス外にあります")
}
// "\(somePoint)はボックス内にあります"と出力されます。

switch文は、点が原点(0, 0)にあるか、赤いx軸上にあるか、緑のy軸上にあるか、原点を中心とした青い4x4ボックス内にあるか、ボックス外にあるかを判断します。

C言語とは異なり、Swiftは同じ値または値に対して複数のswitchケースを考慮することができます。実際、この例では点(0, 0)がすべての4つのケースに一致する可能性があります。ただし、複数の一致が可能な場合、最初に一致したケースが常に使用されます。点(0, 0)は最初にcase (0, 0)に一致し、他のすべての一致するケースは無視されます。

値バインディング

switchケースは、一時的な定数や変数に一致する値を名前付けし、そのケースの本体内で使用することができます。この動作は値バインディングとして知られており、値がケースの本体内で一時的な定数や変数にバインドされます。

以下の例では、(x, y)点をタプル型(Int, Int)として表現し、次のグラフ上で分類します。

swift
let anotherPoint = (2, 0)
switch anotherPoint {
case (let x, 0):
    print("x軸上にあり、xの値は\(x)です")
case (0, let y):
    print("y軸上にあり、yの値は\(y)です")
case let (x, y):
    print("他の場所にあり、(\(x), \(y))にあります")
}
// "x軸上にあり、xの値は\(x)です"と出力されます。

switch文は、点が赤いx軸上にあるか、緑のy軸上にあるか、他の場所にあるかを判断します。

3つのswitchケースは、anotherPointからの1つまたは両方のタプル値を一時的な定数xyにバインドします。最初のケースcase (let x, 0)は、y値が0の任意の点に一致し、点のx値を一時的な定数xに割り当てます。同様に、2番目のケースcase (0, let y)は、x値が0の任意の点に一致し、点のy値を一時的な定数yに割り当てます。

一時的な定数が宣言された後、それらはケースのコードブロック内で使用できます。ここでは、点の分類を出力するために使用されます。

このswitch文にはdefaultケースがありません。最終的なケースcase let (x, y)は、任意の値に一致する2つの一時的な定数のタプルを宣言します。anotherPointは常に2つの値のタプルであるため、このケースはすべての残りの値に一致し、switch文を網羅的にするためにdefaultケースは必要ありません。

Where

switchケースは、追加の条件をチェックするためにwhere句を使用できます。

以下の例では、(x, y)点を次のグラフ上で分類します。

swift
let yetAnotherPoint = (1, -1)
switch yetAnotherPoint {
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))は任意の点です")
}
// "(\(x), \(y))はx == -yの線上にあります"と出力されます。

switch文は、点が緑の対角線上にあるか(x == y)、紫の対角線上にあるか(x == -y)、またはどちらでもないかを判断します。

3つのswitchケースは、yetAnotherPointからの2つのタプル値を一時的な定数xyにバインドします。これらの定数はwhere句の一部として使用され、動的なフィルタを作成します。switchケースは、where句の条件がその値に対してtrueと評価される場合にのみ、現在の点の値に一致します。

前の例と同様に、最終的なケースはすべての残りの値に一致し、switch文を網羅的にするためにdefaultケースは必要ありません。

複合ケース

同じ本体を共有する複数のswitchケースは、caseの後にいくつかのパターンをカンマで区切って記述することで結合できます。パターンのいずれかが一致する場合、そのケースは一致したと見なされます。リストが長い場合、パターンは複数行にわたって記述できます。たとえば:

swift
let someCharacter: Character = "e"
switch someCharacter {
case "a", "e", "i", "o", "u":
    print("\(someCharacter)は母音です")
case "b", "c", "d", "f", "g", "h", "j", "k", "l", "m",
    "n", "p", "q", "r", "s", "t", "v", "w", "x", "y", "z":
    print("\(someCharacter)は子音です")
default:
    print("\(someCharacter)は母音でも子音でもありません")
}
// "\(someCharacter)は母音です"と出力されます。

switch文の最初のケースは、英語の小文字の母音すべてに一致します。同様に、2番目のケースは英語の小文字の子音すべてに一致します。最後に、defaultケースは他のすべての文字に一致します。

複合ケースには値バインディングも含めることができます。複合ケースのすべてのパターンには同じセットの値バインディングが含まれている必要があり、各バインディングは複合ケースのすべてのパターンから同じ型の値を取得する必要があります。これにより、複合ケースのどの部分が一致しても、ケースの本体のコードは常にバインディングの値にアクセスでき、その値は常に同じ型を持つことが保証されます。

swift
let stillAnotherPoint = (9, 0)
switch stillAnotherPoint {
case (let distance, 0), (0, let distance):
    print("軸上にあり、原点から\(distance)離れています")
default:
    print("軸上にありません")
}
// "軸上にあり、原点から\(distance)離れています"と出力されます。

上記のケースには2つのパターンがあります。(let distance, 0)はx軸上の点に一致し、(0, let distance)はy軸上の点に一致します。両方のパターンにはdistanceのバインディングが含まれており、distanceは両方のパターンで整数です。これにより、ケースの本体のコードは常にdistanceの値にアクセスできます。

制御転送文

制御転送文は、コードの実行順序を変更し、コードの一部から別の部分に制御を転送します。Swiftには5つの制御転送文があります。

  • continue
  • break
  • fallthrough
  • return
  • throw

continuebreak、およびfallthrough文については以下で説明します。return文については関数を参照し、throw文についてはエラーの伝播を参照してください。

Continue

continue文は、ループに現在の反復を停止し、ループの次の反復の最初から再開するように指示します。ループ全体を終了せずに「現在のループ反復が終了しました」と言います。

次の例では、小文字の文字列からすべての母音とスペースを削除して、暗号化されたパズルフレーズを作成します。

swift
let puzzleInput = "great minds think alike"
var puzzleOutput = ""
let charactersToRemove: [Character] = ["a", "e", "i", "o", "u", " "]
for character in puzzleInput {
    if charactersToRemove.contains(character) {
        continue
    }
    puzzleOutput.append(character)
}
print(puzzleOutput)
// "grtmndsthnklk"と出力されます。

上記のコードは、母音またはスペースに一致するたびにcontinueキーワードを呼び出し、ループの現在の反復を直ちに終了し、次の反復の最初にジャンプします。

Break

break文は、制御フロー文全体の実行を直ちに終了します。break文は、switch文またはループ文の内部で使用して、switch文またはループ文の実行を通常よりも早く終了したい場合に使用できます。

ループ文でのBreak

ループ文の内部で使用される場合、breakはループの実行を直ちに終了し、ループの閉じ括弧(})の後のコードに制御を転送します。現在のループ反復のコードは実行されず、ループの次の反復は開始されません。

Switch文でのBreak

switch文の内部で使用される場合、breakswitch文の実行を直ちに終了し、switch文の閉じ括弧(})の後のコードに制御を転送します。

この動作は、switch文で1つ以上のケースを一致させて無視するために使用できます。Swiftのswitch文は網羅的であり、空のケースを許可しないため、意図を明確にするためにケースを一致させて無視する必要がある場合があります。この場合、無視したいケースの本体全体としてbreak文を記述します。そのケースがswitch文によって一致すると、ケース内のbreak文がswitch文の実行を直ちに終了します。

注:コメントのみを含むswitchケースはコンパイル時エラーとして報告されます。コメントはステートメントではなく、switchケースを無視する原因にはなりません。switchケースを無視するためには常にbreak文を使用してください。

次の例では、Character値を切り替えて、それが4つの言語のいずれかで数字のシンボルを表しているかどうかを判断します。簡潔にするために、複数の値が単一のswitchケースでカバーされています。

swift
let numberSymbol: Character = "三"  // 数字3の中国語のシンボル
var possibleIntegerValue: Int?
switch numberSymbol {
case "1", "١", "一", "๑":
    possibleIntegerValue = 1
case "2", "٢", "二", "๒":
    possibleIntegerValue = 2
case "3", "٣", "三", "๓":
    possibleIntegerValue = 3
case "4", "٤", "四", "๔":
    possibleIntegerValue = 4
default:
    break
}
if let integerValue = possibleIntegerValue {
    print("\(numberSymbol)の整数値は\(integerValue)です。")
} else {
    print("\(numberSymbol)の整数値が見つかりませんでした。")
}
// "\(numberSymbol)の整数値は3です。"と出力されます。

この例では、numberSymbolがラテン、アラビア、中国、またはタイの数字のシンボルであるかどうかを確認します。一致が見つかった場合、switch文のケースの1つがオプションのInt?変数possibleIntegerValueに適切な整数値を設定します。

switch文の実行が完了した後、例はオプションバインディングを使用して値が見つかったかどうかを判断します。possibleIntegerValue変数はオプション型であるため、暗黙的に初期値としてnilを持ち、オプションバインディングはpossibleIntegerValueswitch文の最初の4つのケースのいずれかによって実際の値に設定された場合にのみ成功します。

上記の例では、すべての可能なCharacter値をリストすることは実用的ではないため、一致しない文字を処理するためにdefaultケースが使用されます。このdefaultケースはアクションを実行する必要はなく、その本体として単一のbreak文が記述されています。defaultケースが一致すると、break文がswitch文の実行を直ちに終了し、コードの実行はif let文から続行されます。

Fallthrough

Swiftでは、switch文は各ケースの下部をフォールスルーして次のケースに入ることはありません。つまり、最初に一致したケースが完了すると、switch文全体の実行が終了します。対照的に、C言語では各switchケースの最後に明示的なbreak文を挿入してフォールスルーを防ぐ必要があります。デフォルトのフォールスルーを避けることで、Swiftのswitch文はC言語のものよりもはるかに簡潔で予測可能になり、誤って複数のswitchケースを実行することを避けることができます。

C言語スタイルのフォールスルー動作が必要な場合、fallthroughキーワードを使用してケースごとにこの動作をオプトインできます。以下の例では、fallthroughを使用して数値のテキスト説明を作成します。

swift
let integerToDescribe = 5
var description = "数値\(integerToDescribe)は"
switch integerToDescribe {
case 2, 3, 5, 7, 11, 13, 17, 19:
    description += "素数であり、さらに"
    fallthrough
default:
    description += "整数です。"
}
print(description)
// "数値5は素数であり、さらに整数です。"と出力されます。

この例では、新しいString変数descriptionを宣言し、初期値を割り当てます。関数は次にintegerToDescribeの値をswitch文を使用して考慮します。integerToDescribeの値がリスト内の素数の1つである場合、関数はdescriptionの末尾にテキストを追加して、その数が素数であることを示します。その後、fallthroughキーワードを使用してdefaultケースにも「フォールイン」します。defaultケースは説明の末尾に追加のテキストを追加し、switch文が完了します。

integerToDescribeの値が既知の素数のリストにない場合、最初のswitchケースには一致しません。特定のケースが他にないため、integerToDescribedefaultケースに一致します。

switch文の実行が完了した後、数値の説明がprint(_:separator:terminator:)関数を使用して出力されます。この例では、数値5が正しく素数として識別されます。

注:fallthroughキーワードは、実行を次のケース(またはdefaultケース)ブロック内のステートメントに直接移動させるだけで、switchケースの条件をチェックしません。これはC言語の標準的なswitch文の動作と同様です。

ラベル付きステートメント

Swiftでは、ループや条件文を他のループや条件文の内部にネストして、複雑な制御フロー構造を作成できます。ただし、ループや条件文の両方でbreak文を使用して実行を早期に終了することができます。したがって、どのループや条件文をbreak文で終了するかを明示することが役立つ場合があります。同様に、複数のネストされたループがある場合、どのループにcontinue文が影響するかを明示することが役立つ場合があります。

これらの目的を達成するために、ステートメントラベルを使用してループ文や条件文にラベルを付けることができます。条件文では、break文とともにステートメントラベルを使用して、ラベル付きステートメントの実行を終了できます。ループ文では、breakまたはcontinue文とともにステートメントラベルを使用して、ラベル付きステートメントの実行を終了または続行できます。

ラベル付きステートメントは、ステートメントの導入キーワードと同じ行にラベルを配置し、その後にコロンを付けることで示されます。以下は、この構文のwhileループの例ですが、原則はすべてのループやswitch文に同じです。

swift
<#label name#>: while <#condition#> {
   <#statements#>
}

次の例では、breakcontinue文をラベル付きwhileループとともに使用して、この章の前半で見たスゴロクゲームの適応バージョンを示します。今回は、ゲームに追加のルールがあります。

  • 勝つためには、正確に25番目のマスに到達する必要があります。
  • 特定のサイコロの目が25番目のマスを超える場合、25番目のマスに到達するために必要な正確な数が出るまで再度サイコロを振る必要があります。

ゲームボードは以前と同じです。

finalSquareboardsquare、およびdiceRollの値は以前と同じ方法で初期化されます。

swift
let finalSquare = 25
var board = [Int](repeating: 0, count: finalSquare + 1)
board[03] = +08; board[06] = +11; board[09] = +09; board[10] = +02
board[14] = -10; board[19] = -11; board[22] = -02; board[24] = -08
var square = 0
var diceRoll = 0

このバージョンのゲームでは、whileループとswitch文を使用してゲームのロジックを実装します。whileループには、スゴロクゲームのメインゲームループを示すためにgameLoopというステートメントラベルがあります。

whileループの条件はwhile square != finalSquareであり、正確に25番目のマスに到達する必要があることを反映しています。

swift
gameLoop: while square != finalSquare {
    diceRoll += 1
    if diceRoll == 7 { diceRoll = 1 }
    switch square + diceRoll {
    case finalSquare:
        // diceRollは私たちを最終マスに移動させるので、ゲームは終了です
        break gameLoop
    case let newSquare where newSquare > finalSquare:
        // diceRollは私たちを最終マスを超えて移動させるので、再度サイコロを振ります
        continue gameLoop
    default:
        // これは有効な移動なので、その効果を確認します
        square += diceRoll
        square += board[square]
    }
}
print("ゲームオーバー!")

サイコロは各ループの開始時に振られます。プレイヤーを直ちに移動させるのではなく、ループはswitch文を使用して移動の結果を考慮し、その移動が許可されるかどうかを判断します。

  • サイコロの目がプレイヤーを最終マスに移動させる場合、ゲームは終了です。break gameLoop文は、whileループの外側の最初の行に制御を転送し、ゲームを終了します。
  • サイコロの目がプレイヤーを最終マスを超えて移動させる場合、その移動は無効であり、プレイヤーは再度サイコロを振る必要があります。continue gameLoop文は現在のwhileループの反復を終了し、ループの次の反復を開始します。
  • その他のすべての場合、サイコロの目は有効な移動です。プレイヤーはdiceRollマス進み、ゲームロジックはヘビやはしごを確認します。ループは終了し、制御は次のターンが必要かどうかを決定するためにwhile条件に戻ります。

注:上記のbreak文がgameLoopラベルを使用していなかった場合、switch文を終了し、while文を終了しません。gameLoopラベルを使用することで、どの制御文を終了するかが明確になります。

continue gameLoopを呼び出す際にgameLoopラベルを使用する必要はありません。ゲームには1つのループしかなく、continue文がどのループに影響するかに曖昧さはありません。ただし、continue文とともにgameLoopラベルを使用しても問題ありません。これにより、break文と一貫性が保たれ、ゲームのロジックが読みやすく理解しやすくなります。

早期終了

guard文は、if文のように、式のブール値に基づいてステートメントを実行します。guard文を使用して、条件がtrueであることを要求し、guard文の後のコードが実行されるようにします。if文とは異なり、guard文には常にelse句があります。条件がtrueでない場合、else句内のコードが実行されます。

swift
func greet(person: [String: String]) {
    guard let name = person["name"] else {
        return
    }

    print("こんにちは、\(name)さん!")

    guard let location = person["location"] else {
        print("あなたの近くの天気が良いことを願っています。")
        return
    }

    print("あなたの近くの天気が良いことを願っています。")
}

greet(person: ["name": "John"])
// "こんにちは、Johnさん!"と出力されます。
// "あなたの近くの天気が良いことを願っています。"と出力されます。
greet(person: ["name": "Jane", "location": "Cupertino"])
// "こんにちは、Janeさん!"と出力されます。
// "Cupertinoの天気が良いことを願っています。"と出力されます。

guard文の条件が満たされると、コードの実行はguard文の閉じ括弧の後に続きます。オプションバインディングを使用して条件の一部として値が割り当てられた変数や定数は、guard文が表示されるコードブロックの残りの部分で使用できます。

その条件が満たされない場合、elseブランチ内のコードが実行されます。そのブランチは、guard文が表示されるコードブロックを終了するために制御を転送する必要があります。これを行うには、returnbreakcontinue、またはthrowなどの制御転送文を使用するか、fatalError(_:file:line:)のような戻らない関数やメソッドを呼び出すことができます。

guard文を使用して要件を満たすことで、同じチェックをif文で行う場合と比較してコードの可読性が向上します。通常実行されるコードをelseブロックにラップすることなく記述でき、要件が満たされなかった場合のコードを要件の近くに配置できます。

遅延アクション

ifwhileのような制御フロー構造とは異なり、コードが実行されるかどうかや何回実行されるかを制御するのではなく、deferはコードが実行されるタイミングを制御します。deferブロックを使用して、プログラムが現在のスコープの終わりに達したときに実行されるコードを記述します。たとえば:

swift
var score = 1
if score < 10 {
    defer {
        print(score)
    }
    score += 5
}
// "6"と出力されます。

上記の例では、deferブロック内のコードはif文の本体を終了する前に実行されます。最初にif文のコードが実行され、scoreが5増加します。その後、if文のスコープを終了する前に、遅延されたコードが実行され、scoreが出力されます。

defer内のコードは、プログラムがそのスコープを終了する方法に関係なく常に実行されます。これには、関数からの早期終了、forループの中断、エラーのスローなどのコードが含まれます。この動作により、deferはメモリの手動割り当てと解放、低レベルのファイルディスクリプタのオープンとクローズ、データベースのトランザクションの開始と終了など、ペアのアクションが発生する必要がある操作に役立ちます。たとえば、次のコードは、コードのチャンク内で100を追加および減算することで、一時的なボーナスをスコアに与えます。

swift
var score = 3
if score < 100 {
    score += 100
    defer {
        score -= 100
    }
    // ボーナスを使用する他のコードがここにあります。
    print(score)
}
// "103"と出力されます。

同じスコープで複数のdeferブロックを記述する場合、最初に指定したものが最後に実行されます。

swift
if score < 10 {
    defer {
        print(score)
    }
    defer {
        print("スコアは:")
    }
    score += 5
}
// "スコアは:"と出力されます。
// "6"と出力されます。

プログラムが停止した場合(たとえば、ランタイムエラーやクラッシュが発生した場合)、遅延されたコードは実行されません。ただし、エラーがスローされた後に遅延されたコードは実行されます。エラー処理でdeferを使用する方法については、クリーンアップアクションの指定を参照してください。

APIの可用性の確認

SwiftにはAPIの可用性を確認するための組み込みサポートがあり、指定されたデプロイメントターゲットで利用できないAPIを誤って使用しないようにします。

コンパイラはSDKの可用性情報を使用して、コードで使用されているすべてのAPIがプロジェクトで指定されたデプロイメントターゲットで利用可能であることを確認します。利用できないAPIを使用しようとすると、Swiftはコンパイル時にエラーを報告します。

ifまたはguard文で可用性条件を使用して、使用したいAPIが実行時に利用可能かどうかに応じてコードブロックを条件付きで実行します。コンパイラは、可用性条件からの情報を使用して、そのコードブロック内のAPIが利用可能であることを確認します。

swift
if #available(iOS 10, macOS 10.12, *) {
    // iOSではiOS 10のAPIを使用し、macOSではmacOS 10.12のAPIを使用します
} else {
    // 以前のiOSおよびmacOSのAPIにフォールバックします
}

上記の可用性条件は、iOSではif文の本体がiOS 10以降でのみ実行され、macOSではmacOS 10.12以降でのみ実行されることを指定します。最後の引数*は必須であり、他のプラットフォームではif文の本体がターゲットで指定された最小デプロイメントターゲットで実行されることを指定します。

一般的な形式では、可用性条件はプラットフォーム名とバージョンのリストを取ります。iOS、macOS、watchOS、tvOS、visionOSなどのプラットフォーム名を使用します。完全なリストについては、宣言属性を参照してください。iOS 8やmacOS 10.10のようなメジャーバージョン番号を指定するだけでなく、iOS 11.2.6やmacOS 10.13.3のようなマイナーバージョン番号も指定できます。

swift
if #available(<#platform name#> <#version#>, <#...#>, *) {
    <#statements to execute if the APIs are available#>
} else {
    <#fallback statements to execute if the APIs are unavailable#>
}

guard文で可用性条件を使用する場合、それはそのコードブロックの残りの部分で使用される可用性情報を洗練します。

swift
@available(macOS 10.12, *)
struct ColorPreference {
    var bestColor = "blue"
}

func chooseBestColor() -> String {
    guard #available(macOS 10.12, *) else {
       return "gray"
    }
    let colors = ColorPreference()
    return colors.bestColor
}

上記の例では、ColorPreference構造体はmacOS 10.12以降が必要です。chooseBestColor()関数は可用性ガードで始まります。プラットフォームのバージョンがColorPreferenceを使用するには古すぎる場合、常に利用可能な動作にフォールバックします。guard文の後、macOS 10.12以降が必要なAPIを使用できます。

#availableに加えて、Swiftは逆のチェックもサポートしています。たとえば、次の2つのチェックは同じことを行います。

swift
if #available(iOS 10, *) {
} else {
    // フォールバックコード
}

if #unavailable(iOS 10) {
    // フォールバックコード
}

#unavailable形式を使用すると、チェックにフォールバックコードのみが含まれている場合にコードの可読性が向上します。