Потік керування
Мова Swift надає багато різноманітних інструкцій потоку керування. Вони охоплюють цикли while
, щоб виконувати задачу кілька разів; інструкції if
, guard
, та switch
для розгалуження коду в залежності від певних умов; інструкції на кшталт break
та continue
для передачі керування до іншої точки у вашому коді.
Мова Swift також надає цикл for
-in
, що спрощує ітерування масивів, словників, діапазонів, рядків та інших послідовностей.
Інструкція switch
у Swift значно потужніша за її аналоги у багатьох C-подібних мовах. Випадки в інструкції switch
не переходять автоматично до наступного випадку у Swift, що унеможливлює типові помилки мови C спричинені пропущеною інструкцією break
. Випадки можуть співпадати із багатьма різними шаблонами, включаючи потрапляння до інтервалу, кортежі, чи приведення до певних типів. При співпадінні значення у випадках switch
може бути прив’язане до тимчасової константи чи змінної для використання у тілі випадку, а складні умови співпадіння можуть виражатись за допомогою пункту where
для кожного випадку.
Цикл For-In
Цикл for
-in
використовується для ітерування послідовності, такої як діапазон чисел, елементи масиву, чи символи в рядку.
У наступному прикладі друкуються перші кілька рядків таблиці множення на п’ять:
for index in 1...5 {
print("\(index) × 5 = \(index * 5)")
}
// 1 × 5 = 5
// 2 × 5 = 10
// 3 × 5 = 15
// 4 × 5 = 20
// 5 × 5 = 25
Послідовність, що ітерується, є діапазоном чисел від 1
до 5
включно, що вказано за допомогою використання оператору закритого діапазону (...
). Значення index
встановлюється у перше число в діапазоні (1
), і після цього виконуються інструкції всередині циклу. В цьому випадку, цикл містить одну інструкцію, котра друкує рядок із таблиці множення на п’ять для поточного значення index
. Після виконання інструкції, значення index
встановлюється у друге значення в діапазоні (2
), і функція print(_:separator:terminator:)
викликається знову. Цей процес триває допоки не буде досягнуто кінець діапазону.
У прикладі вище, index
є константою, чиє значення автоматично встановлюється на початку кожної ітерації циклу. Константа index
як така не зобов’язана бути оголошеною до використання. Вона неявно оголошена просто за допомогою входження її в оголошення циклу, без необхідності використання ключового слова let
.
Якщо вам не потрібно кожне значення в послідовності, їх можна проігнорувати записавши символ підкреслення замість імені змінної.
let base = 3
let power = 10
var answer = 1
for _ in 1...power {
answer *= base
}
print("\(base) в степені \(power) дорівнює \(answer)")
// Надрукує "3 в степені 10 дорівнює 59049"
У прикладі вище обчислюється значення числа base
в степені power
(в даному випадку, 3
в степені 10
). Початкове значення 1
(тобто 3
в степені 0
) перемножується на 3
десять разів, що вказано за допомогою закритого діапазону, від 1
до 10
. Для даного обчислення, поточне значення лічильника всередині циклу не потрібне, код просто виконує цикл потрібну кількість разів. Використання символу підкреслення (_
) замість змінної циклу дозволяє ігнорувати окремі значення і не надавати доступ до поточного значення всередині кожної ітерації циклу.
Цикл for
-in
зручно використовувати для ітерування елементів масиву.
let names = ["Карпо", "Мотря", "Лаврін", "Мелашка"]
for name in names {
print("Привіт, \(name)!")
}
// Привіт, Карпо!
// Привіт, Мотря!
// Привіт, Лаврін!
// Привіт, Мелашка!
Також можна ітерувати елементи словнику для доступу до пар ключ-значення. Під час ітерації словнику, кожен елемент зі словника повертається як кортеж (key, value)
, і ви можете робити декомпозицію елементів кортежу (key, value)
у явно іменовані константи для використання їх у тілі циклу for
-in
. Тут, ключі словника декомпонуються у константу на ім’я animalName
, а значення словника декомпонуються у константу на ім’я legCount
.
let numberOfLegs = ["павуки": 8, "мурахи": 6, "коти": 4]
for (animalName, legCount) in numberOfLegs {
print("\(animalName) мають \(legCount) ніг")
}
// мурахи мають 6 ніг
// павуки мають 8 ніг
// коти мають 4 ніг
Елементи у словнику Dictionary
не обов’язково ітеруватимуться в тому ж порядку, в якому їх було додано в словник. Вміст словнику по суті невпорядкований, тому їх ітерування не гарантує порядку, в якому з’являтимуться елементи словнику. Детальнішу інформацію про масиви та словники можна знайти в розділі Колекції.
Цикли While
Цикли while
виконують набір інструкцій, допоки умова не стане хибною (false
). Ці типи циклів найкраще використовувати тоді, коли кількість ітерацій невідома до початку першої ітерації. У Swift є два види циклів while
:
- Цикл
while
перевіряє умову на початку кожного проходження циклу. - Цикл
repeat
-while
перевіряє умову в кінці кожного проходження циклу.
While
Цикл while
починається із перевірки єдиної умови. Якщо умова виконується (true
), то виконується набір інструкцій до тих пір, поки умова не перестане виконуватись (false
).
Ось загальна форма циклу while
:
while <умова> {
<інструкції>
}
Наступний приклад демонструє просту гру Ліла (https://uk.wikipedia.org/wiki/Ліла_(гра (також відому як Змії та сходи):

Правила гри наступні:
- Дошка має 25 клітин, і метою є потрапити на клітину 25 або за неї.
- Під час кожного ходу, ви кидаєте гральні кості, і рухаєтесь на відповідне число клітин вперед, слідуючи горизонтальним шляхом відповідно до штрих-пунктирної стрілки вище.
- Якщо хід закінчується на клітинці із нижньою частиною сходів, ви підіймаєтесь угору по цих сходам
- Якщо хід закінчується на голові змії, ви спускаєтесь униз по цій змії
Дошку представлено масивом цілочисельних значень (Int
). Її розмір визначається константою на ім’я finalSquare
, котра використовується для ініціалізації масиву і для перевірки виграшу в умові далі в цьому прикладі. Дошка ініціалізується 26-ма нульовими значеннями Int
, не 25-ма (по одному індексу від 0
до 25
).
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 // змії
Клітинка 3 містить початок сходів, що підіймає гравця на клітинку 11. Щоб представити це, третьому елементу board[03]
присвоєно значення 8
(різниця між 3 та 11). Оператор унарного плюса (+i
) використано для балансування оператору унарного мінуса (-i
), а числа, менші за 10
, префіксуються незначущим нулем, щоб вирівняти всі інструкції по тексту. (Жоден із цих стилістичних прийомів не є строго обов’язковим, але вони роблять код значно акуратнішим).
Гравці починають рухатись із “нульової клітинки”, котра знаходиться поруч із нижній лівим кутом дошки. Перший кидок костей вводить гравця на дошку.
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
. Кожної ітерації циклу while
змінна diceRoll
збільшується на 1
, та перевіряється, чи не стало це значення завеликим. Щоразу коли змінна diceRoll
дорівнює 7
, значення костей стає завеликим і тому воно скидається назад до 1
. Таким чином послідовність значень diceRoll
буде завжди 1
, 2
, 3
, 4
, 5
, 6
, 1
, 2
, і так далі.
Після кидання костей, гравець рухається вперед на diceRoll
клітинок. Можливо, що в цей момент гравець прийшов за клітинку 25, що означає закінчення гри. Щоб покрити цей сценарій, код перевіряє, що змінна square
менша за властивість count
масиву board
перед тим як додати значення, котре зберігається в board[square]
, до поточного значення square
, і таким чином пересунути гравця вверх чи вниз відповідно до поточних сходів чи змій.
Примітка
Якби дана перевірка не виконувалась, у
board[square]
ми могли б звертатись до значення за межами масивуboard
, що призвело би до помилки. Якщо би зміннаsquare
дорівнювала б26
, код намагався би перевірити значенняboard[26]
, що більше за розмір цього масиву.
Поточна ітерація циклу while
закінчується, і далі перевіряється умова, щоб дізнатись, що потрібна ще одна ітерація циклу. Якщо гравець прийшов на клітинку 25
або за неї, умова циклу обчислюється як false
, а гра закінчується.
Цикл while
є доречним у даному випадку, бо тривалість гри є невідомою на початок циклу while
. Цикл триває до моменту виконання умови закінчення гри.
Цикли Repeat-While
Інша варіація циклу while
, відома як цикл repeat
-while
, виконує одну ітерацію циклу перед тим, як перевіряє умову циклу. Після цього вона повторює ітерації циклу допоки умова не стане хибною (false
).
Примітка
Цикл
repeat
-while
у Swift є аналогом циклуdo
-while
у інших мовах.
Ось загальна форма циклу repeat
-while
:
repeat {
<інструкції>
} while <умова>
Переглянемо ще раз приклад зі грою Ліла, переписавши його за допомогою циклу repeat
-while
замість while
. Значення змінних finalSquare
, board
, square
, та diceRoll
ініціалізовано точно так само як і з циклом while
.
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, і тому неможливо виграти у грі, тільки рухаючись вверх по драбині. Таким чином, допустимо перевіряти на сходи чи змій першою дією в циклі.
На початку гри, гравець знаходиться у “нульовій клітинці. board[0]
завжди дорівнює 0
і тому ніяк не впливає.
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]
завжди виконується одразу після того, як виконання умови циклу підтвердить, що square
все ще знаходиться в межах ігрової дошки. Дана поведінка позбавляє необхідності перевіряти потрапляння в межі масиву, котра виконувалась в попередній версії цієї гри.
Інструкції умови
Дуже часто потрібно виконувати різні частини коду в залежності від певних умов. Вам може бути необхідно виконати додаткову частину коду у випадку помилки, або для виведення повідомлення, коли якесь значення стає занадто великим або малим. Для цього частини коду роблять умовними.
У мові Swift є два способи додавати умовні гілки коду: інструкція if
та інструкція switch
. Як правило, інструкція if
використовується для перевірки простих умов із невеликою кількістю можливих результатів. Інструкція switch
краще підходить для складніших умов із багатьма можливими варіантами, і є корисною в ситуаціях, коли співпадіння шаблону може допомогти обрати потрібну гілку коду для виконання.
If
У найпростішій формі, інструкція if
має єдину умову. Вона виконує набір інструкцій тільки тоді, коли значення умови обчислюється як true
.
var temperatureInCelsius = -1
if temperatureInCelsius <= 0 {
print("Дуже холодно. Варто вдягнути шарф.")
}
// Надрукує "Дуже холодно. Варто вдягнути шарф."
Наступний приклад перевіряє, чи є значення температури менше або рівне 0°C (точці замерзання води). Якщо це так, буде надруковане повідомлення. В іншому випадку не буде надруковано жодного повідомлення, і виконання коду буде продовжено від закриваючої фігурної дужки.
В інструкції if
можна вписати альтернативний набір інструкцій, відомий як пункт else, для ситуацій, коли умова if
обчислюється як false
. Ці інструкції позначаються ключовим словом else
.
temperatureInCelsius = 5
if temperatureInCelsius <= 0 {
print("Дуже холодно. Варто вдягнути шарф.")
} else {
print("Не так вже й холодно, вдягайте футболку.")
}
// Надрукує "Не так вже й холодно, вдягайте футболку."
Одна з цих гілок коду завжди буде виконана. Оскільки температура піднялась до 5°C, вже не достатньо холодно, щоб радити вдягнути шарф, і тому виконується гілка коду else
.
Можна об’єднувати кілька інструкцій if
у ланцюжок для розгляду додаткових умов.
temperatureInCelsius = 32
if temperatureInCelsius <= 0 {
print("Дуже холодно. Варто вдягнути шарф.")
} else if temperatureInCelsius >= 30 {
print("Спекотно. Не забудьте скористуватись сонцезахисним кремом.")
} else {
print("Не так вже й холодно, вдягайте футболку.")
}
// Надрукує "Спекотно. Не забудьте скористуватись сонцезахисним кремом."
У прикладі вище, було додано додаткову інструкцію if
, щоб реагувати на особливо високі температури. Фінальна гілка else
залишається незмінною, але тепер вона друкує повідомлення для температур, що не є занадто високими чи занадто низькими.
Однак, фінальна гілка else
не є обов’язковою, і може бути пропущеною, якщо набір умов не повинен бути повним.
temperatureInCelsius = 22
if temperatureInCelsius <= 32 {
print("Дуже холодно. Варто вдягнути шарф.")
} else if temperatureInCelsius >= 86 {
print("Спекотно. Не забудьте скористуватись сонцезахисним кремом.")
}
Оскільки температура не є ні занадто низькою, ні занадто високою, щоб спрацювала умова if
чи else if
, не буде надруковано жодного повідомлення.
Switch
Інструкція switch
розглядає значення і порівнює його із кількома можливими шаблонами. Після цього виконується відповідний блок коду, що визначається першим шаблоном, з яким співпало значення. Інструкція switch
є альтернативою інструкції if
для реагування на кілька можливих станів.
У найпростішій формі, інструкція switch
порівнює значення з одним чи багатьма значеннями одного типу.
switch <якесь значення для розглядуи> {
case <значення 1>:
<реакція на значення 1>
case <значення 2>,
<значення 3>:
<реакція на значення 2 чи 3>
default:
<інакше, робимо щось інше>
}
Кожна інструкція switch
складається із кількох можливих випадків, кожен із яких починається ключовим словом case
. Окрім простого порівняння із конкретним значенням, у Swift є кілька способів вказати складніші шаблони в кожному випаду. Ці можливості описані далі у цьому розділі.
Як і тіло в інструкції if
, кожен випадок case
є окремою гілкою виконання коду. Інструкція switch
визначає, яку із гілок виконати. Ця процедура відома як перемикання значення, що розглядається.
Кожна інструкція switch
повинна бути вичерпною. Це означає, що кожне можливе значення, що розглядається, повинно співпасти з одним випадком у switch
. Якщо в якійсь ситуації недоречно надавати випадок для кожного можливого значення, можна визначити випадок за замовчанням, котрий покриє всі значення, котрі не опрацьовані явно. Випадок за замовчанням позначається ключовим словом default
і повинен завжди слідувати останнім.
У наступному прикладі інструкція switch
використовується для розгляду єдиного рядкового символу на ім’я someCharacter
:
let someCharacter: Character = "я"
switch someCharacter {
case "а":
print("Перша буква абетки")
case "я":
print("Остання буква абетки")
default:
print("Якась інша літера")
}
// Надрукує "Остання буква абетки"
Перший випадок інструкції switch
порівнює значення із першою літерою української абетки, а
, а другий випадок порівнює значення з останньою літерою, я
. Оскільки switch
повинен мати випадок для будь-якого можливого символа, не тільки для символів алфавіту, в інструкції switch
використовується випадок за замовчанням (default
) для всіх інших символів крім а
та я
. Це положення забезпечує вичерпність інструкції switch
.
Відсутність неявного переходу до наступного випадку
На відміну від інструкції switch
в мовах C та Objective-C, інструкція switch
у Swift не переходить вкінці випадку до наступного випадку неявно. Замість цього, виконання інструкції switch
закінчується після виконання першого випадку, що підійшов, без необхідності вказування інструкції break
явно. Це робить інструкцію switch
легшою у використанні ніж її аналоги в C та запобігає випадковому виконанню більш ніж одного випадку.
Примітка
Хоч іструкція
break
і не обов’язкова у Swift, її тим не менше можна використовувати у випадках інструкціїswitch
для ігнорування певного випадку, або для раннього виходу з нього. Більш детальну інформацію можна знайти в підрозділі Інструкція Break в інструкції Switch.
Тіло кожного випадку повинно містити хоча б одну інструкцію для виконання. Наступний зразок коду не є коректним, бо перший випадок порожній:
let anotherCharacter: Character = "а"
switch anotherCharacter {
case "а": // Некоректно, бо тіло випадку порожнє
case "А":
print("Літера A")
default:
print("Не літера A")
}
// Тут буде помилка компіляції.
На відміну від інструкції switch
у C, дана інструкція switch
не співпадає одночасно із "a"
та "A"
. Замість цього буде виведено помилка часу компіляції, про те, що case "a":
не містить жодної інструкції для виконання. Цей підхід унеможливлює випадкові неявні переходи до наступного випадку, робить код безпечнішим, а його наміри зрозумілішими.
Щоб створити інструкцію switch
з одним випадком, що співпадає одночасно із "a"
та "A"
, треба поєднати ці два значення в об’єднаному випадку, розділивши їх комою.
let anotherCharacter: Character = "а"
switch anotherCharacter {
case "а", "А":
print("Літера А")
default:
print("Не літера А")
}
// Надрукує "Літера A"
Для легкості читання, об’єднані випадки також можна записувати на декількох рядках. За детальнішою інформацією про об’єднані випадки, звертайтесь до підрозділу [Об’єднані випадки](#Об’єднані-випадки).
Примітка
Для явного переходу до наступного випадку, слід у кінці поточного випадку вказати ключове слово
fallthrough
, як зазначено в підрозідіTo explicitly fall through at the end of a particular switch case, use the
fallthrough
keyword, as described in Перехід до наступного випадку.
Співпадіння з інтервалом
У випадках інструкції switch
значення можуть перевірятись на входження в інтервал. У наступному прикладі, для опису природною мовою кількостей супутників планети використовуються числові інтервали:
let approximateCount = 62
let countedThings = "На орбіті Сатурна"
var naturalCount: String
switch approximateCount {
case 0:
naturalCount = "жодного супутника"
case 1..<5:
naturalCount = "декілька супутників"
case 5..<10:
naturalCount = "численні супутники"
case 10..<100:
naturalCount = "десятки супутників"
case 100..<1000:
naturalCount = "сотні супутників"
default:
naturalCount = "багато супутників"
}
print("\(countedThings) \(naturalCount).")
// Надрукує "На орбіті Сатурна десятки супутників."
У прикладі вище, в інструкції switch
розглядається значення approximateCount
. В кожному випадку воно порівнюється із числом або інтервалом. Оскільки значення approximateCount
знаходиться між 10 та 100,
In the above example, approximateCount
is evaluated in a switch
statement. Each case
compares that value to a number or interval. Because the value of approximateCount
falls between 12 and 100, змінній naturalCount
присвоєно значення "десятки супутників"
, і виконання виходить з інструкції switch
.
Кортежі
В одній інструкції switch
можна перевіряти одразу кілька значень, об’єднавши їх у кортеж. Кожен елемент кортежу може порівнюватись з окремим значенням чи інтервалом значень. Також можна використовувати символ підкреслення (_
), також відомий як шаблон підстановки, для співпадіння із будь-яким можливим значенням.
У наступному прикладі розглядається точка (x, y), виражена за допомогою простого кортежу типу (Int, Int)
, та характеризується попадання цієї точки на один із графіків на ілюстрації.
let somePoint = (1, 1)
switch somePoint {
case (0, 0):
print("(0, 0) в початку координат")
case (_, 0):
print("(\(somePoint.0), 0) знаходиться на осі x")
case (0, _):
print("(0, \(somePoint.1)) знаходиться на осі y")
case (-2...2, -2...2):
print("(\(somePoint.0), \(somePoint.1)) всередині квадрату")
default:
print("(\(somePoint.0), \(somePoint.1)) за межами квадрату")
}
// Надрукує "(1, 1) всередині квадрату"
Інструкція switch
визначає, чи знаходиться точка в початку координат (0, 0), на осі x, на осі y, всередині синього квадрата розміром 4х4 з центром в початку координат, або за його межами.
На відміну від C, Swift дозволяє кільком випадкам switch
розглядати однакове значення. Фактично, точка (0, 0) співпадає із чотирма випадками у прикладі вище. Однак, у разі якщо можливо кілька співпадінь, завжди виконується первий випадок. Точка (0, 0) спершу співпадає із case (0, 0)
, тому всі подальші співпадіння ігноруються.
Прив’язування значень
У випадках інструкції switch
, одне чи кілька значень можна прив’язати до тимчасових констант чи змінних, для використання у тілі випадку. Така поведінка відома як прив’язування значень, бо значення прив’язуються до тимчасових констант чи змінних всередині тіла випадку.
У наступному прикладі розглядається точка (x, y), виражена кортежем типу (Int, Int)
, і категоризується її потрапляння на графік:
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 = 2"
Дана інструкція switch
визначає, чи потрапляє точна на червону вісь x, чи на жовтогарячу вісь y, чи деінде (на жодну з осей).
Усі три випадки switch
оголошують тимчасові константи x
and y
, яким присвоюється одне зі значень кортежу anotherPoint
, чи обидва. У першому випадку, case (let x, 0)
співпадає із будь-якою точкою, в якій y
дорівнює 0
, а значення x
присвоєно тимчасовій константі x
. Аналогічно, у другому випадку case (0, let y)
співпадає із будь-якою точкою, в якій x
дорівнює 0
, а значення y
присвоєно тимчасовій константі y
.
Після оголошення тимчасових констант, їм можна використовувати у блоці коду тіла випадку. В даному прикладі вони використовуються для друку категоризації точки.
Ця інструкція switch
не має випадку за замовчанням default
. Останній випадок, case let (x, y)
оголошує кортеж із двох констант, що співпадає із будь-яким значенням. Оскільки кортеж anotherPoint
завжди містить два значення, цей випадок співпадає із будь-яким значенням, і тому дана інструкція switch
вичерпна і не потребує випадку за замовчанням default
.
Where
A switch
case can use a where
clause to check for additional conditions.
The example below categorizes an (x, y) point on the following graph:
let yetAnotherPoint = (1, -1)
switch yetAnotherPoint {
case let (x, y) where x == y:
print("(\(x), \(y)) is on the line x == y")
case let (x, y) where x == -y:
print("(\(x), \(y)) is on the line x == -y")
case let (x, y):
print("(\(x), \(y)) is just some arbitrary point")
}
// Надрукує "(1, -1) is on the line x == -y"
The switch
statement determines whether the point is on the green diagonal line where x == y
, on the purple diagonal line where x == -y
, or neither.
The three switch
cases declare placeholder constants x
and y
, which temporarily take on the two tuple values from yetAnotherPoint
. These constants are used as part of a where
clause, to create a dynamic filter. The switch
case matches the current value of point
only if the where clause’s condition evaluates to true
for that value.
As in the previous example, the final case matches all possible remaining values, and so a default
case is not needed to make the switch
statement exhaustive.
Об’єднані випадки
Коли є декілька випадків інструкції switch
, котрі мають однакове тіло, їх можна об’єднувати, записуючи декілька шаблонів після ключового слова case
та розмежовуючи їх комами. Якщо значення, котре розглядається, співпаде хоча б з одним із шаблонів, буде виконано тіло випадку. Шаблони можна записати у кілька рядків, якщо їх список довгий. Наприклад:
let someCharacter: Character = "е"
switch someCharacter {
case "а", "е", "и", "i", "о", "у", "я", "ю", "є", "ï":
print("\(someCharacter) є голосною")
case "б", "в", "г", "ґ", "д", "ж", "з", "й", "к", "л",
"м", "н", "п", "р", "с", "т", "ф", "х", "ц", "ш", "щ":
print("\(someCharacter) є приголосною")
default:
print("\(someCharacter) не є ні голосною ні приголосною")
}
// Надрукує "е є голосною"
Перший випадок інструкції switch
співпадає із десятьма голосними української мови. Аналогічно, другий випадок співпадає з усіма приголосними української мови. Випадок за замовчанням default
співпадає із будь-яким іншим символом.
Об’єднані випадки також можуть містити прив’язані значення. Всі шаблони в об’єднаному випадку повинні містити однаковий набір прив’язаних значень, і кожне прив’язане значення повинно мати однаковий тип. Таким чином, незалежно від того, з яким із шаблонів співпало значення, код у тілі випадку зажди отримує значення одного й того ж типу.
let stillAnotherPoint = (9, 0)
switch stillAnotherPoint {
case (let distance, 0), (0, let distance):
print("Точка лежить на осі, на відстані \(distance) від початку координат")
default:
print("Точка не лежить на осі")
}
// Надрукує "Точка лежить на осі, на відстані 9 від початку координат"
У випадку вище є два шаблони: (let distance, 0)
, що співпадає із точками на осі Х, та (0, let distance)
, що співпадає із точками на осі Y. Обидва шаблони містять прив’язане значення distance
, котре є цілим числом в обох випадках – що означає, що код у тілі випадку завжди має доступ до значення distance
.
Інструкції передачі контролю
Інструкції передачі контролю міняють порядок, у якому виконується код, передаючи контроль з однієї секції коду в іншу. У мові Swift є п’ять інструкцій передачі контролю:
continue
break
fallthrough
return
throw
Інструкції continue
, break
, та fallthrough
описані нижче. Інструкція return
описана у розділі Функції, а інструкція throw
описана в підрозділі Поширення помилок за допомогою функцій, що викидають помилки.
Інструкція Continue
Інструкція continue
вказує поточному циклу, що слід припинити виконання поточної ітерації та почати наступну ітерацію циклу. Вона каже “я закінчив із поточною ітерацією циклу” без повного виходу із циклу.
У наступному прикладі із рядка, записаного малими літерами, видаляються всі голосні, пробіли та тире, для створення фрази-загадки:
let puzzleInput = "борітеся – поборете"
var puzzleOutput = ""
let charactersToRemove: [Character] = ["а", "е", "и", "i", "о", "у", "я", "ю", "є", "ï", " ", "–"]
for character in puzzleInput.characters {
if charactersToRemove.contains(character) {
continue
} else {
puzzleOutput.append(character)
}
}
print(puzzleOutput)
// Надрукує "бртспбрт"
У коді вище інструкція continue
викликається щоразу, коли поточний символ співпадає із голосним, пробілом чи тире, внаслідок чого поточна ітерація циклу одразу завершується, а цикл переходить до наступної ітерації.
Інструкція Break
Інструкція break
зупиняє виконання поточного потоку керування. Інструкцію break
можна використовувати всередині інструкції switch
або циклу, коли потрібно перервати виконання інструкції switch
чи циклу раніше, ніж би це відбулось за звичайних умов.
Інструкція Break у циклі
При використанні всередині циклу, інструкція break
завершує виконання циклу та негайно передає контроль до коду після фігурної дужки, що закриває тіло циклу (}
). Подальший код в поточній ітерації не виконується, а наступні ітерації циклу не розпочинаються.
Інструкція Break в інструкції Switch
При використанні всередині інструкції switch
, інструкція break
негайно завершує її виконання і передає контроль до коду після фігурної дужки, що закриває switch
(}
).
Ця поведінка може бути використана для ігнорування одного чи більше випадків в інструкції switch
. Інструкція switch
у мові Swift повинна бути вичерпною і не дозволяє створювати порожні випадки, але при цьому іноді потрібно свідомо ігнорувати певні випадки, щоб зробити намір коду явним. Зробити це можна, написавши інструкцію break
замість тіла випадку, який потрібно проігнорувати. Коли виконання потрапляє у такий випадок, інструкція break
всередині його негайно зупиняє виконання інструкції switch
.
Примітка
Випадок в іструкції
switch
, що містить лише коментар, також призводить до помилки компіляції. Коментарі не є інструкціями і не дозволяють ігнорувати випадок. Тому слід завжди використовувати інструкціюbreak
для ігнорування випадку вswitch
.
У наступному прикладі розглядається значення Character
та визначається, чи представляє воно значення числа в одній із чотирьох мов. Для стислості, в інструкції switch
використовуються об’єднані випадки.
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).")
}
// Надрукує "Цілочисельне значення символу 三 дорівнює 3."
У цьому прикладі розглядається символ numberSymbol
, ти визначається, чи є він латинським, арабським, китайським чи тайським записом числа від 1
до 4
. Якщо він співпав з одним із шаблонів, у відповідному випадку інструкції switch
опціональній змінній possibleIntegerValue
типу Int?
присвоюється відповідне цілочисельне значення.
Після завершення інструкції switch
, у прикладі використовується прив’язування опціоналу для визначення, чи було знайдено цілочисельне значення. Змінній possibleIntegerValue
неявно присвоюється початкове значення nil
через те, що вона має опціональний тип. Тому прив’язування опціоналу буде успішним тоді, коли змінній possibleIntegerValue
було присвоєно реальне значення в одному із чотирьох випадків інструкції switch
.
Оскільки перелічувати всі можливі значення Character
у прикладі вище не практично, випадок за замовчанням default
покриває всі символи, що не співпали. Цей випадок за замовчанням не повинен виконувати жодної дії, і тому його тіло складається з єдиної інструкції break
. Як тільки спрацьовує випадок за замовчанням, інструкція break
призводить до виходу з інструкції switch
, і виконання коду переходить до наступної інструкції if let
.
Перехід до наступного випадку
У Swift, виконання інструкції switch
не переходить в кінці одного випадку до виконання наступного автоматично. Натомість, виконання всієї інструкції switch
завершується, як кільки відпрацював перший із випадків, що співпав. На відміну від Swift, в мові C для такої поведінки потрібно явно вказувати інструкцію break
в кінці кожного випадку. Уникання автоматичних переходів у Swift робить інструкції switch
більш стислими й передбачуваними, ніж їх аналоги з мови C, бо унеможливлюється помилковий виклик кількох випадків замість одного.
Однак, якщо потрібна саме поведінка інструкцій switch
в стилі мови C, її можна увімкнути у кожному конкретному випадку за допомогою ключового слова fallthrough
. У наступному прикладі ключове слово fallthrough
використовується для створення текстового опису числа.
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
співпадає з одним із простих значень у списку, до тексту description
додається повідомлення, що дане число є простим. Потім, за допомогою ключового слова fallthrough
відбувається перехід до наступного випадку, default
. Випадок default
додає кінцівку в текст description
, на чому інструкція switch
завершується.
Якщо ж значення integerToDescribe
не співпадає з жодним зі значень у списку простих значень, перший випадок інструкції switch
не виконується. Оскільки більше нема особливих випадків, виконується випадок за замовчанням default
.
Після завершення виконання інструкції switch
, опис числа друкується за допомогою функції print(_:separator:terminator:)
. В даному прикладі було коректно визначено, що число 5
є простим.
Примітка
Ключове слово
fallthrough
не перевіряє умову випадку, до виконання якого воно призводить. Ключове словоfallthrough
просто змушує виконання коду перейти напряму до інструкцій всередині тіла наступного випадку (або випадку за замовчаннямdefault
), як і в стандартній поведінці інструкціїswitch
у мові C.
Іменовані інструкції
У Swift, можна вкладати цикли чи умовні інструкції всередині інших циклів чи умовних інструкцій, щоб створювати складніші структури управління потоком виконання. Однак, всередині циклів та умовних інструкцій можуть бути інструкції break
для раннього виходу з них. Таким чином, іноді виникає необхідність у явному позначенні, з якого саме циклу чи умовної інструкції потрібно вийти за допомогою інструкції break
. Аналогічно, у випадку кількох вкладених циклів, іноді може бути корисним явно вказувати, що якого з циклів повинна відноситись інструкція continue
.
Щоб досягнути цих цілей, інструкції циклів чи умов можна робити іменованими. В умовних інструкціях, ім’я інструкції може використовуватись разом з інструкцією break
для завершення її виконання. В інструкції циклу, ім’я інструкції може використовуватись разом з інструкціями break
та continue
для управління відповідною іменованою інструкцією.
Іменування інструкції відбувається за допомогою вказування імені інструкції на тому ж рядку, де знаходиться перше ключове слово даної інструкції, та двокрапки. Ось приклад даного синтаксису для циклу while
, хоча принцип є однаковим для всіх циклів та інструкцій switch
:
<ім'я інструкції>: while <умова> {
<інструкції>
}
У наступному прикладі використовуються інструкції break
та continue
всередині іменованого циклу while
для переробленої версії гри Ліла, з котрою ми ознайомились вище у цьому розділі. Цього разу, у грі є додаткове правило:
- Для виграшу, потрібно потрапити точно на клітинку 25.
Якщо певний кидок костей призводить до переходу за клітинку 25, потрібно кидати кості знову, допоки не випаде число, що приведе точно на клітинку 25.
Ігрова дошка є такою ж, як і раніше.

Значення змінних finalSquare
, board
, square
, та diceRoll
ініціалізуються так само, як і раніше:
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.
gameLoop: while square != finalSquare {
diceRoll += 1
if diceRoll == 7 { diceRoll = 1 }
switch square + diceRoll {
case finalSquare:
// кидок костей пересуне нас на останню клітинку, тому гру закінчено
break gameLoop
case let newSquare where newSquare > finalSquare:
// кидок костей пересуне нас за мені ігрового поля, тому слід кидати кості ще раз
continue gameLoop
default:
// даних хід є коректним, тому слід визначити його ефект (змії, сходи чи нічого)
square += diceRoll
square += board[square]
}
}
print("Гру завершено!")
Кості кидаються на початку кожної ітерації циклу. Замість того, щоб одразу пересунути гравця, тепер використовується інструкція switch
, що розглядає результат майбутнього руху та визначає, чи цей рух взагалі можливий:
- Якщо кидок костей пересуне гравця на останню клітинку, гру завершено. Інструкція
break gameLoop
передає контроль до першого рядку після циклуwhile
, що й завершує гру. - Якщо кидок костей пересуне гравця за межі ігрового поля, рух є неможливим і гравець повинен кидати кості ще раз. Інструкція
continue gameLoop
завершує поточну ітерацію циклуwhile
та розпочинає наступну ітерацію циклу. - В усіх інших випадках, кидок костей призводить до коректного руху. Гравець рухається вперед на
diceRoll
клітинок, і гра перевіряє наявність змій чи сходів. Після цього ітерація циклу завершується, і контроль переходить до перевірки умови циклу, щоб визначити, чи потрібен ще один хід.
Примітка
Якщо інструкція
break
би не використовувала ім’яgameLoop
, то вона перервала би інструкціюswitch
, а не циклwhile
. Використання іменіgameLoop
робить зрозумілим, яку саме інструкцію потрібно перервати.Немає строгої необхідності використовувати ім’я
gameLoop
при викликуcontinue gameLoop
для переходу на наступну ітерацію циклу. Це єдиний цикл у грі, і тому немає невизначеності, до якого із циклів відноситься інструкціяcontinue
. Однак, так само немає жодної шкоди у використанні іменіgameLoop
разом з інструкцієюcontinue
. Використання імені робить код послідовнішим (вище ми використовуємо ім’я з інструкцієюbreak
), та допомагає легше читати і розуміти код ігрової логіки.
Ранній вихід
Інструкція guard
, як і інструкція if
, виконує код в залежності від значення булевого виразу. Інструкцію guard
слід використовувати тоді, коли певна умова повинна бути істинною (true
) для подальшого виконання коду. На відміну від інструкції if
, в інструкції guard
завжди є пункт else
- код всередині пункту else
виконується, коли умова хибна (false
).
func greet(person: [String: String]) {
guard let name = person["name"] else {
return
}
print("Вітаємо, \(name)!")
guard let location = person["city"] else {
print("Сподіваємось, погода біля вас непогана.")
return
}
print(Сподіваємось, погода у місті \(location) непогана.")
}
greet(person: ["name": "Іван"])
// Надрукує "Вітаємо, Іван!"
// Надрукує "Сподіваємось, погода біля вас непоганаи."
greet(person: ["name": "Марічка", "city": "Київ"])
// Надрукує "Вітаємо, Марічка!"
// Надрукує "Сподіваємось, погода у місті Київ непогана."
Якщо умова всередині інструкції guard
виконується, код продовжує виконуватись після закриття фігурної дужки цієї інструкції (}
). Всі константи та змінні, що їм було присвоєно значення за допомогою прив’язування опціоналів всередині умови, будуть доступними до кінця блоку коду, де записаний даний guard
.
Якщо умова всередині інструкції guard
не виконується, відбувається виконання коду всередині інструкції else
. Ця гілка коду обов’язково повинна передати контроль до виходу з блоку коду, де записаний даний guard
. Вона може це зробити за допомогою інструкцій передачі контролю, таких як return
, break
, continue
, чи throw
, або вона може викликати метод чи функцію, з якої нема повернення, таку як fatalError(_:file:line:)
.
Інструкція guard
найкраще підходить для запису передумов певного коду, використання її покращує читабельність коду, якщо порівнювати з аналогічним кодом, записаним за допомогою інструкції if
. Вона дозволяє записувати основну гілку коду, що як правило, виконується, без обгортання її в блок else
, та дозволяє тримати код, що відноситься до обробки порушених передумов поруч із самими передумовами.
Перевірка доступності API
У мові Swift є вбудована підтримка перевірки доступності API, котра убезпечує вас від випадкового використання API, що недоступне на тій чи іншій версії тої чи іншої платформи.
Компілятор використовує інформацію про доступність в SDK для перевірки, що всі API використані у вашому коді є доступними на тій версії ОС, що ви вказали у вашому проєкті як мінімальну (deployment target). Якщо
спробувати використати недоступне API, компілятор Swift повідомить про відповідну помилку.
Однак, іноді потрібно використовувати API, що доступні не на всіх версіях ОС, що підтримує ваша програма. Для цього слід використовувати умови доступності в інструкціях if
чи guard
, щоб певний код виконувався чи не виконувався в залежності від того, чи є певні API доступними під час виконання. Компілятор використовує інформацію про доступність з умови доступності при перевірці, чи всі API у даному блоці коду є доступними.
if #available(iOS 10, macOS 10.12, *) {
// Використання API, доступних починаючи із iOS 10 та macOS 10.12
} else {
// Повернення до більш ранніх API iOS та macOS
}
Умови доступності вище вказують, що на iOS, тіло if
буде виконуватись тільки на версії 10 або вище; на macOS, тільки на версії 10.12 або вище. Останній аргумент, *
, потрібний для того, щоб вказати, що на будь-яких інших платформах, тіло if
буде виконуватись на мінімальній версії ОС, вказаній у налаштуваннях проєкту (deployment target).
У загальній формі, умова доступності приймає список платформ на їх версій. Слід вказувати назви платформ, такі, як iOS
, macOS
, watchOS
, та tvOS
— повний список можна знайти у розділі Declaration Attributes. Крім основних номерів версій, як iOS 8, можна вказувати також і мінорні номери версій, як iOS 8.3 та macOS 10.10.3.
if #available(<ім'я платформи> <версія>, ..., *) {
<інструкції для виконання, якщо API доступні>
} else {
<інструкції для виконання, якщо API недоступні>
}