Link Search Menu Expand Document

Основи

Мова Swift – це нова мова програмування для розробки додатків для iOS, macOS, watchOS, та tvOS. Проте багато частин Swift будуть знайомі з вашого досвіду розробки на мовах C та Objective-C.

Мова Swift надає свої власні версії всіх фундаментальних типів C та Objective-C, включаючи Int для цілих чисел, Double та Float для значень з плаваючою комою, Bool для Булевих значень, та String для текстової інформації. Мова Swift також має потужні версії трьох основних типів колекцій: Array, Set, та Dictionary, як описано в розділі Колекції.

Як і мова C, Swift використовує змінні для того, щоб зберігати значення та щоб посилатись на них по ідентифікатору. У мові Swift також широко використовуються змінні, чиє значення не може бути змінене. Їх називають константами, але вони набагато потужніші ніж константи в мові C. Константи вживаються всюди у Swift, щоб зробити код безпечнішим та більш зрозумілим у намірах, коли ви працюєте зі значеннями, котрим не потрібно змінюватись.

Окрім знайомих типів, у мові Swift з’являються більш розвинені типи, котрих нема в Objective-C, наприклад, кортежі. Кортежі дозволяють створювати та передавати групи значень. За допомогою кортежу можна повернути одразу декілька значень з функції як єдине об’єднане значення.

У мові Swift також вводяться опціональні типи – опціонали – які дозволяють обробляти відсутність значення. Опціонали виражають або “є деяке значення, і воно дорівнює x” або “немає взагалі жодного значення”. Користування опціоналами схоже на використання nil із вказівниками в Objective-C, але опціонали працюють з усіма типами, а не тільки з класами. Опціонали є не просто безпечніші та виразніші аніж вказівники на nil в Objective-C, вони лежать у серці найбільш потужних можливостей Swift.

Мова Swift типобезпечна, тобто мова допомагає вам виражатись ясно щодо типів значень, з якими працює ваш код. Якщо частина вашого коду очікує String, типобезпечність не дасть вам помилково передати в нього Int. Подібним чином типобезпечність не дасть вам помилково передати опціональний String в частину коду, котра очікує не опціональний String. Типобезпечність дозволяє якомога раніше відловлювати та виправляти помилки в процесі розробки.

Константи та змінні

Константи та змінні створюють асоціацію між іменем (як, наприклад, maximumNumberOfLoginAttempts чи welcomeMessage) зі значенням певного типу (такі як число 10 чи рядок "Hello"). Як тільки значення константи задано, його не можна змінити, в той час як значення змінної може бути змінене на інше в майбутньому.

Оголошення констант та змінних

Константи та змінні повинні бути оголошені до того, як вони будуть вперше використані. Константи оголошуються за допомогою ключового слова let, а змінні – за допомогою ключового слова var. Ось приклад того, як константи та змінні можуть бути використані для того, щоб відслідковувати кількість спроб користувача увійти в систему.

let maximumNumberOfLoginAttempts = 10
var currentLoginAttempt = 0

Даний код можна прочитати так:

“Оголошуємо нову константу під іменем maximumNumberOfLoginAttempts (максимальна кількість спроб увійти), і надаємо їх значення 10. Потім оголошуємо нову змінну під іменем currentLoginAttempt (поточна кількість спроб увійти), і надаємо їй початкове значення 0.”

У цьому прикладі максимальна кількість спроб увійти оголошено константою, тому що максимальна кількість спроб ніколи не зміниться. Поточна кількість спроб увійти оголошено змінною, бо це значення має збільшуватись на одиницю після кожної невдалої спроби користувача увійти в систему.

Ми можемо оголосити кілька констант чи змінних в один рядок, відділивши їх комами:

var x = 0.0, y = 0.0, z = 0.0

Примітка

Якщо значення, що зберігається у вашому коді, наврядчи зміниться, завжди оголошуйте його як константу за допомогою ключового слова let. Вживайте змінні тільки для зберігання значень, які реально повинні змінюватись.

Анотації типів

Можна вживати анотації типів під час оголошення констант чи змінних, щоб було зрозуміло, який тип даних може зберігати константа чи змінна. Анотацію типу можна вказати, поставивши двокрапку після імені константи чи змінної, пробіл та ім’я бажаного типу.

У наступному прикладі ми надаємо анотацію типу змінній welcomeMessage, щоб позначити, що ця змінна може зберігати значення типу String:

var welcomeMessage: String

Двокрапка в оголошення означає “…що має тип…,” тобто код вище можна прочитати так:

“Оголошуємо змінну з іменем welcomeMessage що має тип String.”

Фраза “що має тип String” означає “може зберігати будь-які значення типу String.” Це слід розуміти як “тип речі” (чи “вид речі”) що може зберігатись.

Тепер змінній welcomeMessage можна присвоїти будь-яке рядкове значення без помилок:

welcomeMessage = "Hello"

Можна оголосити кілька пов’язаних змінних одного типу в один рядок, відділивши їх імена комами, та вказавши єдину анотацію типу після останнього імені змінної:

var red, green, blue: Double

Примітка

На практиці анотаціями типів доводиться користуватись рідко. Якщо надати початкове значення константі чи змінній в момент оголошення, мова Swift майже завжди може визначити тип, який повинна мати дана константа чи змінна. Це більш детально описано в Типобезпечність та Визначення Типів. У прикладі вище в оголошенні welcomeMessage не вказано початкове значення, тому тип змінної welcomeMessage визначається анотацією типу.

Іменування констант і змінних

Імена констант і змінних можуть містити майже будь-які символи, в тому числі символи Unicode:

let π = 3.14159
let 你好 = "你好世界"
let 🐶🐮 = "dogcow"

Імена констант та змінних не можуть містити пробільних символів, математичних символів, стрілок, приватні (чи недійсні) коди Unicode, символи для малювання ліній та рамок. Змінні також не можуть починатись з цифри, хоча цифри можуть бути входити в ім’я в будь-якому іншому місці.

Як тільки було оголошено константу чи змінну певного типу, неможливо переоголосити її з таким же ім’ям, або тип. Так само неможливо зробити константу змінною чи навпаки.

Примітка

Якщо потрібно дати константі чи змінній ім’я, що співпадає з зарезервованим мовою Swift ключовим словом, можна заключити ім’я в косі апострофи (````` ). Однак не бажано вживати ключові слова як імена констант чи змінних, окрім видадків коли не зовсім немає іншого вибору.

Значення змінної можна змінити на інше значення сумісного типу. В наступному прикладі ми змінимо значення змінної friendlyWelcome з "Hello!" на "Вітаю!":

var friendlyWelcome = "Hello!"
friendlyWelcome = "Вітаю!"
// змінна friendlyWelcome стала тепер "Вітаю!"

На відміну від змінних, як тільки значення константи задано - його неможливо змінити. Спроба зміни значення виллється в помилку компіляції:

let languageName = "Swift"
languageName = "Swift++"
// Це помилка часу компіляції: languageName не може бути змінено.

Друк констант та змінних

Можна надрукувати поточне значення константи чи змінної за допомогою функції print(_:separator:terminator:):

print(friendlyWelcome)
// Надрукує "Вітаю!"

Функція print(_:separator:terminator:) є глобальною функцією, що друкує одне або кілька значень у відповідний вивід. У Xcode, наприклад, функція print(_:separator:terminator:) друкує у “консольну” панель. Параметри separator (символ-розділювач) та terminator (символ-закінчення) мають значення за замовчуванням, тому їх можна пропустити під час виклику функції. За замовчуванням, функція закінчує рядок, що друкує, символом переходу на новий рядок. Щоб вивести значення без переходу на новий рядок у кінці, можна передати порожній рядок як параметр terminator. Наприклад, print(someValue, terminator: ""). Для більш детальної інформації про параметри зі значенням за замовчуванням, дивіться Значення параметрів за замовчуванням.

У мові Swift є механізм інтерполяції рядків, що дозволяє включити ім’я константи чи змінної у довший рядок як заповнювач, що буде замінено на поточне значення константи чи змінної. Для цього потрібно огорнути ім’я у круглі дужки, поставивши перед дужкою, що відкривається, Swift uses string interpolation to include the name of a constant or variable as a placeholder in a longer string, and to prompt Swift to replace it with the current value of that constant or variable. Wrap the name in parentheses and escape it with a зворотний слеш \:

print("The current value of friendlyWelcome is \(friendlyWelcome)")
// Надрукує "The current value of friendlyWelcome is Вітаю!"

Note

Детальніше ознайомитись з можливостями інтерполяції рядків можна у розділі Інтерполяція рядків.

Коментарі

Коментарі - це блоки тексту у коді, що не впливають на виконання коду. Їх вживають як примітки чи нагадування для себе. Коментарі ігноруються компілятором Swift.

Коментарі у мові Swift дуже схожі на коментарі в мові C. Однорядкові коментарі починаються з подвійного прямого слешу (//):

// Це коментар.

Багаторядкові коментарі починаються із символів слешу та зірочки (/*) і закінчуються символами зірочки та слешу (*/):

/* Це також коментар,
 але написаний у кілька рядків. */

На відміну від коментарів у мові C, багаторядкові коментарі в мові Swift можуть бути вкладені в інші багаторядкові коментарі. Щоб написати вкладені коментарі, слід просто розпочати блок багаторядкових коментарів, а потім розпочати ще один блок багаторядкових коментарів усередині першого блоку. Потім слід закрити спочатку вкладений блок коментарів, а потім перший блок:

/* Це початок першого багаторядкового коментаря.
 /* Це другий, вкладений багаторядковий коментар. */
 Це кінець першого багаторядкового коментаря. */

Вкладені багаторядкові коментарі дозволяють закоментувати великі блоки коду швидко і легко, навіть якщо код уже містить багаторядкові коментарі.

Крапка з комою

На відміну від багатьох інших мов, у мові Swift не вимагається ставити крапку з комою (;) після кожної інструкції у коді, хоча її й можна ставити за бажанням.
Однак крапку з комою ставити обов’язково якщо потрібно написати кілька окремих інструкцій в один рядок:

let cat = "🐱"; print(cat)
// Друкує "🐱"

Цілі числа

Цілими числами є числа без дробової частини, такі як 42 та -23. Цілі числа бувають або знаковими (додатні, від’ємні та нуль), або беззнаковими (додатні та нуль).

У мові Swift знакові та беззнакові цілі можна зберігати у 8-, 16-, 32-, та 64-бітній формах. Назви типів цілих відповідають нормам кодування, схожим на відповідні норми в мові С: 8-бітний беззнаковий цілий тим має назву UInt8, а 32-бітний знаковий цілий тип має назву Int32. Як і всі інші типи у мові Swift, цілі типи мають імена, що пишуться у ВерхньомуВерблюжомуРегістрі.

Межі цілих чисел

Доступитись до найбільшого і найменшого можливого значення кожного з цілих типів можна за властивостями min та max:

let minValue = UInt8.min  // minValue дорівнює 0, і має тип UInt8
let maxValue = UInt8.max  // maxValue дорівнює 255, і має тип UInt8

Значення цих властивостей мають числовий тип відповідного розміру (такий як UInt8 у прикладі вище), і можуть далі використовуватись у виразах разом з іншими значеннями того ж типу.

Int

У більшості випадків не потрібно обирати конкретний розмір цілочисельного типу. Мова Swift надає додатковий цілочисельний тип Int, що має такий же розмір, що і розмір машинного слова на поточній платформі.

  • На 32-бітній платформі, тип Int має такий же розмір, що і Int32.
  • На 64-бітній платформі, тип Int має такий же розмір, що і Int64.

Окрім випадків, де потрібно працювати з конкретним розміром цілочисельного типу, слід користуватись типом Int для цілих значень у коді. Це допомагає коду бути консистентним і сумісним. Навіть на 32-бітній платформі, тип Int може зберігати значення від -2 147 483 648 до 2 147 483 647, що є більш ніж достатньо для багатьох цілочисельних діапазонів.

UInt

Мова Swift також надає беззнаковий цілочисельний тип UInt, що має такий же розмір, що і розмір машинного слова на поточній платформі:

  • На 32-бітній платформі, тип UInt має такий же розмір, що і UInt32.
  • На 64-бітній платформі, тип UInt має такий же розмір, що і UInt64.

Примітка

Слід уживати UInt тільки тоді, коли конкретно потрібен беззнаковий цілочисельний тип розміру, що співпадає з розміром машинного слова поточної платформи. Якщо це не ваш випадок, слід надавати перевагу типові Int, навіть якщо значення, що буде зберігатись, точно не може бути від’ємним. Послідовне вживання типу Int для цілих значень допомагає коду бути сумісним, оминати необхідність постійно конвертувати один числовий тип у інший, і відповідає цілочисельному визначенню типів, як описано у розділі Типобезпечність та Визначення Типів.

Числа з рухомою комою

Числами з рухомою комою є числа з дробовою частиною, такі як 3.14159, 0.1, та -273.15.

Числа з рухомою комою можуть представити набагато більший діапазон значень, ніж цілочисельні типи, і може зберігати числа, що набагато більші чи менші ніж ті, що можуть зберігатись у типі Int. Мова Swift надає два знакових типи для чисел з рухомою комою:

  • Double представляє 64-бітні числа з рухомою комою.
  • Float представляє 32-бітні числа з рухомою комою.

Примітка

Тип Double має точнісь у як мінімум 15 десяткових цифр, тоді як точність типу Float може бути всього лише 6 десяткових цифр. Доречний вибір типу чисел з рухомою комою залежить від природи і діапазону значень, з якими потрібно працювати у коді. У ситуаціях коли обидва типи можуть бути доречними, слід надавати перевагу типу Double.

Типобезпечність та Визначення Типів

Мова Swift - типобезбечна. Типобезпечна мова заохочує розробників бути ясними щодо типів значень, з якими може працювати їх код. Якщо частина коду очікує рядок (String), неможливо помилково передати в нього число (Int).

Оскільки мова Swift - типобезпечна, перевірка типів відбувається під час компіляції коду, і несумісні типи сигналізуються помилками. Це дозволяє перехоплювати та виправляти помилки в процесі розробки на стільки рано, на скільки це можливо.

Перевірка типів допомагає уникати помилок під час роботи з різними типами значень. Однак це не означає, що потрібно вказувати тип кожної константи та змінної, що оголошується. Якщо тип потрібного значення не вказано, мова Swift вираховує потрібний тип за допомогою механізму визначення типів. Визначення типів дозволяє компілятору вивести тип певного виразу автоматично під час компіляції коду, просто вивчивши надані вами значення.

Через визначення типів мова Swift вимагає набагато меншої кількості оголошень типів, аніж інші мови, такі як C чи Objective-C. Константи та змінні є все ще явно типізовані, але більша частина роботи по вказанню типу робиться за вас.

Зокрема, визначення типів є корисним під час оголошень констант чи змінних із вказанням початкового значення. Це часто робиться через присвоєння літерального значення (або літералу) константі чи змінній під час її оголошення. (Літеральним значенням називають значення, що з’являється у коді прямо, наприклад числа 42 та 3.14159 у прикладах нижче.)

Наприклад, якщо присвоїти літеральне значення 42 новій константі та не вказати, якого вона типу, мова Swift визначить, що константа повинна мати тип Int, бо вона ініціалізована числом, що схоже на ціле:

let meaningOfLife = 42
// визначено, що meaningOfLife матиме тип Int

Аналогічно, якщо не вказано тип для літерала з рухомою комою, Swift визначить, що тут був намір створити Double:

let pi = 3.14159
// визначено, що pi матиме тип Double

Swift завжди обирає Double (а не Float) під час визначення типу числа з рухомою комою.

Якщо поєднати цілочисельний літерал з літералом з рухомою комою у виразі, з контексту буде визначено тип Double:

let anotherPi = 3 + 0.14159
// anotherPi також визначається як значення типу Double

Літеральне значення 3 не має явного типу саме по собі, і тому відповідний вихідний тип Double визначено з присутності літералу з рухомою комою в частині виразу.

Числові літерали

Цілочисельні літерали можуть бути записані як:

  • Десяткове число, без префіксу
  • Двійкове число, з префіксом 0b
  • Вісімкове число, з префіксом 0o
  • Шістнадцяткове число, з префіксом 0x

Усі наступні літерали мають десяткове значення 17:

let decimalInteger = 17
let binaryInteger = 0b10001       // 17 в двійковій нотації
let octalInteger = 0o21           // 17 у вісімковій нотації
let hexadecimalInteger = 0x11     // 17 в шістнадцятковій нотації

Літерали з рухомою комою можуть бути десятковими (без префіксу), або шістнадцятковими (з префіксом 0x). Для них завжди повинно бути вказано десяткове (або шістнадцяткове) число по обидва боки від десяткового розділювача, в якості якого вживається крапка. Десяткові літерали з рухомою комою можуть також мати необов’язковий показник ступеня, маркований прописною чи строковою e; шістнадцяткові літерали з рухомою комою обов’язково повинні мати показник ступеня, маркований прописною чи строковою p.

Для десяткових чисел з показником ступеня exp, мантиса множиться на 10exp:

1.25e2 означає 1.25 x 102, або 125.0.

1.25e-2 означає 1.25 x 10-2, або 0.0125.

Для шістнадцяткових чисел з показником ступеня exp, мантиса множиться на 2exp:

0xFp2 означає 15 x 22, або 60.0.

0xFp-2 означає 15 x 2-2, або 3.75.

Усі наступні літерали з рухомою комою мають десяткове значення 12.1875:

let decimalDouble = 12.1875
let exponentDouble = 1.21875e1
let hexadecimalDouble = 0xC.3p0

Числові літерали можуть містити додаткове форматування, що їх було легше читати. І цілочисельні літерали, і літерали з рухомою комою можуть містити незначущі нулі, або містити символи підкреслення (_), щоб полегшити їх сприйняття. Жоден вид форматування не впливає на значення літералу:

let paddedDouble = 000123.456
let oneMillion = 1_000_000
let justOverOneMillion = 1_000_000.000_000_1

Перетворення числових типів

Слід вживати тип Int для всіх цілочисельних констант і змінних загального призначення, навіть якщо відомо, що вони точно додатні. Вживаючи цілочисельний тип за замовчуванням в щоденних ситуаціях ми отримуємо константи та змінні, які є одразу сумісні у коді та підходять до типу, що виводиться з цілочисельних літеральних значень.

Слід користуватись іншими цілочисельними типами тільки тоді, коли для задачі потрібні конкретно сами вони. Наприклад, для обробки даних явного розміру із зовнішнього джерела, або для оптимізації швидкодії, пам’яті чи іншої необхідної оптимізації. Користування типами явного розміру у цих ситуаціях допомагає відловити будь-які несподівані переповнення значень і неявно документує природу інформації, яка використовується.

Перетворення цілих чисел

Діапазон чисел, що можуть зберігатись у цілочисельній константі чи змінній є різним для кожного чисельного типу. Константа чи змінна типу Int8 може зберігати значенням між -128 та 127, тоді як константа чи змінна типу UInt8 може зберігати значенням між 0 та 255. Якщо число не вміщується в розмір цілочисельного типу константи чи змінної, під час компіляції коду буде повідомлено про помилку:

let cannotBeNegative: UInt8 = -1
// UInt8 не може зберігати від'ємні числа,
// і тому це призведе до помилки компіляції

let tooBig: Int8 = Int8.max + 1
// Int8 не може зберігати число, більше за максимальне значення,
// і тому це призведе до помилки компіляції

Оскільки кожен числовий тип може зберігати різний діапазон значень, слід робити перетворення числових типів у кожному випадку окремо. Цей підхід захищає від помилок прихованих перетворень, і допомагає робити наміри перетворення типів у коді явними.

Щоб перетворити одним конкретний числовий тип на інший, слід проініціалізувати нове число потрібного типу існуючим значенням. У наступному прикладі, константа twoThousand має тип UInt16, тоді як константа one має тип UInt8. Їх не можна додавати прямо, бо вони різних типів. Замість цього, у прикладі викликається UInt16(one) щоб створити нове число типу UInt16, проініціалізоване значенням one, і це значення використовується замість початкового:

let twoThousand: UInt16 = 2_000
let one: UInt8 = 1
let twoThousandAndOne = twoThousand + UInt16(one)

Оскільки обидва доданки тепер мають тип UInt16, додавання дозволяється. Тип вихідної константи (twoThousandAndOne) визначається як UInt16, бо це суми двох значень типу UInt16.

Вираз виду ЯкийсьТип(зЯкимосьПочатковимЗначенням) є звичайним способом викликати ініціалізатор типу у Swift, і передати йому початкове значення. За лаштунками, тип UInt16 має ініціалізатор, що приймає значення типу UInt8, і тому цей ініціалізатор вживається для створення нового значення типу UInt16 з існуючого значення типу UInt8. Сюди не можна передати будь-яке значення, це має бути такий тип, для якого у UInt16 є ініціалізатор. Однак у мові Swift є можливість розширити існуючий тип, додавши до нього новий ініціалізатор, що приймає новий тип (включаючи користувацький тип). Детальніше про це можна прочитати у розділі Розширення.

Перетворення цілих чисел та чисел з рухомою комою

Перетворення між цілочисельними числовими типами та типами з рухомою комою має відбуватись явно:

let three = 3
let pointOneFourOneFiveNine = 0.14159
let pi = Double(three) + pointOneFourOneFiveNine
// константа pi дорівнює 3.14159, і її тип визначено як Double

Тут значення константи three використано для того, щоб створити нове значення типу Double, і тому обидва доданки мають однаковий тип. Якби це перетворення не мало місця, додавання не було б дозволено.

Перетворення чисел з рухомою комою на цілі також має бути явним. Ціле число можна проініціалізувати значенням типу Double чи Float:

let integerPi = Int(pi)
// константа integerPi дорівнює 3, і її тип визначено як Int

Значення з рухомою комою завжди усікаються під час ініціалізації цілих значень таким чином. Тобто 4.75 стане 4, і -3.9 стане -3.

Примітка

Правила поєднання числових констант і змінних відрізняються від правил для числових літералів. Літеральне значення 3 може бути додане прямо до літерального значення 0.14159, бо числові літерали не мають явного типу самі по собі. Їх тип визначається тільки в момент, коли вони оцінюються компілятором.

Псевдоніми типів

Псевдоніми типів визначають альтернативне ім’я для існуючого типу. Псевдоніми типів визначаються за допомогою ключового слова typealias.

Псевдоніми типів корисні, коли потрібно посилатись на існуючий тип за іменем, що більш доречне в даному контексті. Наприклад, при роботі з інформацією фіксованого розміру із зовнішнього джерела:

typealias AudioSample = UInt16

Як тільки було оголошено псевдонім типу, його можна використовувати будь-де замість початкового імені:

var maxAmplitudeFound = AudioSample.min
// maxAmplitudeFound тепер дорівнює 0

Тут AudioSample визначено як псевдонім до типу UInt16. Оскільки це псевдонім, виклик AudioSample.min насправді викликає UInt16.min, котрий надає початкове значення 0 змінній maxAmplitudeFound.

Булевий тип даних

Мова Swift має базовий булевий тип, що називається Bool. Булеві значення ще називають логічними, бо вони можуть бути лише істиною чи хибною. Мова Swift надає два булевих константних значення, true (істина) та false (хиба):

let orangesAreOrange = true
let turnipsAreDelicious = false

Типи констант orangesAreOrange та turnipsAreDelicious було визначено як Bool через те, що їх було проініціалізовано булевими літералами. Так само як і з типами Int та Double вище, не потрібно явно вказувати тип Bool якщо їм присвоюється значення true чи false одразу після створення. Визначення типів дозволяє робити код мовою Swift коротшим та читабельним під час ініціалізації констант чи змінних значеннями, чий тип відомо.

Булеві значення зокрема корисні під час роботи з умовними інструкціями, такими як інструкція if:

if turnipsAreDelicious {
    print("Mmm, tasty turnips!")
} else {
    print("Eww, turnips are horrible.")
}
// Надрукує "Eww, turnips are horrible."

Умовні інструкції, такі як інструкція if описані більш детально у розділі Потік керування.

Типобезпечність мови Swift запобігає підстановці не булевих значень замість булевих. Наступний приклад призведе до повідомлення про помилку компіляції:

let i = 1
if i {
    // Цей приклад не скомпілюється, і буде повідомлено про помилку.
}

Однак, альтернативний приклад нижче є дійсним:

let i = 1
if i == 1 {
    // Цей приклад успішно скомпілюється
}

Результат порівняння i == 1 має тип Bool, і тому другий приклад проходить перевірку типу. Порівняння на кшталт i == 1 описано у розділі Базові оператори.

Як і в інших прикладах типобезпечності у мові Swift, цей підхід запобігає випадковим помилкам і гарантує, що намір в окремій секції коду є завжди зрозумілим.

Кортежі

Кортежі групують кілька значень в єдине складене значення. Значення всередині кортежу можуть бути будь-якого типу і не обов’язково повинні мати однаковий тип.

У наступному прикладі, (404, "Not Found") є кортежем, що описує код стану HTTP. Код стану HTTP - це спеціальне значення, що повертається веб-сервером після кожного запиту веб-сторінки. Код стану 404 Not Found повертається тоді, коли запитана сторінка не існує.

let http404Error = (404, "Не знайдено")

// http404Error має тип (Int, String), і дорівнює (404, "Не знайдено")

Кортеж (404, "Not Found") групує разом значення типів Int та String щоб надати коду стану HTTP два окремих значення: число і зручний для сприйняття людиною опис. Його можна описати як “кортеж типу (Int, String)”.

Можна створити кортежі з будь-якої перестановки типів, і вони можуть містити скільки завгодно різних типів. Ніщо не заважає мати кортеж типу (Int, Int, Int) або (String, Bool), або будь-яку іншу необхідну перестановку типів.

Можна розкласти вміст кортежу на окремі константи чи змінні, до яких можна звертатись як звичайно:

let (statusCode, statusMessage) = http404Error

print("Код стану дорівнює \(statusCode)")

// Надрукує "Код стану дорівнює 404"

print("Повідомлення статусу є \(statusMessage)")

// Надрукує "Повідомлення статусу є Не знайдено"

Якщо потрібно тільки деякі зі значень кортежу, можна проігнорувати частини кортежу за допомогою символу підкреслення (_) під час декомпозиції кортежу:

let (justTheStatusCode, _) = http404Error

print("Код статусу дорівнює \(justTheStatusCode)")

// Надрукує "Код статусу дорівнює 404"

Іншим способом доступу до окремих елементів кортежу є числові індекси, які починаються з нульового:

print("Код статусу дорівнює \(http404Error.0)")

// Надрукує "Код статусу дорівнює 404"

print("Повідомлення статусу є \(http404Error.1)")

// Надрукує "Повідомлення статусу є Не знайдено"

Також можна іменувати окремі елементи в кортежі під час його оголошення:

let http200Status = (statusCode: 200, description: "OK")

// Якщо елементи кортежу іменовані,

// можна користуватись їх іменами для доступу до їх значень:

print("Код статусу дорівнює \(http200Status.statusCode)")

// Надрукує "Код статусу дорівнює 200"

print("Повідомлення статусу є \(http200Status.description)")

// Надрукує "Повідомлення статусу є OK"

Зокрема, кортежі зручні як значення, що повертають функції. Функція, що намагається отримати веб-сторінку, може повертати кортеж типу (Int, String), щоб описати успіх чи невдачу при завантаженні сторінки. Повертаючи кортеж з двома окремими значеннями різних типів, функція надає більш корисну інформацію про свій результат, ніж якби вона повертала лише одне значення, чи кілька значень одного типу. З більш детальною інформацією можна ознайомитись у розділі Функції, що повертають кілька значень.

Примітка

Кортежі зручно вживати для тимчасових груп пов’язаних значень. Вони не підходять для створення складних структур даних. Якщо структура даних може жити поза тимчасовим контекстом, слід можелювати її за допомогою класу чи структури, а не кортежу. Більш детальну інформацію можна знайти у розділі Класи та структури.

Опціонали

Опціонали слід вживати, коли значення може бути відсутнє. Опціонали відображають дві можливості: або існує значення, і можна його розгорнути щоб отримати це значення, або не існує жодного значення.

Примітка

Концепція опціоналів не існує у мовах C чи Objective-C. Найближчим поняттям у Objective-C є можливість повернути nil із методу, що зазвичай повертає об’єкт, в такому випадку nil означає “відсутність валідного об’єкту”. Однак це працює лише з об’єктами, і не працює ні з структурами, ні з базовими типами мови C, ні з перечисленнями. Для таких типів методи Objective-C як правило повертають спеціальне значення (таке як NSNotFound) щоб позначити відсутність значення. Цей підхід базується на припущенні, що той, хто викликає метод, знає про це спеціальне значення, і пам’ятає, що слід його перевірити. Опціонали у мові Swift дозволяють вказувати на відсутність значення взагалі для будь-якого типу, без необхідності у спеціальних константах.

Ось приклад того як опціонали можуть бути використані щоб впоратись із відсутністю значення. Тип Int у Swift має ініціалізатор, що намагається конвертувати рядкове значення String у значення Int. Однак не кожний рядок можна конвертувати у ціле число. Рядок "123" можна конвертувати у числове значення 123, але рядок "привіт, світ" не можна очевидним чином конвертувати у рядок.

У наступному прикладі ініціалізатор використовується для конвертації рядку String у число Int:

let possibleNumber = "123"

let convertedNumber = Int(possibleNumber)

// тип convertedNumber визначено як "Int?", тобто "опціональний Int"

Оскільки ініціалізатор може провалитись, він повертає опціональне значення Int, замість простого Int. Опціональний тип Int пишеться як Int?, а не Int. Знак питання позначає те, що значення містить опціонал, і означає, що воно може містити деяке значення Int, або може не містити жодного значення взагалі. (Воно не може містити будь-що інше, будь то значення типу Bool чи String. Воно може містити або Int, або нічого взагалі.)

nil

Щоб опціональна змінна не містила жодного значення взагалі, слід присвоїти їй спеціальне значення nil:

var serverResponseCode: Int? = 404

// serverResponseCode містить фактичне значення 404 типу Int

serverResponseCode = nil

// serverResponseCode тепер не містить жодного значення

Примітка

nil не може вживатись із неопціональними константами та змінними. Якщо константа чи змінни в коді повинна опрацьовувати відсутність значення у деяких умовах, слід завжди оголошувати її як опціональне значення певного типу.

Якщо оголосити опціональну змінну без зазначення значення за замовчуванням, змінна автоматично набуває значення nil:

var surveyAnswer: String?

// surveyAnswer автоматично набуває значення nil

Примітка

nil у Swift - це не те ж саме, що nil у Objective-C. У Objective-C, nil - це вказівник на неіснуючий об’єкт. У Swift, nil - це не вказівник, це відсутність значення певного типу. Опціонали будь-якого типу можуть набувати значення nil, а не лише опціонали об’єктних типів.

Інструкція If та Примусове розгортання

Щоб визначити, чи містить опціонал значення, можна вживати інструкцію if, порівнявши опціонал зі значенням nil. Цю операцію можна виконати за допомогою оператора “дорівнює” (==) або оператора “не дорівнює” (!=).

Якщо опціонал має значення, вважається що він “не дорівнює” nil:

if convertedNumber != nil {

print("convertedNumber містить деяке цілочисельне значення.")

}

// Надрукує "convertedNumber містить деяке цілочисельне значення."

Як тільки ми впевнені, що опціонал містить значення, можна отримати це значення, додавши знак оклику (!) наприкінці імені опціоналу. Знак оклику фактично говорить “Я знаю, що цей опціонал точно має значення, будь-ласка, використовуйте його.” Ця операція відома як примусове розгортання значення опціоналу:

if convertedNumber != nil {

print("convertedNumber має цілочисельне значення \(convertedNumber!).")

}

// Prints "convertedNumber має цілочисельне значення 123."

Більш детально ознайомитись з інструкцією if можна в розділі Потік керування.

Примітка

Спроба використати ! для доступу до неіснуючого значення опціоналу призведе до помилки часу виконання. Перед тим, як вживати ! для примусового розгортання значення опціоналу, слід упевнитись, що він містить не nil.

Опціональне зв’язування

Опціональне зв’язування вживається щоб дізнатись, чи містить опціонал значення, і якщо містить, зробити це значення доступним як тимчасова константа чи змінна. Опціональне зв’язування може вживатись з інструкціями if та while щоб перевірити опціонал на наявність значення, витягти це значення у константу чи змінну, і все це у вигляді єдиної дії. Інструкції if та while більш детально описані у розділі Потік керування.

Синтаксис опціонального зв’язування для інструкції if виглядає наступним чином:

if let <ім'яКонстанти> = <якийсьОпціонал> {
    <інструкції>
}

Можна переписати приклад з possibleNumber із секції Опціонали використовуючи опціональне зв’язування замість примусового розгортання:

if let actualNumber = Int(possibleNumber) {
    print("\"\(possibleNumber)\" має цілочисельне значення \(actualNumber)")
} else {
    print("\"\(possibleNumber)\" не можна конвертувати у ціле число")
}
// Надрукує ""123" має цілочисельне значення 123"

Цей код можна прочитати так:

“Якщо опціональний Int, що повертається з Int(possibleNumber), містить значення, створити нову константу з назвою actualNumber і ініціалізувати її значенням, що міститься в опціоналі.”

Якщо перетворення успішне, константа actualNumber стає доступною для використання всередині першої гілки інструкції if. Її вже було проініціалізовано значенням, що містилось всередині опціоналу, і тому непотрібно використовувати суфікс ! для доступу до значення. У цьому прикладі actualNumber просто використовується для друку результату перетворення.

З опціональним зв’язуванням можна використовувати як константи, так і змінні. Якщо необхідно маніпулювати значенням actualNumber всередині першої гілки інструкції if, можна просто замість if let actualNumber написати if var actualNumber, і значення, що міститься всередині опціоналу стане доступною у вигляді змінної замість константи.

В єдину інструкцію if можна включити стільки опціональних зв’язувань і булевих умов, скільки потрібно, розділивши їх комами. Якщо будь-яке зі значень в опціональних зв’язуваннях є nil, або будь-яка булева умова виявиться хибною (false), вся умова інструкції if вважається хибною (false). Наступні інструкції if є еквівалентними:

if let firstNumber = Int("4"), let secondNumber = Int("42"), firstNumber < secondNumber && secondNumber < 100 {
    print("\(firstNumber) < \(secondNumber) < 100")
}
// Надрукує "4 < 42 < 100"

if let firstNumber = Int("4") {
    if let secondNumber = Int("42") {
        if firstNumber < secondNumber && secondNumber < 100 {
            print("\(firstNumber) < \(secondNumber) < 100")
        }
    }
}
// Надрукує "4 < 42 < 100"

Примітка

Константи і змінні, створені за допомогою опціонального зв’язування у інструкції if є доступними лише у тілі інструкції if. На відміну від цього, константи і змінні створені у інструкції guard є доступними у рядках коду, що слідують за інструкцією guard, як описано у розділі Ранній вихід.

Опціонали, що розгортаються неявно

Як описано вище, опціонали вказують на те, що константі чи змінній дозволяється “не мати значення”. Опціонали можна перевірити за допомогою інструкції if, щоб побачити, чи існує значення, і можна розгорнути за допомогою опціонального зв’язування, щоб доступитись до значення опціоналу, якщо воно існує.

Іноді зі структури програми ясно, що опціонал завжди має значення, після того як воно вперше задано. В таких випадках зручно позбутись необхідності перевіряти та розгортати значення опціоналу при кожному доступі до нього, бо можна безпечно припустити, що опціонал завжди має значення.

Такі види опціоналів є опціоналами, що розгортаються неявно. Щоб створити опціонал, що розгортається неявно, слід додати знак оклику (String!) замість знаку питання (String?) після типу, котрий потрібно зробити опціональним. Замість того, щоб ставити знак оклику після назви опціоналу при кожному використанні, ви ставите знак оклику один раз після типу опціоналу при його оголошенні.

Опціонали, що розгортаються неявно, зручно вживати коли існування значення опціоналу підтверджено одразу після оголошення, і можна точно припустити, що воно буде і надалі існувати. В основному опціонали, що розгортаються неявно, використовуються у мові Swift під час ініціалізації класів, як описано у розділі Безхазяйні посилання та Опціональні властивості, що розгортаються неявно.

За лаштунками опціонали, що розгортаються неявно, є звичайними опціоналами, але їх також можна використовувати як не опціональні значення, без необхідності кожного разу розгортати значення опціоналу. Наступний приклад демонструє різницю у поведінці між опціональним рядком та опціональним рядком, що розгортається неявно, під час доступу до їх внутрішнього значення типу String:

let possibleString: String? = "Опціональний рядок."
let forcedString: String = possibleString! // потрібен знак оклику


let assumedString: String! = "Опціональний рядок, що розгортається неявно."
let implicitString: String = assumedString // не потрібен знак оклику

Опціонали, що розгортаються неявно, можна розглядати як право опціоналу на автоматичне примусове розгортання там, де він використовується. Коли ви використовуєте опціонал, що розгортається неявно, Swift спершу намагається використати його як звичайне опціональне значення; якщо його не можна використати як опціонал, Swift виконує примусове розгортання значення. У коді вище опціонал assumedString примусово розгортається перед присвоєнням його значення константі implicitString, оскільки константа implicitString має явний, неопціональний тип String. У коді нижче, константа optionalString не має явно вказаного типу, тому вона є звичайним опціоналом:

let optionalString = assumedString
// Типом константи optionalString є "String?", і assumedString не розгортаєтсья примусово.

Якщо опціонал, що розгортається неявно, має значення nil, і ви спробуєте звернутись до його загорнутого значення, ви спричините помилку часу виконання. Результат буде точно таким же, як і у випадку якби ви розмістили знак оклику після звичайного опціоналу, що не містить значення.

Перевірити, чи містить опціонал, що розгортаєтсья неявно, значення nil, можна у той же спосіб що і звичайний опціонал:

if assumedString != nil {
    print(assumedString!)
}
// Надрукує "Опціональний рядок, що розгортається неявно."

Також з опціоналами, що розгортаються неявно, можна застосовувати опціональне зв’язування, щоб перевірити та розгорнути значення однією інструкцією:

if let definiteString = assumedString {
    print(definiteString)
}

// Надрукує "Опціональний рядок, що розгортається неявно."

Примітка

Не слід використовувати опцінали, що розгортаються неявно, коли є можливість що змінна стане nil пізніше. Потрібно завжди використовувати звичайні опціональні типи, якщо потрібно перевіряти змінну на nil під час її життєвого циклу.

Обробка помилок

Обробка помилок - це механізм, що дозволяє відповідати помилковим умовам, з якими може зіткнутись програми під час виконання.

Опціонали присутністю або відсутністю значення можуть повідомляти про успіх чи невдачу у функції. На відміну від них, обробка помилок дозволяє визначити причину невдачі, і, якщо потрібно, поширити цю помилку до інших частин програми.

Коли функція стикається із помилковими умовами, вона викидає помилку. Викликач цієї функції може перехопити помилку і відповідним чином прореагувати.

func canThrowAnError() throws {
    // ця функція може або викинути помилку, або відпрацювати успішно.
}

Функції повідомляють про можливість викинути помилку за допомогою ключового слова throws в оголошенні. Викликаючи функцію, що може викинути помилку, слід додавати ключове слово try перед її іменем.

У мові Swift помилки автоматично поширюються назовні від їх поточного контексту до тих пір, поки вони не будуть оброблені у блоці catch.

do {
    try canThrowAnError()
    // помилку не викинуто
} catch {
    // помилку викинуто
}

Інструкція do створює новий контекст, що дозволяє помилкам бути поширеними до одного чи кількох блоків catch.

Ось приклад того, як обробка помилок може бути використаною, щоб відповідати до різних помилкових умов:

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

do {
    try makeASandwich()
    eatASandwich()
} catch SandwichError.outOfCleanDishes {
    washDishes()
} catch SandwichError.missingIngredients(let ingredients) {
    buyGroceries(ingredients)
}

У даному прикладі, функція makeASandwich() (“зробити сендвіч”) викине помилку, якщо немає чистих тарілок, або якщо не вистачає певних інгредієнтів. Оскільки функція makeASandwich() може викинути помилку, виклик функції обгорнуто у вираз try. При огортанні виклику функції в блок інструкції do, будь-які помилки, викинуті цією функцією, будуть передані до прописаних нижче блоків catch.

Якщо не викинуто жодної помилки, буде викликано функцію eatASandwich() (з’їсти сендвіч). Якщо викинуто помилку, і вона відповідає випадку SandwichError.outOfCleanDishes, тоді буде викликано функцію washDishes().

Якщо викинуто помилку, і вона відповідає випадку SandwichError.missingIngredients, тоді буде викликано функцію buyGroceries(_:) з асоційованим значенням [String], захопленим за допомогою патерну catch. Викидання, перехоплення і передача помилок розкрито біль детально у розділі Обробка помилок.

Припущення

У деяких випадках неможливо продовжити виконання коду, якщо не виконуються певні умови. У таких випадках слід викликати припущення у коді, щоб завершити виконання коду, та надати можливість дослідити причину невиконання умови.

[Зневадження] з припущеннями

Припущення - це перевірка часу виконання, що булева умова точно виконується як true. Буквально, припущення “припускає”, що умова є true. Слід вживати припущення, щоб упевнитись, що важлива умова буде задоволена до виконання подальшого коду. Якщо умова є true, виконання коду продовжується як звичайно; якщо умова є false, виконання коду припиняється, а програма завершується.

Якщо код викликає припущення під час виконання у [зневаджувальному] середовищі, як, наприклад, під час побудови та запуску програми у Xcode, можна бачити точне місце, де було досягнуто хибного стану, і запросити стан програми під час виклику припущення. Припущення також дають можливість надати доречне [зневаджувальне] повідомлення, що пояснить причину припущення.

Щоб написати припущення, треба викликати функцію стандартної бібліотеки Swift assert(_:_:file:line:). Слід передати в цю функцію вираз, що обчислиться як true або false, та повідомлення, що слід відобразити у випадку, якщо результат умови буде false:

let age = -3
assert(age >= 0, "Вік особи не може бути меншим від нуля")
// це призведе до виклику припущення, бо вік не >= 0

У даному прикладі, виконання коду продовжиться тільки якщо age >= 0 обчислиться як true, тобто якщо значення віку age невід’ємне. Якщо значення age від’ємне, як у коді вище, тоді age >= 0 обчислиться як false, і припущення буде викликано, завершуючи програму.

Повідомлення у припущенні може бути пропущене, якщо потрібно, як у наступному прикладі:

assert(age >= 0)

Примітка

Припущення відключаються, якщо код скомпільовано з оптимізаціями, як наприклад під час побудови програми у конфігурації Release за замовчуванням у Xcode.

Коли слід вживати припущення

Слід вживати припущення завжди коли існує можливість, що умова може бути false, але при цьому умова має точно бути true для того, щоб код продовжив виконання. Доречні сценарії для перевірок припущень включають:

  • Цілочисельний індекс передано до власної реалізації оператору індексу, але значення індексу може бути занадто мале чи занадто велике.
  • Значення передано функції, але при деяких значеннях функція не може виконати своє завдання.
  • Опціональне значення наразі nil, коду для успішного виконання потрібно значення, що не дорівнює nil.

Дивіться також Індекси та Функції.

Примітка

Припущення призводять до термінового завершення програми, вони не підходять для проектування коду в такий спосіб, що невалідні умови наврядчи трапляться. Тим не менше, у ситуаціях, коли невалідні умови можливі, припущення - це ефективний спосіб впевнитись, що такі умови виділені і помічені під час розробки, до часу публікації програми.