Swift язык руководство по

Основы

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 также включает опциональные типы, которые позволяют работать с отсутствующими значениями. Опциональные значения говорят либо «здесь есть значение, и оно равно х», либо «здесь нет значения вообще». Опциональные типы подобны использованию nil с указателями в Objective-C, но они работают со всеми типами, не только с классами. Опциональные значения безопаснее и выразительнее чем nil указатели в Objective-C, и находятся в сердце многих наиболее мощных особенностей Swift.

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, то вы можете воспользоваться обратными кавычками (`) написанными вокруг этого слова. Однако старайтесь избегать имен совпадающих с ключевыми словами Swift и используйте такие имена только в тех случаях, когда у вас абсолютно нет выбора.

Вы можете изменить значение переменной на другое значение совместимого типа. В примере ниже значение friendlyWelcome изменено с “Hello!” на “Bonjour!”:

var friendlyWelcome = “Hello!”
friendlyWelcome = “Bonjour!”
// теперь friendlyWelcome имеет значение “Bonjour!”

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

let languageName = "Swift"
languageName = "Swift++"
// Это ошибка компилляции: languageName cannot be changed (значение languageName не может быть изменено).

Печать констант и переменных

Вы можете напечатать текущее значение константы или переменной при помощи функции print(_:separator:terminator:):

print(friendlyWelcome)
// Выведет "Bonjour!"

Функция print(_:separator:terminator:) является глобальной, которая выводит одно или более значений в подходящем виде. В Xcode, например, функция print(_:separator:terminator:) выводит значения в консоль. Параметры separator и terminator имеют дефолтные значения, так что при использовании функции их можно просто пропустить. По умолчанию функция заканчивает вывод символом переноса строки. Чтобы вывести в консоль значения без переноса на новую строку, вам нужно указать пустую строку в параметре terminator, например, print(someValue, terminator: «»). Для получения дополнительной информации по дефолтным значениям параметров обратитесь в раздел «Значения по умолчанию для параметров».

Swift использует интерполяцию строки для включения имени константы или переменной в качестве плейсхолдера внутри строки, что подсказывает Swift подменить это имя на текущее значение, которое хранится в этой константе или переменной. Поместите имя константы или переменной в круглые скобки, а затем добавьте обратный слеш () перед открывающей  скобкой:

print("Текущее значение friendlyWelcome равно (friendlyWelcome)")
// Выведет "Текущее значение friendlyWelcome равно Bonjour!"

Заметка

Все опции, которые вы можете использовать в интерполяции строки вы сможете найти в разделе «Интерполяция строк».

Комментарии

Используйте комментарии, чтобы добавить неисполняемый текст в коде, как примечание или напоминание самому себе. Комментарии игнорируются компилятором Swift во время компиляции кода.

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

// это комментарий

Вы также можете написать многострочные комментарии, которые начинаются со слеша и звездочки (/*) и заканчиваются звездочкой, за которой следует слеш (*/):

/* это тоже комментарий,
но написанный на двух строках */

В отличие от многострочных комментариев в C, многострочные комментарии в Swift могут быть вложены в другие многострочные комментарии. Вы можете написать вложенные комментарии, начав многострочный блок комментариев, а затем, начать второй многострочный комментарий внутри первого блока. Затем второй блок закрывается, а за ним закрывается первый блок:

/* это начало первого многострочного комментария
/* это второго, вложенного многострочного комментария */
это конец первого многострочного комментария */

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

Точки с запятой

В отличие от многих других языков, Swift не требует писать точку с запятой (;) после каждого выражения в коде, хотя вы можете делать это, если хотите. Однако точки с запятой требуются, если вы хотите написать несколько отдельных выражений на одной строке:

let cat = "?"; print(cat)
// Выведет "?"

Целые числа

Integer (целое число) — это число, не содержащее дробной части, например, как 42 и -23. Целые числа могут быть либо знаковыми (положительными, ноль или отрицательными) либо беззнаковыми (положительными или ноль).

Swift предусматривает знаковые и беззнаковые целые числа в 8, 16, 32 и 64 битном форматах. Эти целые числа придерживаются соглашения об именах, аналогичных именам в C, в том, что 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-битное число с плавающей точкой. Используйте его, когда значение не нуждается в 64-битной точности.

Заметка

Double имеет точность минимум 15 десятичных цифр, в то время как точность Float может быть всего лишь 6 десятичных цифр. Соответствующий тип числа с плавающей точкой используется в зависимости от характера и диапазона значений, c которыми вы должны работать в коде. В случаях, где возможно использование обоих типов, предпочтительным считается 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). Они всегда должны иметь число (десятичное или шестнадцатеричное) по обе стороны от дробной точки. Они также могут иметь экспоненту, с указанием в верхнем или нижнем регистре е для десятичных чисел с плавающей точкой, или в верхнем или нижнем регистре р для шестнадцатеричных чисел с плавающей точкой.

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

  • 1.25e2 означает 1.25 × 102, или 125.0.
  • 1.25e-2 означает 1.25 × 10-2, или 0.0125.

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

  • 0xFp2 означает 15 × 22, или 60.0.
  • 0xFp-2 означает 15 × 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 есть инициализатор. Расширение существующих типов с помощью создания инициализаторов, которые принимают новые типы (включая объявление вашего типа) рассматривается в главе Расширения.

Преобразования целых чисел и чисел с плавающей точкой

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

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. Этот тип называют логическим, потому что он может быть только true или false. 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 препятствует замене значения Bool на не логическое значение. Следующий пример выведет ошибку компиляции:

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, "Not Found")
// http404Error имеет тип (Int, String), и равен (404, "Not Found")

Чтобы передать код статуса, кортеж (404, «Not Found») группирует вместе два отдельных значения Int и String: число и понятное человеку описание. Это может быть описано как «кортеж типа (Int, String)«.

Вы можете создать кортеж с любой расстановкой типов, и они могут содержать сколько угодно нужных вам типов. Ничто вам не мешает иметь кортеж типа (Int, Int, Int), или типа (String, Bool), или же с любой другой расстановкой типов по вашему желанию.

Вы можете разложить содержимое кортежа на отдельные константы и переменные, к которым можно получить доступ привычным способом:

let (statusCode, statusMessage) = http404Error
print("The status code is (statusCode)")
// Выведет "The status code is 404"
print("The status message is (statusMessage)")
// Выведет "The status message is Not Found"

Если вам нужны только некоторые из значений кортежа, вы можете игнорировать части кортежа во время разложения с помощью символа подчеркивания (_):

let (justTheStatusCode, _) = http404Error
print("The status code is (justTheStatusCode)")
// Выведет "The status code is 404"

В качестве альтернативы можно получать доступ к отдельным частям кортежа, используя числовые индексы, начинающиеся с нуля:

print("The status code is (http404Error.0)")
// Выведет "The status code is 404"
print("The status message is (http404Error.1)")
// Выведет "The status message is Not Found"

Вы можете давать имена отдельным элементам кортежа во время объявления:

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

Когда вы присвоили имя элементу кортежа, вы можете обратиться к нему по имени:

print("The status code is (http200Status.statusCode)")
// Выведет "The status code is 200"
print("The status message is (http200Status.description)")
// Выведет "The status message is OK"

Кортежи особенно полезны в качестве возвращаемых значений функций. Функция, которая пытается получить веб-страницу, может вернуть кортеж типа (Int, String), чтобы описать успех или неудачу в поиске страницы. Возвращая кортеж с двумя отдельными значениями разного типа, функция дает более полезную информацию о ее результате, чем, если бы, возвращала единственное значение одного типа, возвращаемое функцией. Для более подробной информации смотрите главу Функции, возвращающие несколько значений.

Заметка

Кортежи полезны для временной группировки связанных значений. Они не подходят для создания сложных структур данных. Если ваша структура данных, вероятно, будет выходить за рамки временной структуры, то такие вещи лучше проектируйте с помощью классов или структур, вместо кортежей. Для получения дополнительной информации смотрите главу Классы и структуры.

Опциональные типы (опционалы)

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

Заметка

В C или Objective-C нет понятия опционалов. Ближайшее понятие в Objective-C это возможность вернуть nil из метода, который в противном случае вернул бы объект. В этом случае nil обозначает «отсутствие допустимого объекта». Тем не менее, это работает только для объектов, и не работает для структур, простых типов C, или значений перечисления. Для этих типов, методы Objective-C, как правило, возвращают специальное значение (например, NSNotFound), чтобы указать отсутствие значения. Этот подход предполагает, что разработчик, который вызвал метод, знает, что есть это специальное значение и что его нужно учитывать. Опционалы Swift позволяют указать отсутствие значения для абсолютно любого типа, без необходимости использования специальных констант.

Приведем пример, который покажет, как опционалы могут справиться с отсутствием значения. У типа Int в Swift есть инициализатор, который пытается преобразовать значение String в значение типа Int. Тем не менее, не каждая строка может быть преобразована в целое число. Строка «123» может быть преобразована в числовое значение 123, но строка «hello, world» не имеет очевидного числового значения для преобразования.

В приведенном ниже примере используется метод Int() для попытки преобразовать String в Int:

let possibleNumber = "123"
let convertedNumber = Int(possibleNumber)
// для convertedNumber выведен тип "Int?", или "опциональный Int"

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

nil

Мы можем установить опциональную переменную в состояние отсутствия значения, путем присвоения ему специального значения nil

var serverResponseCode: Int? = 404
// serverResponseCode содержит реальное Int значение 404
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 contains some integer value.")
}
// Выведет "convertedNumber contains some integer value."

Если вы уверены, что опционал содержит значение, вы можете получить доступ к его значению, добавив восклицательный знак (!) в конце имени опционала. Восклицательный знак фактически говорит: «Я знаю точно, что этот опционал содержит значение, пожалуйста, используй его». Это выражение известно как Принудительное извлечение значения опционала:

if convertedNumber != nil {
    print("convertedNumber has an integer value of (convertedNumber!).")
}
// Выведет "convertedNumber has an integer value of 123."

Более подробную информацию об инструкции if можно получить в главе Управление потоком.

Заметка

Попытка использовать ! к несуществующему опциональному значению вызовет runtime ошибку. Всегда будьте уверены в том, что опционал содержит не-nil значение, перед тем как использовать ! чтобы принудительно извлечь это значение.

Привязка опционалов

Можно использовать Привязку опционалов, чтобы выяснить содержит ли опционал значение, и если да, то сделать это значение доступным в качестве временной константы или переменной. Привязка опционалов может использоваться с инструкциями if и while, для проверки значения внутри опционала, и извлечения этого значения в константу или переменную, в рамках одного действия. Инструкции if и while более подробно представлены в главе Управление потоком.

Привязку опционалов для инструкции if можно писать как показано ниже:

  1. if let constantName = someOptional {
  2.     statements
  3. }

Мы можем переписать пример possibleNumber сверху, используя привязку опционалов, а не принудительное извлечение:

if let actualNumber = Int(possibleNumber) {
    print("(possibleNumber) has an integer value of (actualNumber)")
} else {
    print("(possibleNumber) could not be converted to an integer")
}
// Выведет "123" has an integer value of 123

Это может быть прочитано как:

«Если опциональный Int возвращаемый Int(possibleNumber) содержит значение, установи в новую константу с названием actualNumber значение, содержащееся в опционале.»

Если преобразование прошло успешно, константа actualNumber становится доступной для использования внутри первого ветвления инструкции if. Он уже инициализируется значением, содержащимся внутри опционала, и поэтому нет необходимости в использовании ! для доступа к его значению. В этом примере, actualNumber просто используется, чтобы напечатать результат преобразования.

Вы можете использовать и константы и переменные для привязки опционалов. Если вы хотели использовать значение actualNumber внутри первого ветвления инструкции if, вы могли бы написать if var actualNumber вместо этого, и значение, содержащееся в опционале, будет использоваться как переменная, а не константа.

Вы можете включать столько опциональных привязок и логических условий в единственную инструкцию if, сколько вам требуется, разделяя их запятыми. Если какое-то значение в опциональной привязке равно nil, или любое логическое условие вычисляется как false, то все условия выражения будет считаться 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?), после типа, который вы хотите сделать опциональным.

Вместо того, чтобы ставить восклицательный знак (!) после опционала, когда вы его используете, поместите восклицательный знак (!) после опционала, когда вы его объявляете.

Неявно извлеченные опционалы полезны, когда известно, что значение опционала существует непосредственно после первого объявления опционала, и точно будет существовать после этого. Неявно извлечённые опционалы в основном используются во время инициализации класса, как описано в разделе «Бесхозные ссылки и неявно извлеченные опциональные свойства».

Честно говоря, неявно извлеченные опционалы — это нормальные опционалы, но они могут быть использованы как не опциональные значения, без необходимости в извлечении опционального значения каждый раз при доступе. Следующий пример показывает разницу в поведении между опциональной строкой и неявно извлеченной опциональной строкой при доступе к их внутреннему значению как к явному String:

let possibleString: String? = "An optional string."
let forcedString: String = possibleString! // необходим восклицательный знак

let assumedString: String! = "An implicitly unwrapped optional string."
let implicitString: String = assumedString // восклицательный знак не нужен

Вы можете считать неявно извлеченные опционалы обычными опционалами дающими разрешение на принудительное извлечение, если это требуется. Когда вы используете неявно извлеченный опционал, то Swift сначала пробует использовать его в качестве обычного опционального значения, если так его использовать не получается, то уже пробует принудительно извлечь значение. В коде выше опциональное значение assumedString является принудительно извлеченным прежде, чем будет записано в implicitString, так как implicitString имеет явный неопциональный тип String. В коде ниже optionalString не имеет явного типа, так что является обычным опционалом.

let optionalString = assumedString
// Тип optionalString является "String?" и assumedString не является принудительно извлеченным значением.

Если вы попытаетесь получить доступ к неявно извлеченному опционалу когда он не содержит значения — вы получите runtime ошибку. Результат будет абсолютно тот же, если бы вы разместили восклицательный знак после нормального опционала, который не содержит значения.

Вы можете проверить не является ли неявно извлеченный опционал nil точно так же как вы проверяете обычный опционал:

if assumedString != nil {
    print(assumedString!)
}
// Выведет "An implicitly unwrapped optional string."

Вы также можете использовать неявно извлеченный опционал с привязкой опционалов, чтобы проверить и извлечь его значение в одном выражении:

if let definiteString = assumedString {
  print(definiteString)
}
// Выведет "An implicitly unwrapped optional string."

Заметка

Не используйте неявно извлечённый опционал, если существует вероятность, что в будущем переменная может стать 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, то выполнение кода просто продолжается далее, но если значение равно false, то текущее состояние исполнения программы некорректно и выполнение кода останавливается и ваше приложение завершает работу.

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

В дополнение к сравниванию ваших ожиданий и действительности во время исполнения, и утверждения, и предусловия становятся полезными при использовании, так как они по сути становятся документацией к вашему коду. В отличии от Обработки ошибок, о которых мы с вами говорили чуть ранее, утверждения и предусловия не используются для ожидаемых ошибок, получив которые ваше приложение может восстановить свою работу. Так как ошибка, полученная через утверждение или предусловие является индикатором некорректной работы программы, то нет возможности отловить эту ошибку в утверждении, которое ее вызвало.

Использование утверждений и предусловий не являются заменой проектированию вашего кода таким образом, где вероятность того, что появятся некорректные условия будет маловероятна. Однако их использование для обеспечения правильности данных и состояния заставляет ваше приложение прекращать работу по причине ошибки более предсказуемо, если наступает некорректное состояние, которое помогает быстрее найти эту самую ошибку. Прекращение работы как только наступает некорректное состояние позволяет ограничить ущерб, который вызывается этим самым некорректным состоянием.

Различие между утверждениями и предусловиями в том, когда они проверяются: утверждения проверяются только в сборках дебаггера, а предусловия проверяются и в сборках дебаггера и продакшн сборках. В продакшн сборках условие внутри утверждения не вычисляется. Это означает, что вы можете использовать сколько угодно утверждений в процессе разработки без влияния на производительность продакшена.

Отладка с помощью утверждений

Утверждения записываются как функция стандартной библиотеки Swift assert(_:_:file:line:). Вы передаете в эту функцию выражение, которые оценивается как true или false и сообщение, которое должно отображаться, если результат условия будет false. Например:

let age = -3
assert(age >= 0, "Возраст человека не может быть меньше нуля")
// это приведет к вызову утверждения, потому что age >= 0, а указанное значение < 0.

В этом примере, выполнение кода продолжится, только если age >= 0 вычислится в true, что может случиться, если значение age не отрицательное. Если значение age отрицательное, как в коде выше, тогда age >= 0 вычислится как false, и запустится утверждение, завершив за собой приложение.

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

assert(age >= 0)

Если код уже проверяет условие, то вы используете функцию assertionFailure(_:file:line:) для индикации того, что утверждение не выполнилось. Например:

if age > 10 {
    print("Ты можешь покататься на американских горках и чертовом колесе.")
} else if age > 0 {
    print("Ты можешь покататься на чертовом колесе.")
} else {
    assertionFailure("Возраст человека не может быть отрицательным.")
}

Обеспечение предусловиями

Используйте предусловие везде, где условие потенциально может получить значение false, но для дальнейшего исполнения кода оно определенно должно равняться true. Например, используйте предусловие для проверки того, что значение сабскрипта не вышло за границы диапазона или для проверки того, что в функцию было передано корректное значение.

Для использования предусловий вызовите функцию precondition(_:_:file:line:). Вы передаете этой функции выражение, которое вычисляется как true или false и сообщение, которое должно отобразиться, если условие будет иметь значение как false. Например:

 // В реализации сабскрипта...
precondition(index > 0, "Индекс должен быть больше нуля.")

Вы так же можете вызвать функцию preconditionFailure(_:_:file:line:) для индикации, что отказ работы уже произошел, например, если сработал дефолтный кейс инструкции switch, когда известно, что все валидные значения должны быть обработаны любым кейсом, кроме дефолтного.

Заметка

Если вы компилируете в режиме -0unchecked, то предусловия не проверяются. Компилятор предполагает, что предусловия всегда получают значения true, и он оптимизирует ваш код соответствующим образом. Однако, функция fatalError(_:file:line:) всегда останавливает исполнение, несмотря на настройки оптимизации.

Вы можете использовать функцию fatalError (_:file:line:) во время прототипирования или ранней разработки для создания заглушек для функциональности, которая еще не реализована, написав fatalError («Unimplemented») в качестве реализации заглушки. Поскольку фатальные ошибки никогда не оптимизируются, в отличие от утверждений или предусловий, вы можете быть уверены, что выполнение кода всегда прекратится, если оно встречает реализацию заглушки.

Если вы нашли ошибку, пожалуйста, выделите фрагмент текста и нажмите Ctrl+Enter.

imageПривет, Хабр! 2 июня все мы воочию могли наблюдать, как компания Apple начала творить революцию в стане Objective-C разработчиков, представив миру свой новый язык программирования – Swift. Вместе с этим, она выложила в открытый доступ небольшую документацию по языку, которую мы решили перевести, если на то будет спрос. Предлагаем вашему вниманию перевод первой главы. Если тема будет интересна, то мы продолжим публиковать перевод каждую неделю.

Оглавление

Добро пожаловать в Swift
    О Swift
    Введение в Swift

Language guide
    The Basics
    Basic Operators
    String and Characters
    Collection Types
    Control Flow
    Functions
    Closures
    Enumerations
    Classes and Structures
    Properties
    Methods
    Subscripts
    Inheritance
    Initialization
    Deinitialization
    Automatic Reference Counting
    Optional Chaining
    Type Casting
    Nested Types
    Extensions
    Protocols
    Generics
    Advanced Operators

Language Reference
    About the Language Reference
    Lexical Structure
    Types
    Expressions
    Statements
    Declarations
    Attributes
    Patterns
    Generic Parameters and Arguments
    Summary of the Grammar
    Trademarks

Добро пожаловать в Swift

О языке Swift

Swift – это новый язык программирования для разработки iOS и OS X приложений, который сочетает в себе все лучшее от C и Objective-C, но лишен ограничений, накладываемых в угоду совместимости с C. В Swift используются паттерны безопасного программирования и добавлены современные функции, превращающие создание приложения в простой, более гибкий и увлекательный процесс. Swift, созданый нами с чистого листа, – это возможность заново представить себе, как разрабатываются приложения.

Swift разрабатывался нами несколько лет. Основой нового языка программирования послужили существующие компилятор, отладчик и фреймворки. Мы упростили процесс управления памятью с помощью механизма автоматического подсчета ссылок – Automatic Reference Counting (ARC). Наши фреймворки также подверглись серьезной модернизации. Objective-C начал поддерживать блоки, литералы и модули – все это создало благоприятные условия для внедрения современных технологий. Именно эта подготовительная работа послужила фундаментом для нового языка программирования, который будет применяться для разработки будущих программных продуктов для Apple.

Разработчикам Objective-C Swift покажется знакомым. Он сочетает в себе читабельность именованных параметров и мощь динамической объектной модели Objective-C. Он открывает доступ к уже существующим фреймворкам Cocoa и совместим с кодом, написанным на Objective-C. Построенный на этой общей основе язык предлагает множество новых возможностей и унифицирует процедурные и объектно-ориентированные аспекты языка программирования.

Swift не отпугнет и начинающих программистов. Это первый мощный язык программирования, такой же понятный и увлекательный, как скриптовый язык. Он поддерживает так называемые playground-ы, которые позволяют программистам экспериментировать с кодом, видя результат в режиме реального времени без необходимости компилировать и запускать приложение.

Swift вобрал в себя все лучшее от современных языков и разработан с учетом обширного опыта компании Apple. Наш компилятор – синоним производительности, наш язык оптимизирован для разработки без оглядки на компромиссы. Он спроектирован таким образом, чтобы вы смогли легко разработать и ваше первое приложение «hello, world!», и даже целую операционную систему. Все это делает Swift важным инструментом для разработчиков и для самой компании Apple.

Swift – это новый фантастический способ создавать приложения для iOS и OS X, и мы продолжим развивать его, добавляя новый функционал и представляя новые возможности. Наша цель – амбициозна. И мы с нетерпением ждем, чтобы увидеть, что вы сумеете создать при помощи него.

Введение в Swift

По давней традиции первая программа на новом языке должна выводить на экран слова “Hello, world”. С помощью Swift это делается так:

println("Hello, world")

Если вы когда-нибудь разрабатывали на C или Objective-C этот синтаксис должен казаться вам до боли знакомым – в Swift эта строчка кода является законченной программой. Вам больше не нужно импортировать отдельные библиотеки для обеспечения базового функционала вроде ввода/вывода в консоль или работы со строками. Код, написанный в глобальной области видимости, является точкой входа в программу, таким образом функция main больше не нужна. Также обратите внимание на отсутствие точки с запятой в конце каждой строки.

Это введение содержит достаточно информации, чтобы начать писать код на Swift. Не переживайте, если вам будет что-то непонятно – мы все детально объясним в последующих главах.

Замечание
Для лучшего понимания материала мы рекомендуем использовать режим playground в Xcode. Playground позволяет вам видеть результат сразу в процессе редактирования кода без необходимости компилировать и запускать приложение.

Простые типы данных

Используйте let для создания константы и var для создания переменной. Тип константы указывать не нужно, вы можете присвоить ей значение лишь единожды.

var myVariable = 42
myVariable = 50
let myConstant = 42

Типы константы и переменной должны совпадать с типами присваиваемых им соответствующих значений. Однако это не означает, что вы должны напрямую указывать их тип. Компилятор автоматически определит тип константы и переменной при присваивании им значения. Так, в приведенном примере компилятор определит, что myVariable имеет целочисленный тип.

Если же инициализатор отсутствует или не предоставляет достаточной информации, вы можете указать тип самостоятельно после переменной, разделив название и тип двоеточием:

let implicitInteger = 70
let inplicitDouble = 70.0
let inplicitDouble: Double = 70

Давайте поэкспериментируем
Создайте константу с типом Float и проинициализируйте ее числом 4.

Значения никогда не конвертируются в другой тип неявно. Если вам необходимо конвертировать значение в другой тип, делайте это явно:

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

Давайте поэкспериментируем
Попробуйте удалить явное преобразование к типу String в последней строке. Какую ошибку вы получите?

Имеется более простой способ включения значений в строки: для этого заключите выражение в скобки и поставьте перед ними обратный слэш (). Пример:

let apples = 3
let oranges = 5
let appleSummary = "I have (apples) apples."
let fruitSummary = "I have (apples + oranges) pieces of fruit."

Давайте поэкспериментируем
Попробуйте использовать конструкцию () и выведите на экран строку, включающую результат суммы двух целочисленных переменных и чье-нибудь имя.

При работе с массивами и ассоциативными массивами (словарями, dictionary) используются квадратные скобки ([]):

var shoppingList = ["catfish", "water", "tulips", "blue paint"]
shoppingList[1] = "bottle of water"
 
var occupations = [
    "Malcolm": "Captain",
    "Kaylee": "Mechanic",
]
occupations["Jayne"] = "Public Relations"

Чтобы создать пустой массив или dictionary, используйте следующий синтаксис:

let emptyArray = String[]()
let emptyDictionary = Dictionary<String, Float>()

Для создания пустых массивов и словарей используйте [] и [:] соответственно, – например, когда вы присваиваете новое значение переменной или передаете аргумент в функцию.

shoppingList = []   // Went shopping and bought everything.
Условия и циклы

Для создания условий используются операторы if и switch, для создания циклов – for-in, for, while и do-while. При этом выделять круглыми скобками условия и инициализирующие выражения необязательно, тогда как фигурные скобки обязательны.

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

Условие внутри оператора if должно быть логическим, это в частности означает, что выражение if score {…} является ошибочным, поскольку здесь нет явного сравнения (например, с нулем).

Условный оператор if можно использовать совместно с let и var для работы с константами и переменными, которые могут иметь значение nil. Такие константы и переменные называются опциональными (то есть они могут либо принимать какое-либо значение, либо быть равны nil). Чтобы создать опциональную переменную или константу добавьте знак вопроса (?) после указания типа.

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

Давайте поэкспериментируем
Измените optionalName на nil. Что вы видите на экране? Добавьте блок else для обработки случая, когда optionalName равен nil.

Если опциональное значение равно nil, условие будет ложным и код в фигурных скобках после if выполнен не будет. В противном случае переменной greeting будет присвоено новое значение.

Оператор множественного выбора switch поддерживает внутри себя множество других операторов сравнения и не ограничен лишь простыми сравнениями:

let vegetable = "red pepper"
switch vegetable {
case "celery":
    let vegetableComment = "Add some raisins and make ants on a log."
case "cucumber", "watercress":
    let vegetableComment = "That would make a good tea sandwich."
case let x where x.hasSuffix("pepper"):
    let vegetableComment = "Is it a spicy (x)?"
default:
    let vegetableComment = "Everything tastes good in soup."
}

Давайте поэкспериментируем
Попробуйте удалить условие по умолчанию. Какую ошибку вы получите?

После выполнения подходящего блока кода, программа покидает оператор switch, не проверяя последующие условия. Таким образом вам не нужно вручную добавлять операторы прерывания (break) в конце каждого блока case.

Для перебирания элементов ассоциативного массива используйте оператор for-in совместно с указанием пары имен для каждой пары ключ-значение.

let interestingNumbers = [
    "Prime": [2, 3, 5, 7, 11, 13],
    "Fibonacci": [1, 1, 2, 3, 5, 8],
    "Square": [1, 4, 9, 16, 25],
]
var largest = 0
for (kind, numbers) in interestingNumbers {
    for number in numbers {
        if number > largest {
            largest = number
        }
    }
}
largest

Давайте поэкспериментируем
Добавьте еще одну переменную, которая позволит выяснить, к какому из трех типов относится найденное максимальное число.

Оператор цикла while позволяет выполнять блок кода внутри него до тех пор, пока условие не станет ложным. Условие также может быть указано после блока, который в таком случае будет выполнен по крайней мере один раз.

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

Оператор for можно использовать для перебора последовательности чисел с помощью двух точек (..) или с помощью инициализатора, условия и инкремента. Посмотрите, эти два цикла делают одно и то же:

var firstForLoop = 0
for i in 0..3 {
    firstForLoop += i
}
firstForLoop
 
var secondForLoop = 0
for var i = 0; i < 3; ++i {
    secondForLoop += 1
}
secondForLoop

При создании цикла используйте две точки (..), если не хотите включать большее значение в диапазон, и три точки (), чтобы включить как меньшее, так и большее значения.

Функции и замыкания.

Для объявления функций используйте ключевое слово func. Вызов функции производится через указание ее имени и списка аргументов в круглых скобках. Возвращаемый тип следует отделить от перечня формальных аргументов с помощью ->.

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

Давайте поэкспериментируем
Удалите параметр day. Вместо него добавьте переменную, обозначающую наименование подаваемого на обед блюда.

Если функция возвращает множество значений, следует использовать кортеж:

func getGasPrices() -> (Double, Double, Double) {
    return (3.59, 3.69, 3.79)
}
getGasPrices()

Функции также могут иметь неопределенное число аргументов:

func sumOf(numbers: Int...) -> Int {
    var sum = 0
    for number in numbers {
        sum += number
    }
    return sum
}
sumOf()
sumOf(42, 597, 12)

Давайте поэкспериментируем
Напишите функцию, позволяющую находить среднее арифметическое произвольного числа своих аргументов.

Функции могут вкладываться друг в друга. Вложенная функция может обращаться к переменным, объявленным во внешней функции. Используйте вложенные функции, чтобы привести в порядок код сложной или большой функции.

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

Функции являются объектами первого класса (first-class type), иными словами, функция в качестве свого результата может возвращать другую функцию.

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

Функция также может принимать другую функцию в качестве одного из аргументов.

func hasAnyMatches(list: Int[], condition: Int -> Bool) -> Bool {
    for item in list {
        if condition(item) {
            return true
        }
    }
    return false
}
func lessThanTen(number: Int) -> Bool {
    return number < 10
}
var numbers = [20, 19, 7, 12]
hasAnyMatches(numbers, lessThanTen)

Функции являются частным случаем замыканий. Вы можете создать замыкание, не указывая его имени и окружив тело замыкания фигурными скобками ({}). Для отделения аргументов и типа возвращаемого значения от тела замыкания используйте оператор in.

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

Давайте поэкспериментируем
Перепишите замыкание таким образом, чтобы оно возвращало ноль для всех лишних чисел.

Существует несколько техник, позволяющих делать замыкания более лаконичными. Если тип замыкания априори известен (например, это callback делегата), можно опустить указание типа его параметров и/или типа возвращаемого значения. Замыкания, состоящие из единственного выражения, неявно возвращают результат этого выражения.

numbers.map({ number in 3 * number })

В замыкании вместо указания имени переменной, вы можете использовать ее порядковый номер – это особенно полезно при написании коротких замыканий. Замыкание, являющееся последним аргументом функции, может быть передано в нее сразу после круглых скобок с перечнем остальных параметров.

sort([1, 5, 3, 12, 2]) { $0 > $1 }
Объекты и классы

Для создания класса используется зарезервированное слово class. Члены класса объявляются точно так же, как и обычные константы и переменные. Более того, методы класса объявляются как обычные функции.

class Shape {
    var numberOfSides = 0
    func simpleDescription() -> String {
        return "A shape with (numberOfSides) sides."
    }
}

Давайте поэкспериментируем
Добавьте константу-член класса и метод класса, принимающую ее в качестве своего аргумента.

Чтобы создать экземпляр (объект) класса, достаточно добавить круглые скобки после названия класса. Доступ к методам и членам класса осуществляется через точку.

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

В этом примере мы упустили одну важную деталь – конструктор класса, метод init.

class NamedShape {
    var numberOfSides: Int = 0
    var name: String
    
    init(name: String) {
        self.name = name
    }
    
    func simpleDescription() -> String {
        return "A shape with (numberOfSides) sides."
    }
}

Обратите внимание, как член класса name при помощи self отделен от аргумента конструктора name. Аргументы передаются в конструктор обычным образом, как и в любой другой метод класса. Обратите внимание на то, что каждый член класса должен быть проинициализирован – либо при объявлении (как, например, numberOfSides), либо в конструкторе (как name).

Деструктор класса – метод deinit, который можно переписать в случае необходимости.

Чтобы наследовать класс от уже существующего класса, после указания имени дочернего класса следует поставить двоеточие и указать название родительского. В Swift нет никаких ограничений по обязательному наследованию какого-либо стандартного класса.

Переопределенные дочерним классом методы должны быть помечены ключевым словом override – переопределение методов без override приведет к ошибке. Компилятор также выявляет методы, маркированные override, но не переопределяющие какие-либо методы своего родительского класса.

class Square: NamedShape {
    var sideLength: Double
    
    init(sideLength: Double, name: String) {
        self.sideLength = sideLength
        super.init(name: name)
        numberOfSides = 4
    }
    
    func area() ->  Double {
        return sideLength * sideLength
    }
    
    override func simpleDescription() -> String {
        return "A square with sides of length (sideLength)."
    }
}
let test = Square(sideLength: 5.2, name: "my test square")
test.area()
test.simpleDescription()

Давайте поэкспериментируем
Создайте класс Circle и наследуйте его от класса NamedShape. Конструктор класса Circle принимает два аргумента – радиус и название. Переопределите методы area и describe этого класса.

Члены класса могут также иметь собственные getter и setter.

class EquilateralTriangle: NamedShape {
    var sideLength: Double = 0.0
    
    init(sideLength: Double, name: String) {
        self.sideLength = sideLength
        super.init(name: name)
        numberOfSides = 3
    }
    
    var perimeter: Double {
    get {
        return 3.0 * sideLength
    }
    set {
        sideLength = newValue / 3.0
    }
    }
    
    override func simpleDescription() -> String {
        return "An equilateral triagle with sides of length (sideLength)."
    }
}
var triangle = EquilateralTriangle(sideLength: 3.1, name: "a triangle")
triangle.perimeter
triangle.perimeter = 9.9
triangle.sideLength

В setter-е переменной perimeter новое присваиваемое значение неявно называется newValue. Вы можете изменить название этой переменной, указав его в скобках сразу после set.

Обратите внимание на структуру конструктора класса EquilateralTriangle. Этот метод включает в себя три последовательных шага:

  1. инициализация членов дочернего класса;
  2. вызов конструктора родительского класса;
  3. изменение значений членов родительского класса.

Если вам необходимо выполнить определенный код до или после присваивания нового значения переменной, вы можете переопределить методы willSet и didSet нужным вам образом. Например, в приведенном ниже классе гарантируется, что длина стороны треугольника всегда будет равна длине стороны квадрата.

class TriangleAndSquare {
    var triangle: EquilateralTriangle {
    willSet {
        square.sideLength = newValue.sideLength
    }
    }
    var square: Square {
    willSet {
        triangle.sideLength = newValue.sideLength
    }
    }
    init(size: Double, name: String) {
        square = Square(sideLength: size, name: name)
        triangle = EquilateralTriangle(sideLength: size, name: name)
    }
}
var triangleAndSquare = TriangleAndSquare(size: 10, name: "another test shape")
triangleAndSquare.square.sideLength
triangleAndSquare.triangle.sideLength
triangleAndSquare.square = Square(sideLength: 50, name: "larger square")
triangleAndSquare.triangle.sideLength

У методов классов имеется одно важное отличие от функций. Названия аргументов функции используются только в пределах этой функции, тогда как в методе класса параметры также используются при вызове этого метода (кроме первого параметра). По умолчанию метод класса имеет одинаковые названия параметров как при вызове, так и внутри себя. Однако вы можете указать другое название (в примере ниже – times), которое будет использовано только внутри этого метода. При этом для вызова этого метода необходимо использовать первое название (numberOfTimes).

class Counter {
    var count: Int = 0
    func incrementBy(amount: Int, numberOfTimes times: Int) {
        count += amount * times
    }
}
var counter = Counter()
counter.incrementBy(2, numberOfTimes: 7)

При работе с опциональными значениями добавьте знак вопроса (?) перед методами, членами класса и т.д. Если значение перед знаком вопроса равно nil, все, что следует после (?) игнорируется и значение всего выражения равно nil. В противном случае выражение вычисляется обычным образом. В обоих случаях результатом всего выражения будет опциональное значение.

let optionalSquare: Square? = Square(sideLength: 2.5, name: "optional square")
let sideLength = optionalSquare?.sideLength
Перечисления и Структуры

Для создания перечислений используется ключевое слово enum. Отметим, что перечисления также могут иметь в своем составе методы.

enum Rank: Int {
    case Ace = 1
    case Two, Three, Four, Five, Six, Seven, Eight, Nine, Ten
    case Jack, Queen, King
    func simpleDescription() -> String {
        switch self {
        case .Ace:
            return "ace"
        case .Jack:
            return "jack"
        case .Queen:
            return "queen"
        case .King:
            return "king"
        default:
            return String(self.toRaw())
        }
    }
}
let ace = Rank.Ace
let aceRawValue = ace.toRaw()

Давайте поэкспериментируем
Напишите функцию, которая сравнивает 2 перечисления типа Rank по их значениям.

В вышеприведенном примере элементы перечисления первоначально имеют целочисленный тип, и вам достаточно указать значение только первого элемента – значения остальных элементов будут определены в соответствии с порядком их следования. В качестве исходного типа (raw value) значений элементов вы также можете выбрать строковый или вещественные типы.

Для преобразования исходного типа значения в тип перечисления используйте функции toRaw и fromRaw.

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

Отметим, что значения элементов перечисления являются фактическими, а не просто иной записью своих исходных значений. Вообще говоря, вы можете и не указывать их исходные значения.

enum Suit {
    case Spades, Hearts, Diamonds, Clubs
    func simpleDescription() -> String {
        switch self {
        case .Spades:
            return "spades"
        case .Hearts:
            return "hearts"
        case .Diamonds:
            return "diamonds"
        case .Clubs:
            return "clubs"
        }
    }
}
let hearts = Suit.Hearts
let heartsDescription = hearts.simpleDescription()

Давайте поэкспериментируем
Добавьте метод Color, возвращающий строку “black” для Spades и Clubs и “red” для Hearts и Diamonds.

Обратите внимание на то, как осуществляется доступ к члену Hearts перечисления Suit. При присваивании значения константе hearts используется полное имя Suit.Hearts, поскольку мы явно не указываем тип этой константы. А в switch мы используем сокращенную форму .Hearts, поскольку тип значения self априори известен. Вы можете использовать сокращенную форму повсеместно, если тип переменной явно указан.

Для создания структур используется ключевое слово struct. Структуры имеют множество схожих черт с классами, включая методы и конструкторы. Одним из наиболее существенных отличий структур от классов является то, что экземпляры структур, в отличие от экземпляров классов, передаются в функции по значению (то есть предварительно создается их локальная копия), тогда как экземпляры классов передаются по ссылке.

struct Card {
    var rank: Rank
    var suit: Suit
    func simpleDescription() -> String {
        return "The (rank.simpleDescription()) of (suit.simpleDescription())"
    }
}
let threeOfSpades = Card(rank: .Three, suit: .Spades)
let threeOfSpadesDescription = threeOfSpades.simpleDescription()

Давайте поэкспериментируем
Добавьте в структуру Card метод, который создает полную колоду карт.

Экземпляр члена перечисления может иметь собственные значения и они могут быть разными. Вы присваиваете эти значения при создании экземпляра перечисления (константа success в примере). Связанные и исходные значения это разные вещи: исходное значение члена перечисления всегда постоянно для всех экземпляров перечисления и указывается при его объявлении.

Рассмотрим пример получения с сервера времени восхода и заката Солнца. Сервер отправляет в ответ либо соответствующую информацию, либо сообщение об ошибке.

enum ServerResponse {
    case Result(String, String)
    case Error(String)
}
 
let success = ServerResponse.Result("6:00 am", "8:09 pm")
let failure = ServerResponse.Error("Out of cheese.")
 
switch success {
case let .Result(sunrise, sunset):
    let serverResponse = "Sunrise is at (sunrise) and sunset is at (sunset)."
case let .Error(error):
    let serverResponse = "Failure...  (error)"
}

Давайте поэкспериментируем
Добавьте третий вариант в оператор множественного выбора switch

Обратите внимание, каким образом из объекта ServerResponse “вытаскиваются” время восхода и заката.

Протоколы и Расширения.

Для объявления протокола используйте ключевое слово protocol.

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

Протоколы могут поддерживаться классами, перечислениями и структурами.

class SimpleClass: ExampleProtocol {
    var simpleDescription: String = "A very simple class."
    var anotherProperty: Int = 69105
    func adjust() {
        simpleDescription += "  Now 100% adjusted."
    }
}
var a = SimpleClass()
a.adjust()
let aDescription = a.simpleDescription
 
struct SimpleStructure: ExampleProtocol {
    var simpleDescription: String = "A simple structure"
    mutating func adjust() {
        simpleDescription += " (adjusted)"
    }
}
var b = SimpleStructure()
b.adjust()
let bDescription = b.simpleDescription

Давайте поэкспериментируем
Создайте перечисление, которое будет реализовывать этот протокол.

Обратите внимание на ключевое слово mutating в определении структуры SimpleStructure, которое информирует компилятор о том, что соответствующий метод подвергает структуру изменениям. В противовес этому методы класса SimpleClass не нужно маркировать как mutating, поскольку методы класса всегда могут беспрепятственно его изменять.

Для добавления новых методов или членов класса в уже существующий тип необходимо использовать расширения – extensions. Вы также можете использовать расширения для реализации протокола уже существующим типом, даже если он импортирован из какой-либо библиотеки или фреймворка.

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

Давайте поэкспериментируем
Создайте расширение типа Double с переменной-членом absoluteValue.

Вы можете использовать название протокола как и любой другой тип – например, чтобы создать массив объектов разного типа, но реализующих общий протокол. Заметьте, что при работе с объектами такого типа методы, объявленные вне протокола, будут недоступны.

let protocolValue: ExampleProtocol = a
protocolValue.simpleDescription
// protocolValue.anotherProperty  // Uncomment to see the error

Несмотря на то, что во время выполнения программы переменная protocolValue имеет тип SimpleClass, компилятор считает, что ее тип – ExampleProtocol. Это означает, что вы не сможете случайно получить доступ к методам или членам класса, которые реализуются вне протокола ExampleProtocol.

Обобщенные типы (generics)

Для создания обобщенного типа, заключите имя в угловые скобки (<>).

func repeat<ItemType>(item: ItemType, times: Int) -> ItemType[] {
    var result = ItemType[]()
    for i in 0..times {
        result += item
    }
    return result
}
repeat("knock", 4)

Создавайте обобщенные функции, классы, перечисления и структуры.

// Reimplement the Swift standard library's optional type
enum OptionalValue<T> {
    case None
    case Some(T)
}
var possibleInteger: OptionalValue<Int> = .None
possibleInteger = .Some(100)

Если вы хотите задать обобщенные типу определенные требования, такие, как, например, реализация протокола или требование быть наследованным от определенного класса, используйте where.

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

Давайте поэкспериментируем
Измените функцию anyCommonElements таким образом, чтобы она возвращала массив общих элементов.

В простых случаях вы можете опустить where и написать имя протокола или класса после двоеточия. Выражение <T: Equatable> эквивалентно выражению <T where T: Equatable>.

Хотите внедрить подписки в iOS-приложение за 10 минут? Интегрируйте Apphud и:
— оформляйте покупки с помощью лишь одного метода;
— автоматически отслеживайте состояние подписки каждого пользователя;
— легко интегрируйте Subscription Offers;
— отправляйте события о подписках в Amplitude, Mixpanel, Slack и Telegram с учетом локальной валюты пользователя;
— уменьшайте Churn rate в приложениях и возвращайте отписавшихся пользователей.

Создать Playground

Запустим Xcode и создадим Playground. Назовем его “BasicSwift”:

File → New → Playground…

⌥⇧⌘N

Swift Playground – это такой тип файлов, который представляет из себя игровую площадку для ваших экспериментов с кодом Swift. Результат исполнения каждой строчки кода вы можете наблюдать в боковой панели (Sidebar).

Переменные, константы, типы

Переменные обозначаются с помощью ключевого слова var

        var hello = "Hello Proglib"
    

Чтобы объявить константу используется ключевое слово let

        let brandName = "Proglib"
    

Swift – это статистический типизированный язык, а это значит, что мы должно явно задать тип каждой переменной во время компиляции.

Указывать тип переменной необязательно. Компилятор Swift автоматически его определит. Эта особенность называется интерференцией типов(type inference).

Swift предлагает следующие базовые типы: Int или UInt, Double, Float, String, Character, Bool, Tuples, Optional.

Множество типов фундаментально и чаще всего используется для объявления переменных. Забегая вперед скажу, что вы также можете создавать и свои собственные типы.

Мы не будет детально разбирать их все! Посвятим этой теме следующую 30-ти минутку.

Укажем тип нашей константы.

        let brandName: String = "Proglib"
    

Интерполяция строк

Давайте теперь выведем имя бренда в консоль при помощи функции print().

        print(brandName)
    

Интерполяция – это способ объединить переменные и константы внутри строки.

Теперь присвоим переменной hello новое значение, и выведем в консоль.

        hello = "Hello (brandName)"
print(hello)
    

Вот еще пример:

        let name = "Ivan" 
var age: UInt8 = 30
print("(hello). My name is (name). I am (age)")

    

Базовые операторы

Здесь все интуитивно. Предлагаю вам поэкспериментировать с ними самостоятельно и посмотреть на результаты.

Арифметические операторы

+ сложение
вычитание
/ деление
% деление по модулю
        var a = 1, b = 2
var result = a + b
// etc

    

Операторы присвоения

= , +=, -=, /=, %=

        var h = 10
h += 1
h /= 2
h %= 2
// etc

    

Операторы сравнения

Операторы сравнения в качестве результата возвращают значения типа Bool

==, ≠, >, <, ≥, ≤

        let condition = 2 > 3
let condition2 = 1 != 0
let resultCondition = condition == condition2
// etc

    

Логические операторы

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

Рассмотрим на примере:

        // логическое И
print(true && true)      // true
print(true && false)     // false

// логическое ИЛИ
print(true || false)      // true

// логическое НЕ – инвертирует булево значение
print(!true)              // false

    

Это еще не все операторы, которые существуют в Swift. Остальные мы рассмотрим в контексте целей их предназначения.

Коллекции

Swift предлагает нам три основных типа коллекций: Array(Массивы), Dictionary(Словари) и Set(Множества). Сейчас мы рассмотрим первые два.

Array

Используйте квадратные скобки [], чтобы создавать массивы(Array) и словари(Dictionary).

Получить доступ к элементам массива можно написав индекс внутри скобок.

        let digits: [UInt8] = [0, 1, 2, 3, 4, 5, 6, 7, 8 ,9]
 print(digits[3])

    

Попробуйте изменить значение в массиве digits. Что произойдет?

        digits[0] = 1
    

🪲

Cannot assign through subscript: ‘digits’ is a ‘let’ constant

Поскольку мы объявили массив как константу, то и все его значения становится не изменчивыми (immutable), как и его размер. А если мы объявим массив как переменную, то он будет изменчивым (mutable). Такое свойство называют изменчивостью коллекций (mutability of collection).

Это утверждение справедливо и для Словарей.

        // mutable
var numbers = [50, 10, 20, 34, 45]
print(numbers[0])
numbers[0] = numbers[0] + 1

    

Dictionary<Key, Value>

Создадим словарь sunSystemData, в который добавим данные о солнечной системе.

Пусть ключами (key) данного словаря будут номера объектов солнечной системы, а значениями (value) названия объектов. Поскольку звезда рождается первой, то будем считать, что ключ со значением 0 всегда указывает на звезду. Остальные объекты следуют в порядке по отношению к звезде.

        var sunSystemData = [ 0: "Sun", 1:"Mercury", 2:"Venus", 
	3:"Earth", 4:"Mars", 5:"Jupiter", 6:"Saturn", 7:"Uranus", 8:"Neptune"]

    

Мы не указали типы для словаря явно. Вы можете проверить как их определил Swift, используя функцию стандартной библиотеки Swift (Swift Standard Library) type(of: T).

        type(of: sunSystemData) // Dictionary<Int, String>

    

Обратиться к элементу словаря можно, написав ключ внутри скобок. Результат будет содержать значение опционального типа (Optional), а если по заданному ключу (в скобках) нет соответствующего элемента, то получим nil.

nil в Swift означает отсутствие значения.

Получим нашу родную планету и выведем ее в консоль.

        var homePlanet = sunSystemData[3]
print("My home is (homePlanet)")

    

Когда мы попытались вывести переменную в консоль, то получили предупреждение!

🪲

String interpolation produces a debug description for an optional value; did you mean to make this explicit?

Давайте посмотрим какой именно тип приняла переменная.

        type(of: homePlanet) // Optional<String>

    

Чтобы избавиться от предупреждения в интерполяции строки, нам необходимо указать что именно мы выводим. Сделать это можно при помощи оператора as. В этом случаи мы увидим напечатанный nil , когда значение отсутствует. Попробуйте обратиться к элементу с ключом 0.

        print("My home is (sunSystem[0] as String?)") // My home is nil
print("My home is (homePlanet as String?)") // My home is Earth
    

Другим решением может быть присвоение переменной значения по умолчанию.

Сделать это мы можем при помощи оператора объединения по nil??

        var homePlanet = sunSystem[3] ?? "Unknown planet"
print("My home is (homePlanet)") // My home is Earth

    

Пока опустим темы об опциональных типах и приведении типов(type casting).

В следующих статьях я расскажу о них подробней.

Продолжим!

Кстати, если вы считаете, что Плутон это планета, то можете добавить её самостоятельно!

        sunSystem[9] = "Pluto"

    

Чем отличаются массивы от словарей?

  • Массивы хранят множество значений определенного типа в упорядоченном списке. Любое значение может присутствовать несколько раз в списке.
  • Словари хранят множество значение определенного типа, где каждое значение связано с уникальным ключом, который идентифицирует это значение внутри словаря. Элементы в словаре не имеют определенного порядка, как в массивах.

Используйте словари, как вы их используете в реальном мире. Когда вам необходимо искать значения на основе их идентификатора (ключа).

Например:

        var HierarchyOfNumbers: [Character:String] = [
	"N":"Natural Numbers"
	"Z":"Integers"
	"Q":"Rational Numbers"
	"R":"Real Numbers"
	"C":"Complex Numbers"
]

    

Управление потоком

Часто необходимо выполнять различный код базирующийся на определенных условий (conditions).

  • Если вам необходимо написать различную логику, которая зависит от определенных условий, тогда используйте условные инструкцииif, switch. Инструкцияifхорошо подходит для простых сравнений, и нескольких вариантов исходов.
  • Инструкция switch подходит для более сложных условий. Лучше использовать ее, когда вам необходимо выбрать из множества вариантов альтернатив, основываясь на каком-либо значении, т.е. выполнить код соответствующий определенному шаблону.
  • Когда вам необходимо многократно повторить какие либо инструкции, используйте циклы: for-in,while.

If…else

Инструкция if бывает трех форм.

Традиционное ее представление выглядит так:

        var condition = true
if condition {
	// 
} else {
	//
}

    

В зависимости от условия, мы попадаем в определенные блоки кода. Когда условие истинно(true), выполняется блок следующий за if, а когда ложно(false), выполняется блок следующий за else.

Например, вы решили, что Плутон планета? Сейчас узнаем!

        var isPluto = sunSystemData[9] != nil

if isPluto {
	print("Pluto is planet")
} else {
	print("Pluto is not planet")
}

    

if…

Мы можем опустить блок else, когда нам необходимо выполнить только одно условие.

Рассмотрим такой случай. У нас есть данные, и мы не хотим перезаписать значение. Тогда необходимо проверить наличие данных, и только после добавить значение.

        var key = 0, value = "Sun"
var isExists = sunSystemData[key] != nil // false
if !isExists { // true
	sunSystemData[key] =  value
}

    

if…else if…else

Также мы можем добавить условия следующее за else.

        var value = 0
if value > 0 {
	print("(value)")
} else if value == 0  {
	print(value)
} else {
	print(value)
}

    

Вы можете комбинировать if else.

Например:

        var a = 0, b = 3, c = 1
if a > b {
	print(a - b)
} else if b > a { 
	print(b - a)
} else if c < a {
	print(a - c)
} else {
	 print(a + b + c)
}

    

switch

В самой простой форме switch выглядит так:

        var value = "R2-D2"
switch value {
	case "R2-D2": print("Take him!")
	case "C-3PO": print("Take him!")
	default: 
		print("These aren't the droids you're looking for.")
}

    

Ключевое слово case определяет возможный случай (шаблон), с которым сравнивается значение value. Затем исполняется соответствующий блок кода на основе первого успешно совпавшего шаблона. В случаи если совпадений не найдено, исполняется блок default , который всегда следует после остальных случаев. Условие default гарантирует что наша конструкция полная и законченная.

Поменяйте значение value, и посмотрите, какой блок кода исполнился.

for-in

Цикл for-in упрощает перебор по последовательностям, т.к. массивы, словари, строки.

Синтаксис for-in выглядит так:

        for value in sequence{
  // 
}

    

Цикл for-in исполняет инструкции определенное количества раз, пока мы не достигнем конца последовательности.

valueэлемент последовательности на каждой итерации(iteration, повторении).

Рассмотрим пример перебора значений массива и словаря.

        for digit in digits  {
	print(digit)
}
for (key, value) in sunSystemData {
	print("Key:(key) Value:(value)")
}

    

while

while имеет две формы.

Первая из них начинается с вычисления условия. Такой цикл можно рассматривать как повторяющаяся инструкция if.

        var condition = true;
while (condition) {
	// тело цикла
}

    

Например, мы хотим пройти 10 000 шагов.

        var stepGoal = 10_000
var mySteps = 0
while mySteps < 10_000 {
	mySteps += 1
}

    

Цикл будет работать пока условие mySteps < 10_000 принимает булево значение true.

Внутри тела цикла мы увеличиваем переменную mySteps на один шаг.

Когда условие принимает значение false, то цикл останавливается.

Расчет факториала числа.

        var counter = 5                
var factorial = 1 // начальное значение             

while counter > 0 {            
    factorial *= counter       
    counter -= 1               
}

print(factorial)  

    

Здесь я вам предлагаю разобраться самостоятельно ;)

Вторая форма исполняет тело цикла прежде, чем вычислит условие. Следовательно, мы можем быть уверены что тело цикла выполняется один раз.

В остальном repeat-while ведет себя идентично while.

        var condition = true
repeat {
	// тело цикла 
} while (condition)

    

Также цикл while используется, когда нам заранее неизвестно число итераций последовательности. В отличии от цикла for-in.

Функции

В Swift есть два типа функций:

Пользовательские(User-defined Function) и функции стандартной библиотеки (Standard Library Functions).

Функции помогают организовывать код, разбив его на более мелкие части, что делает код более легким для понимания и переиспользования. Мы уже использовали функции стандартной библиотеки, такие как print() и type(). Рассмотрим, как создаются пользовательские функции.

Функция – блок кода, который выполняет конкретную задачу.

Каждая функция имеет уникальное имя, которое является ее идентификатором.

        func functionName(parameters) -> returnType {
  // тело функии 
}

    

Объявить функцию можно при помощи ключевого слова func.

За ним следует имя функцииfunctionName.

В скобках указываются параметры(parameters) функции.

Параметр – это значение определенного типа, принимаемое функцией.

returnType – указывает, какого типа значение возвращает функция

Чтобы исполнить функцию, ее необходимо вызвать (как мы уже делали это с print()).

        // вызов функции
functionName()

    

Функции без параметров

Функции в которые мы не передаем параметры, называются функциями без параметров. Например, приветствие, которое мы написали в самом начале может быть организовано в виде функции.

        func greetings() {
	 print("Hello Proglib!")
}
greetings()

print("Done!")

    

Мы объявили функцию с именем greetings() , которая просто печатает "Hello Proglib!" в консоли. Она не имеет параметров и возвращаемого значения. При вызове функции управление программой переходит к определению функции. Затем исполняется код, который содержится внутри тела функции:

        print("Hello Proglib!")
    

Как только функция завершится, управление потоком переходит к следующей инструкции после вызова функции.

        print("Done!")
    

Функции с параметрами

Функции, в которым мы передаем аргументы, называются функциями с параметрами. Если мы хотим, чтобы функция возвращала значение, нужно использовать оператор return.

Например, разница между двумя значениями:

        func difference(a: Int, b: Int) -> Int {
	return a - b 
}
var result = difference(a: 3, b: 2)
print(result)

    

Мы объявили функцию с именем difference, которая принимает два параметра типа Int и возвращает значение типа → Int. При вызове функции, мы передаем параметры в круглых скобках (a: 3, b: 2).

Каждый параметр функции состоит из метки аргумента и имени параметра. Имена параметров используются при реализации функции.

        func difference(of a: Int, less b: Int) -> Int {
  return a - b
}

    

Метка аргумента используется при вызове функции.

        difference(of: 1, less: 2)
    

По умолчанию параметры используют свое имя параметра в качестве метки аргумента(как в первом примере).

Если вам не нужна метка для аргумента, то напишите _ вместо конкретного названия метки для аргумента.

        func difference(_ x: Int, _ y: Int) -> Int {
  return x - y
}

difference(3, 4)

    

Мы можем опустить оператор return , когда тело функции является одним выражением.

        func difference(a: Int, b: Int) -> Int {
	a - b 
}

    

Заключение

Мы познакомились с фундаментальным синтаксисом языка программирования Swift. Рассмотрели основные операторы, научились объявлять переменные и объединять код в функции. В следующие полчаса разберем каждую тему более глубоко. На сегодня все! Playground доступен на Github.

Последнее обновление: 16.09.2022

  1. Глава 1. Введение в Swift

    1. Язык Swift и платформы iOS и Mac OS

    2. Начало работы с Swift и XCode

  2. Глава 2. Основы Swift

    1. Переменные и константы. Типы данных

    2. Числовые типы. Операции с числами

    3. Преобразование числовых данных

    4. Поразрядные операции с числами

    5. Строки. Типы Character и String

    6. Тип Bool. Условные выражения

    7. Кортежи

    8. Условная конструкция If. Тернарный оператор

    9. Конструкция switch

    10. nil и опциональные типы

    11. Циклы

    12. Функции

    13. Возвращение функцией значения

    14. Дополнительно о параметрах функций

    15. Функция как значение. Тип функции

    16. Вложенные и рекурсивные функции

    17. Перегрузка функций

    18. Замыкания

  3. Глава 3. Объектно-ориентированное программирование

    1. Классы и объекты

    2. Инициализаторы

    3. Свойства

    4. Статические свойства и методы

    5. Структуры

    6. Перечисления

    7. Значимые и ссылочные типы

    8. Наследование

    9. Свойства и методы класса

    10. Вложенные типы

    11. Полиморфизм

    12. Преобразование типов

    13. Обобщения

  4. Глава 4. Коллекции

    1. Последовательности

    2. Массивы

    3. Множества

    4. Словари

    5. Сабскрипты

  • Глава 1. Введение в Swift
    • Язык Swift и платформы iOS и Mac OS
    • Начало работы с Swift и XCode
  • Глава 2. Основы Swift
    • Переменные и константы. Типы данных
    • Числовые типы. Операции с числами
    • Преобразование числовых данных
    • Поразрядные операции с числами
    • Строки. Типы Character и String
    • Тип Bool. Условные выражения
    • Кортежи
    • Условная конструкция If. Тернарный оператор
    • Конструкция switch
    • nil и опциональные типы
    • Циклы
    • Функции
    • Возвращение функцией значения
    • Дополнительно о параметрах функций
    • Функция как значение. Тип функции
    • Вложенные и рекурсивные функции
    • Перегрузка функций
    • Замыкания
  • Глава 3. Объектно-ориентированное программирование
    • Классы и объекты
    • Инициализаторы
    • Свойства
    • Статические свойства и методы
    • Структуры
    • Перечисления
    • Значимые и ссылочные типы
    • Наследование
    • Свойства и методы класса
    • Вложенные типы
    • Полиморфизм
    • Преобразование типов
    • Обобщения
  • Глава 4. Коллекции
    • Последовательности
    • Массивы
    • Множества
    • Словари
    • Сабскрипты

Помощь сайту

YooMoney:

410011174743222

Перевод на карту

Номер карты:

4048415020898850

Updated April 19, 2021 | By: Chris Ching | Technical Editors: David Ruvinskiy and Sudeshna Pontula

If you want to learn Swift programming (even if you’ve never coded before), then you’re in the right place!

In this Swift tutorial, you’ll learn how to read and write Swift code, complete exercises, and ultimately become a Swift wiz!

I’ve also compiled a Swift cheat sheet for beginners which you can download and keep beside you as you learn. It covers the most common syntax that you’ll learn in the tutorial below.

Another good supplementary resource is Apple’s own Swift programming language guide that contains in-depth technical documentation of the Swift programming language.

If you prefer to learn via video tutorials instead, check out the video version of this tutorial here.

Table of Contents:

Chapter 0. Xcode Setup


Chapter 1. Variables and Constants


Chapter 2. Data Types


Chapter 3. Swift Math Operators


Chapter 4. If Statement


Chapter 5. Switch Statement


Chapter 6. For-In Loop


Chapter 7. While Loop


Chapter 8. Functions Pt. 1


Chapter 9. Functions Pt. 2


Chapter 10. Classes


Chapter 11. Subclassing


Chapter 12. UIKit


Chapter 13. Initializers


Chapter 14. Optionals


Chapter 15. Properties


Chapter 16. More Initializers


Chapter 17. Arrays


Chapter 18. Dictionaries



Xcode Setup

Chapter 0: Xcode Setup

Getting Started

In order to get started with making iOS apps, you need to download a program called Xcode from the Mac App Store. Xcode is what is known as an integrated development environment or IDE for short. In general terms, an IDE is simply a program that provides you with the necessary tools to write a piece of software. Xcode in particular helps you run and test Swift code on your computer.

Start a New Playground

Once you have Xcode installed and launched, you should see the following welcome dialogue:

Welcome to Xcode

If you don’t get this welcome dialogue, then you can always go up to the “File” menu, go under “New,” and then choose “Playground.”

Xcode File Menu

Click on “Get started with a playground.” The dialog that pops up allows you to choose what type of playground you want to create. For the purposes of this article choose “Blank” under “iOS.”

Blank Playground

You can save your playground anywhere you want. Make sure you remember where you save it so you can easily access it. In my case, I usually save it on my desktop.

Different Elements of the Playground

Here are the most important elements of the playground that you need to focus on for now:

1. Code editor – this is where you’re going to be typing your Swift code.

2. Line numbers – these will help you refer to different lines of code.

If you don’t have line numbers and you want to enable them, then just go to Xcode > Preferences > Text Editing > Line Numbers, and you can turn those line numbers on or off.

You’ll also notice that when you hover your mouse over the different lines, a blue play icon follows. Clicking on the play icon executes the code highlighted in blue on the left. For example, if I hover over line 2 below and click play, Xcode will only run the first line of code.

However, if I hover over line 4 and run it, then Xcode will run all the code up to and including that point.

3. Status bar – tells you the current status of the playground.

Xcode Status Bar

If the status says that it is ready for you, Xcode is ready to accept your code and run it.

4. Show/Hide Debug – allows you to hide or show the debug or console area: the place where we’re going to be testing our Swift code.

Xcode Show/Hide Dialogs

5. Execute Playground – runs all the code in your playground

Holding down the play button gives you two options: “Automatically Run” and “Manually Run.”

Swift Playground automatic or manual execution

The “Manually Run” mode means you need to click either click this play button or the blue play icon to run your code. Otherwise, setting it to “Automatically Run” means Xcode will automatically execute your playground and update the results every time you edit the code.

The automatic mode can be a little buggy at times. Sometimes Xcode won’t update the results when it’s constantly running your playground. If this happens, just click the stop button (same place as the play button), and hold down the play button to change it to “Manually Run.” When the status bar says “Ready,” you will be able to click and run your code again.

Variables and Constants

Chapter 1: Variables and Constants

When you’re coding, you need to have the mindset that you are giving the computer instructions on how to process data. If you were to build a stock portfolio app, for example, then you’d have to write code to tell the computer where to grab the stock prices, tell the computer how to manipulate the prices, and come up with the percentage lost or percentage gained. Then, you’d have to write code to instruct the computer how to display that to the user in a table format.

Data in an app

Similarly, to build a photo app like Instagram, you’d have to write code to tell the computer where to grab the images and how to display them to the user. Furthermore, if the user tries to upload a photo to their account, you’ll need to have code explaining how to take the image data from the user’s phone and upload it to a file server and make an entry in a database.

Thus, you’re always working with data when building an app. In this lesson, we are going to show some simple pieces of data to you. Take a look at the playground below:

Here, we have different types of data. Data can be numbers like 123 and the decimal 0.7 as shown on lines 3 and 4. We can even have text data like "Hello" surrounded by double quotes on line 5.

Swift Variables

Your computer stores data in its memory to keep track of where the data is and what it is. For us to work with data, we need a way to refer to it in our programs. This is where variables come in, to help keep track of data within an app.

Let’s learn how to create, or declare, a new variable to track a piece of data in memory. First, type the keyword var, followed by a space, and your variable’s name. You can name it anything you want, but the name should clearly describe the data the variable will contain. For example, if I had a piece of data for someone’s first name, I might name my variable firstName.

Following your variable name, add an equal sign and then the piece of data you want to keep track of. Since we named this variable “firstName,” we’ll give it a piece of text data, namely the word “Tom.” Note: the single equal sign lets us assign a piece of data to the firstName variable. A complete example of the variable declaration is given below.

var firstName = "Tom" 

Here’s how our variable looks in the playground:

We’ve created a new variable called firstName and assigned the text data “Tom” to it. Now, whenever we want to work with that piece of data, “Tom”, we can just use our variable firstName in its place.

Let’s use firstName by printing “Tom” to the console. We do this with the print() command, which outputs to the console whatever data you put in the middle of its parentheses. In that case, let’s add firstName in the parentheses and click the play button to execute our code:

As shown, we successfully printed our variable’s data, “Tom”. Remember, the print() command tells Xcode to print the data that the variable firstName references.

Although we’ve only worked with text data until now, you can store a variety of data in variables. For example, I can declare a variable called stockPrice and set it equal to the number 100:

var stockPrice = 100 

Something to note when working with variables is that you can always change a variable’s value when you decide it should store something different. Check out the example below:

Here, we declare a variable, stockPrice, and set it to 100 on line 3, then print it out on line 4. Afterwards, we reassign the value of stockPrice to 50 on line 6 and print it again on line 7. Note: we use the var keyword only to create (declare) new variables. We don’t need var to change the value of an existing variable. Where did the original value of 100 go after we changed stockPrice to 50? Well, when you change the value a variable stores, you lose track of its old data and can no longer access it.

So far, it looks like variables are quite flexible since they can store whatever we want. However, you cannot assign data of a different type to what your variable originally had. For example, because stockPrice initially had a number, we can’t give it text data later on:

Xcode ends up giving us an error if we try assigning “Tom” to stockPrice. This is because stockPrice only expects to store number data. When we talk about data types in the next lesson, I will explain why this is so in more detail. For now, just remember that variables only expect to store the same kinds of data as whatever you give them initially. In our case, stockPrice can only accept number data, and firstName can only accept text data.

Swift Constants

Now that you know how to declare new variables and assign pieces of data to them, let’s look at a similar construct used to store data called constants:

let lastName = "Smith"

Notice that the code above looks quite similar to a variable declaration. The only difference is we use the let keyword instead of var. The difference between a constant and a variable is that you cannot reassign a different piece of data to a constant after declaring it. If you tried doing so, Xcode will give you an error as shown below:

Here, Xcode tells me that I cannot assign something else to the constant lastName because it was already assigned the value of “Smith”.

At this point, it seems that variables are much more flexible than constants. Despite this, it’s more preferable in some cases to use constants over variables, like to keep track of data you don’t intend to change later. Over the course of your Swift journey, you’ll build a sense of when to use variables versus constants. Also, if your program uses variables that never change their data, Xcode will suggest changing the variables into constants instead. In those cases, it’s simply a matter of changing the var keyword to the let keyword in the variables’ declarations.

Best Practices for Naming Variables and Constants

I mentioned before that your variable names should be descriptive so you know what kind of data they hold. There’s a fine balance to upkeep when it comes to the length of your names. If they are too long, they can get hard to read. If they are too short like str, you won’t know what kind of data the variable holds. Ideally, a variable name should be one to four words long.

A variable name such as veryfirstname can also be hard to read, though. Thus, a good capitalization method to use is camel casing. Camel casing simply means starting the first word with a lowercase letter, and starting every subsequent word with an uppercase letter. Then, veryfirstname in camel casing would be veryFirstName, which is much easier to read.

Chapter Recap

  1. Variables and constants are used to keep track of data in your app.
  2. Use the var and let keywords to declare new variables and constants.
  3. Use the equal sign to assign data to a variable or constant.
  4. Use camel case as a best practice for naming your variables and constants.
  5. Constants are like variables, but you can’t reassign data to them after the initial assignment.

Data Types

Chapter 2: Data Types

Data in apps can be as simple as text or numbers or as complex as photo data or employee records in a company database. In this chapter, we’ll talk about different data types in Swift. To start, let’s look at a line of code from the previous chapter:

var str = "Hello, playground"

Above, we’ve declared a variable called str, and assigned a piece of string or text data to it. I previously mentioned you can’t change the kind of data a variable stores, as in the following line:

str = 20

Most Common Data Types in Swift

In Swift, there are several different types of data, but these are the most common ones:

  1. String – this is just text data. The name basically refers to having a string of characters:
    var aString = "This is a string"
  2. Int – this is short for “integer.” This type represents whole numbers: positive and negative.
    var myInt = 108
    var itemsInStock = -20
  3. Float and Double – these two represent decimal numbers. The difference is that Doubles have more precision than Floats, so they can store longer decimal numbers.
    let pi = 3.15159265359
  4. Boolean – Swift shortens this to “Bool.” Booleans can only store either “true” or “false”. They are perfect for when there are only one of two options.
    var isTVOn = true

By no means are these the only data types in Swift. However, they are the most common data types you will work with for a while.

How to Specify the Data Type for your Variable or Constant

We learned in the last lesson that we need the var keyword to declare new variables, or the let keyword for new constants:

var firstName = "Tom"
let lastName = "Smith"

But whether you’re declaring a variable or a constant, there is, in fact, an optional part of the declaration we left out. After your variable or constant name, you can add a colon and the type of data you expect this variable or constant to contain:

var firstName: String = "Tom"

In this case, we are explicitly telling Swift that firstName will only store String, or text, data. Why didn’t we have to specify these data types before? Well, that’s because we don’t have to. If a variable’s data type is unspecified, Swift will automatically figure it out from the first piece of data you assign to the variable. Here’s what I mean:

var str = 100

If I first give a variable an integer like the 100 above, Swift will automatically give my variable str an integer data type:

Then, if we try assigning a string to it later, Xcode tells us we can’t. But why doesn’t it work this way? Different types of data are stored differently in memory. When you declare a variable str to store integers, str will only be set up to store integers. If you declare that a variable should store string data, it gets set up differently in memory. But don’t fret – in a later chapter, I will introduce a data type that will give you more flexibility to store different kinds of data.

Some of you might be thinking that not being able to store what you want in a variable when you want seems too limiting. However, if you think about it, this has the advantage of setting your expectations as a coder. If your variable could store just any kind of data, sometimes you wouldn’t know what to expect from it. Too much freedom isn’t ideal because it makes way for more possible errors. When programming, our greatest enemy is actually us making mistakes, either in our logic or the way that we’ve coded something.

Examples of Data Types

Let’s see some examples of specifying data types when declaring variables:

  1. String : var str: String = "Tom"
  2. Int: var anInteger: Int = 100
  3. Float: var aFloat: Float = 0.2
  4. Double: var aDouble: Double = 0.3
  5. Boolean: var aBool: Bool = true

Because Swift can automatically determine the data types in these cases, we can leave them off:

var str = "Tom"
var anInteger = 100
var aFloat = 0.2
var aDouble = 0.3
var aBool = true

However, the float and double examples are slightly misleading since Double is the default decimal data type for Swift. Thus, in the example above, Swift would think aFloat is a Double. In this case, we would need to keep the data type around if we want to enforce aFloat as a Float.

Chapter Recap

  1. String, Int, Float/Double, and Boolean are the most common data types you will work with in Swift.
  2. You can specify a data type when declaring a variable or constant. Leaving it off will give the variable or constant the data type of the first piece of data you assign to it.

Swift Math Operators

Chapter 3: Swift Math Operators

In this chapter, we’ll examine some basic math operations that you can use with constants, variables, and equations in general. Swift covers your basic math operations: addition, subtraction, multiplication, and division (syntax for all shown below).

You can also chain these operators together, so something like the following would evaluate to 8:

print(a * b * c)

Besides the basic math operators, there are several operators in Swift you might not realize exist. For example, we can use the power function for exponentials, as demonstrated below. This function accepts two numbers, either doubles or ints, the base and the exponent to raise the base to. Thus, the example below evaluates to 8.

pow(2, 3)

Another common function at our disposal is sqrt(). For example, sqrt(16) would give us 4.

One operator that you might not be familiar with is ceiling, or ceil(), which rounds any number up to the next whole number. In the example below, it rounds 4.5 up to 5. Whole numbers get rounded up to themselves.

Similarly, there is floor(), which rounds any number down to the next whole number. Below, we have floor() rounding 4.5 down to 4. Again, whole numbers are rounded down to themselves.

Incrementing and Decrementing Numbers

Let’s say you want to increment a variable by one, in this case the variable a. As shown below, you would take a, add 1 to it, and reassign it back to a. If we run this code in a playground, we would see that a now contains 3.

The equivalent, shorthand way of writing the same thing is to use += instead:

a += 1

Similarly, you can use the shorthand way of decrementing numbers, as shown below:

a -= 1

This shorthand also works with multiplication (*=) and division (/=). This shorthand exists because you tend to modify variables through simple math operations quite often when expressing your logic or writing algorithms in your code.

If Statement

Chapter 4: If Statement

Now that you know all about variables, constants, and data types, we’re going to talk about a staple construct that exists in all programming languages in some form or another: the if statement. After this chapter, you will be able to write code that can make its own decisions!

Let’s start with this piece of code:

let a = 10
print("a is less than 10")

You can see on line 1 we create a constant, a, with a value of 10. Afterwards, we have a print statement saying “a is less than 10.” Running this code gives us the following output:

Our console shows that “a is less than 10.” Of course, this isn’t true because a is 10. We don’t want that print statement to show up in this case, but we all know it’s going to run eventually each time we execute the code. So, what do we do? This is where we can use an if statement, which allows us to say we only want to run that print statement under certain conditions.

Syntax

if condition {
    // some code
}

As you can see, an if statement starts with the if keyword followed by the condition you’re going to test. Then you add a pair of braces (or curly brackets). Any code inside those braces will only run if your condition equates to true. If the condition is false, the code in your braces will not run. Let’s see how we can apply this to our example with a:

let a = 10

if a < 10 {
    print("a is less than 10")
}

Notice that on line 3, we’ve specified a condition that we would read as “a less than 10.” To represent that in code, we use the “less than” (<) operator, which checks if what’s on the left side is less than what’s on the right side. Here, we’ve got a on the left and 10 on the right. After this condition, we add our opening brace and the closing brace on line 5. Always make sure that there’s a space in between your opening brace and your condition.

Let’s try running this code in a playground:

Now we see the console is empty. What happened here was that it checked the condition a < 10. Because a is 10, it’s clearly not less than 10. Thus, the condition equated to false, and it didn’t run our print statement inside the braces for that condition.

Now, let’s try making that condition true:

let a = 10

if a <= 10 {
    print("a is less than 10")
}

In our condition on line 3, we added an equal sign beside the less than operator so it would read “a less than or equal to 10.” Now with a being 10, our condition is true. If we run this code, we should see our message in the console. This is really cool because you can now write code that runs based on a condition, instead of every time your code executes.

Else If Clause

Although the standard if statement can be useful in certain situations, oftentimes, we have multiple conditions that we need to test. For situations like these, we’ll be using another feature of the if statement called an “else if” clause. Here’s its structure:

if condition1 {
    // some code
} else if condition2 {
    // some code
}

Right after the closing brace of our if statement, we added the keyword else and then if again. Afterwards, we added another condition, followed by another set of braces. Thus, we are effectively creating another if statement after the else keyword. Now, we have multiple branches of code to run that we can choose from, and Swift will only ever run one of these code branches.

However, how does it choose which branch to run? First it checks the condition in your initial “if” branch. If that condition is false, it skips right down to check the next condition in your “else if” branch. If that’s also false, it simply skips again without doing anything. However, if any one of those conditions was true, then Swift would run its respective branch of code.

To use an analogy, imagine yourself standing in the middle of crossroads with two paths, one going left and one going right, and you can only go down one path. An if statement works similarly, except that it always evaluates the conditions from top to bottom. The moment it finds a true condition, it runs the block of code associated with that branch and ignores everything underneath it.

To see this in action, let’s change a from our example above to 20.

let a = 20

if a <= 10 {
    print("a is less than or equal to 10")
}

We know the print statement won’t show because our condition is false, which we can verify by running the code. However, on line 5 in the example below, we’ve added an “else if” clause followed by the condition a > 15. Running this now prints out “a is greater than 15.”

Now, say on line 1 we change the value of a to 5 and make the condition on line 5 a < 15. Both conditions are now technically true, so what do you think will happen?

After running our code, we see it evaluated the condition on line 3 which happened to be true and then jumped to line 4, which is the corresponding fork in the road. It then executed that branch of code and ignored everything else. Notice it didn’t check the second condition at all after picking the first branch.

Stacking Else If Clauses

If statements are much more flexible than our example above, though. One cool feature is that you can stack else if clauses:

if condition1 {
    // some code
} else if condition2 {
    // some code
} else if condition3 {
    // some code
} else if condition4 {
    // some code
} else if condition5 {
    // some code
}

This helps if you have many conditions to check. The caveat to this is that you don’t want these conditions to grow too long. Later on, we’ll learn another kind of decision-making structure that you can use for those instances instead.

Else Clause

Now let’s take a look at this last feature of the if statement: the else clause:

if condition1 {
    // some code
} else if condition2 {
    // some code
} else if condition3 {
    // some code
} else {
    // some code
}

The else clause is kind of like a “catch-all” bucket. If none of the conditions above it evaluate to true, only then does Xcode run the code associated with that else cause. To include an else clause in your if statement, you simply use the else keyword. There’s no condition necessary because the point of the else branch is to run some code in case nothing above it got executed.

Take a look at the following example:

On the first line, we now set a to 25, and our conditions read “a is less than 10” on line 3, “a is less than 15” on line 5, and “a is greater than 30” on line 7. If we run the code, nothing is printed because none of the conditions are true.

Let’s add an else branch to handle this case with some more useful information:

We added our new else branch on line 9 with a set of curly braces beside it. In its code block, we added a print statement for “a is something else.” Running this clearly shows how Swift fell into the catch-all else branch of code.

Chaining Conditions

We’ve now seen all of the basics of the if statement. But our example conditions have been quite simplistic so far. How do we create more sophisticated conditions to check? For one, we can actually chain conditions together. Let’s see how.

First, let’s introduce another variable b on line 2, initially set to 10. If, for example, I want to check that a is less than 10 and that b is greater than five, i.e. both those facts are true simultaneously, then I can use this double ampersand symbol (&&) in order to express that. The “&&” symbol is called an AND operator and means I want both of those conditions to be true in order for that branch to be executed.

However, if you’re fine with any one of those conditions being true, like an either/or check, then you can use the OR operator, symbolized by two double pipes (||). You can type the “|” character by holding down Shift on your keyboard and pressing the key above your return key.

Here’s what it looks like if we run code with an OR operator:

Even though a isn’t less than 10, the first branch executes because b is greater than 5.

Now I’m going to blow your mind even more because we can further chain these things. By wrapping our conditions in parentheses, we can chain them with even more operators:

We know the (a < 10 || b > 5) part is true from the previous example, and we also want c to be 1 at the same time, which is clearly true, so Swift runs the first branch of the if statement.

Although you can wrap as many conditions as you want in a pair of parentheses, keep in mind that the more conditions you have, the harder it will be to read and understand.

Let’s turn our attention back to line 5 for a moment. You might notice that the c == 1 condition looks quite similar to the c = 1 statement above on line 3. But there’s a big difference between the two equal sign operators. A single equal sign (=) is used for variable/constant assignment, and a double equals sign (==) is used for testing equality between two values.

For testing inequality instead, you would use an exclamation mark and equals signs (!=). Here’s an example of testing for inequality:

The code ran the else clause here because c no longer equals 1, thus none of the conditions evaluate to true. Remember that because we use the && operator on line 5, we need both sides of the && to be true in order for that block of code to run.

However, this behavior changes if we use the OR (||) operator instead:

Now it doesn’t matter if c is equal to 1, because we know (a < 10 || b > 5) is true.

This all takes a bit of practice to wrap your head around. But here’s an important reminder: don’t memorize the keywords you’ve learned today. Why? Because I’m going to introduce more keywords and syntax to you in the next lessons, and it’s not going to be fun or practical for you to try to memorize all of them.

What I would recommend is to instead spend 30 minutes working on the worksheet for this lesson. Try out the statement by yourself in a playground, and after 30 minutes of learning, you’re going to learn a lot more. I also have a Swift cheat sheet and worksheets for you to practice what you’ve learned in today’s lesson. I highly recommend that you go through the worksheet so that you don’t make the same mistakes I did when I first started.

Chapter Recap

  1. Use if statements to conditionally execute code.
  2. Add alternative conditions with the “else if” clause.
  3. Use the “else” clause as a final alternative that will execute if no other condition is true.
  4. Conditions are checked from top to bottom.

Switch Statement

Chapter 5: Switch Statement

In the previous lesson, you learned how to use if statements to run code under certain conditions. But after a while, you might find yourself writing giant if statements with many branches. When you see yourself going down this path, I want you to stop and consider using a switch statement, which we’ll learn about here.

Have a look at the code below:

let chr = "a"

if chr == "a" {
    print("the character is a")
} else if chr == "b" {
    print("the character is b")
}

On line 1, we declared a new constant chr, assigned to the text “a.” To test if chr was equal to “a”, we can use an if statement with a double equal sign (==) as on line 3.

If we wanted to compare chr with every letter of the alphabet, I could use multiple else if clauses like the one on line 5. However, that would result in a giant if statement, which I don’t recommended. It’s at this point where using a switch statement becomes handy.

Syntax

switch value-to-consider {
    case value1:
        //some code
    case value2:
        //some code
    default:
        //some code
}

Let’s discuss each part of this syntax:

  1. Start with the switch keyword, followed by the expression or value that you want to check. In our first example, that’s the constant chr, followed by a set of braces.
  2. Inside the braces, precede each value you want to compare with your expression by the case keyword. After the value, type a colon, then the code that you want to execute if your expression matches that case, followed by the next case, and so on and so forth. 
  3. After all your specific cases, add a default case to run if your expression matched none of the other cases. Swift requires every switch statement to have a default case.

Let’s go back to our code and see this in action:

We’ve now replaced the if statement from earlier with a switch statement. You can type out a whole switch statement for practice, but Xcode has an autocomplete feature that will sometimes autofill all your cases for you. Just type “switch”, choose the “switch – Swift Switch Statement” option, and then you can navigate to the different fields to fill out with the tab key.

On line 4, we see that the pattern we’re checking for in the first case is a. If this case runs, we will see “this is an a” get printed. The default case would print “this is the fallback” as shown on line 7. As expected, when we run the code, it prints out “this is an a” since it matched the case on line 4.

What happens if we wanted to check for something else?

This time, we set chr to “b,” added another case to match with “b” in the switch statement. If we run the code again, we see that it will match the second case this time.

An additional cool feature of switch statements is being able to combine cases. If we want to take the same action when our expression matches with any one of multiple patterns, we simply separate the patterns by commas after the case keyword, as shown below:

And that is pretty much it for switch statements! In all honesty, they’re easy to use and much easier to read than giant if statements.

For-In Loop

Chapter 6: For-In Loop

In the last two lessons, you learned how to write code that can be executed under certain conditions. This lesson will cover loops which allow you to repeat a block of code multiple times. Once you know how they work, it turns out you’ll need to use them all over the place!

Swift has multiple kinds of loops. First, we’ll look at the for-in loop, which repeats a block of code a specific number of times.

Say I want to greet 5 people in a room. I could print “hello” to the console five times like so:

print("hello")
print("hello")
print("hello")
print("hello")
print("hello")

However, a for-in loop can condense this into just a few lines. Let’s dissect its syntax first:

Syntax

for counter in lower...upper {
    // some code
}

Going over each part of this syntax:

a). True to its name, the main keywords these loops use are for and in.

b). Start with the keyword for, followed by a variable name. You don’t have to use var here; just add a stand-alone name. This will act as a kind of placeholder, which I’ll explain how soon.

c). After the variable, type the keyword in, and then specify a range. The syntax for a range is number1...number2, which represents all numbers between number1 and number2. This range is inclusive. For example, if your range is 1...5, the loop would iterate (or repeat) exactly five times.

d). After the range, we add our set of braces. Inside the braces is where you put your code, which the loop will repeat for whatever range you’ve specified.

I know this is another set of keywords and syntactical structure that you have to remember, but let me emphasize this point again: don’t try to memorize anything. As long as you do the exercises and worksheets after each lesson and spend 15 minutes playing around with loops, the structure of the for-in loop will become second-nature. Go ahead and download the Swift cheat sheet from our Resources page. If you ever forget anything, you can just quickly refer to it!

Let’s refactor the code from above to use a for-in loop:

Following the syntax that I discussed above, we started the for-in loop with the keyword for, followed by a variable that I named counter. Then we have in after counter followed by the inclusive range of 1 (lower range), three dots, and 5 (upper range). Right after the range, I added in my braces, in which I’ve put the print("hello") command. After running this code, we see “hello” shown in the console five times, as expected.

Counter Variable

Look at line 1 again where the variable name counter is located. What is it for?

Well, for each iteration of the for-in loop, the value of counter refers to which iteration the loop is on. For example, counter contains 1 in the first iteration of the loop, then counter contains 2 in the second iteration, and so on and so forth. We can see this happening in action:

Notice that on line 2, we print out the value of counter, and after running this code, we see 1, 2, 3, 4, 5 outputted in the console.

Yellow warning

Xcode uses a yellow warning to tell us that if we don’t need to use a certain variable, then we don’t have to specify a name for it.

In the screenshot above, we are printing out “hello” instead of counter. The yellow warning appeared and stated that we should consider replacing counter with an underscore or removing it. What that means is if your loop never uses the counter variable, you can replace it with an underscore, and the loop still functions the same.

We can try this out with our own loop, as the code below shows:

for _ in 1...5 {
    print("hello")
}

Now I want to show you one more thing that might trip you up when working with loops. Let’s say I want to add up every number from 1 to 5. You might do something like this:

for counter in 1...5 {
    var sum = 0
    sum += counter
    print(sum)
}

On line 2, we declared the variable sum and set it equal to 0.

Remember that counter above will contain the numbers 1 to 5 over the loop iterations. The line sum += counter adds the value of counter to sum. The += operator is just shorthand notation for addition. For example, if sum is 10 and counter is 5, using the += operator would update sum to 15. An equivalent way to write sum += counter is sum = sum + counter.

On line 4 in the screenshot below, we print sum on each iteration of the loop. After running the code, we see the numbers 1 to 5 in the console.

However, this is not the result we expected. Let’s take a look at what happened.

In the first iteration, we declared a variable called sum, set it equal to 0, and added counter to it, which was 1. Then, we printed sum and saw 1 in the console. In the second iteration, counter was set to 2, and we declared sum again and set it 0. In other words, we added 2 to 0, printed sum, saw 2 in the console, and so on and so forth.

Thus, on every iteration in the loop, we re-declare sum and set it to 0. This means that we don’t actually add up the previous counter values like we want to.

You might ask: why am I able to declare sum so many times?

Well, that’s because the for loop starts with a clean slate on every iteration. There’s no recollection of what happened in previous iterations, which means that we essentially reset the sum on every single iteration.

Here’s what we should do to get the desired effect:

I moved the declaration of sum outside the for-in loop, and inside each iteration, I just add counter to sum.

After printing sum on line 7 and running the code, I finally see 15 in the console, which is the total sum of all numbers from 1 to 5.

Here’s a short recap of this example: on line 1, we’ve declared sum and set it to 0. Then, on the first iteration of the loop, counter is 1, which we added to 0 so sum becomes 1. On the second iteration, counter is 2. We then added 2 to 1 giving us 3, and so on and so forth. The loop ends after 5 iterations. We then printed sum, which explains why we got the single number 15 in the console.

Chapter Recap

  1. Use a for-in loop to execute a block of code for a set number of times.
  2. You can specify how many times your loop runs with a range.
  3. Each time your loop runs its code is called a loop iteration.
  4. Loops have no recollection of variables from previous iterations.

While and Repeat-While Loops

Chapter 7: While and Repeat-While Loops

Now that we learned about the for-in loop, which repeats code for a set number of times, we’ll examine two kinds of loops that allows you to repeat code indefinitely until a certain condition is met. These kinds of loops are great when you don’t know how many iterations you need to get the job done. Let’s dive in and see how they work!

While Loop

This type of loop resembles an if statement in many ways. It uses a condition to determine whether to run your code, and skips over its block of code entirely if the condition is initially false. Otherwise, it will instead run your code until the condition is false. A while loop’s syntax is similar to that of an if statement, except we use the while keyword instead of if:

while condition {
    // some code
}

Let’s try using a while loop in an example:

var counter = 5

while counter > 0 {
    print("hello")
}

We create a counter variable and then a while loop with the condition “counter greater than 0.” If the condition passes, Swift will run the print command on line 8. What do you think will happen if I run the code?

First, it checks that counter is indeed greater than 0, which is clearly true. Thus, it will print “hello,” return to the top of the loop, and check the condition again. It will be true again, since nothing changed. In fact, it will print “hello” infinitely until our program crashes. In short, the program will overflow. When this happens, we call it an infinite loop:

You can see the number of times our loop ran on the right-hand side. It just won’t stop! There’s a chance your computer will go haywire if you attempt to do this because that happened to me. If that happens, feel free to restart Xcode and go back in.

How can we prevent these infinite loops? In our example, I had to write code that eventually made the condition on line 3 false. Notice the code I added on line 5 below which uses shorthand notation to decrement counter by 1:

var counter = 5

while counter > 0 {
    print("hello")
    counter -= 1
}

We know counter starts off as 5. On the first iteration, it prints “hello” and updates counter to 4. The condition is still true in this new state, so the loop reruns, printing “hello” and updating counter to 3. This continues until counter becomes 0. Since 0 isn’t greater than 0, our loop will then quit and we should see “hello” five times in the console.

Repeat-While Loop

The next indefinite loop we will examine is the repeat-while loop, with its syntax below:

repeat {
    //some code
} while condition

The syntax is similar to a while loop’s, but our condition is now on the bottom. We start with the repeat keyword, then our braces that will surround the code to repeat. Then, we have the while keyword and loop condition, which concludes the repeat-while loop.

A repeat-while loop almost executes opposite to a normal while loop. It runs your code first, then checks if the condition holds true before deciding to continue running your code. As a result, your code is guaranteed to always run at least once.

The example below demonstrates how the repeat-while works identically to the while loop. On line 8, we declared another variable, counter2, to use in our repeat-while loop. This loop prints a different message but decrements its counter like the normal while loop above it:

Now what if we changed both our initial counter values to -5 instead?

Both loop conditions were false initially. As a result, the while loop did not run at all, but the repeat-while loop ran once and no more because of the false condition.

So far, you might wonder how to decide which loop to use among the ones we’ve seen. That depends on what you’re trying to accomplish. For code you know should execute once and only conditionally repeat afterwards, a repeat-while loop works. But for code that should run only while some condition is true, you would use a while loop.

Chapter Recap

  • The while loop and the repeat-while loop lets you execute a block of code and loop it indefinitely until a condition is met.
  • The while loop checks the condition first before executing the block of code and then looping
  • The repeat while loop executes your block of code first and then check your condition to decide whether or not it should repeat for another iteration.

Functions Part 1

Chapter 8: Functions Part 1

A function is a named block of code that you can execute on demand by calling its name. Functions help you organize code into smaller chunks for specific tasks, so you can simply run a specific function to execute a specific task. A function can also take in custom data to work with, making it much more versatile. For now, we’ll just look at some basic functions in this lesson.

Syntax

func name() {
    // some code
}

We start defining a function with the func keyword followed by the function’s name. Like with variables, the name can be anything as long as it sufficiently describes what the function does. This will help you remember what function to call for a certain task later on. Next, we add a pair of parentheses after the function name.

In the next lesson, we’ll learn about input parameters, which you can add here to make your function more flexible. However, for now, we will leave these parentheses empty for our basic functions. Finally, we add a pair of curly braces to contain the code the function will run when you call its name.

Defining a Basic Function

Here’s an example of a basic function, i.e. one without input parameters:

func addTwoNumbers() {
    let a = 1
    let b = 2
    let c = a + b
    
    print(c)
}

We’ve called this function addTwoNumbers and instructed it to run some addition code within its curly brackets. But running this code alone gives us nothing in the console. Why is that? Well, the code inside the function won’t run by itself until you call the function, as shown below:

func addTwoNumbers() {
    let a = 1
    let b = 2
    let c = a + b
    
    print(c)
}

addTwoNumbers()

I call our basic function on the last line. Every basic function call uses the function’s name followed by a pair of parentheses. This should now output 3 when we run the code.

Here’s another function that we invoke on the last line, which should output 4 in the console:

func subtractTwoNumbers() {
    let d = 5
    let e = 1
    let f = d - e
    
    print(f)
}

subtractTwoNumbers()

Functions Save Time

This may not be immediately obvious, but functions are actually huge timesavers. Often, you might want to repeat the same sort of code in different places. But generally, you want to avoid simply duplicating the code wherever you need it. In these cases, try moving that code into a function instead so you can call it from multiple places.

Variable Scope

Variable scope refers to the idea that any variable and constant that you define inside of your function only exists and can be used within the curly brackets of that function. Now…what does that mean, and why is it important?

Let’s explore this in the context of our addTwoNumbers function:

On line 9, we try to print a outside of its function, addTwoNumbers, where we initialized it. But instead, we get the error “Use of unresolved identifier ‘a’.” This error basically tells us that, from line 9’s perspective, the variable a does not exist. This would also happen if I tried printing b or c on line 9 or from another function like subtractTwoNumbers.

If you need to access variables outside a function, you would have to declare the variables outside the function. Many beginners run into issues where they’re frustrated from not being able to access variables they’ve declared inside their functions, and now you know why.

Chapter Recap

  1. Functions help us organize our code into executable blocks.
  2. The func keyword defines a function. We use the function name to call it.
  3. The scope of a function is in between its curly brackets.
  4. Variables and constants created in a function’s scope are only accessible in that function.

Functions Part 2

Chapter 9: Functions Part 2

Previously, you learned what functions are and how to call them. However, we’ve only seen basic functions that print out things when you call them. We call these kinds of functions “void” functions because they don’t give us any data back. But how do we write functions that returned data to us instead?

Return Values

It turns out a common use case for functions is to take some input, perform some task, then return its result to us. Our first step is to specify that our function actually returns something when we call it.

To do this, we must add an extra part to our function syntax:

func name() -> DataType {
    // some code
    
    return someValue
}

Notice that between the parentheses and the starting curly brace, we have this arrow sign ->, written with a hyphen and a greater than sign. Then, we add the data type of the value our function will return. We also call this the return type. It’s very important to know this in advance, which is why we must specify this.

When you specify your function’s return type, Swift will expect your function to have a line of code called a “return statement.” The syntax for return statements is simply return <value>, as the example below shows. The return keyword makes Swift exit the function and return the specified value back to the caller.

Let’s modify our addTwoNumbers function to return its sum instead of printing it out:

func addTwoNumbers() -> Int {
    let a = 1
    let b = 2
    let c = a + b
    
    return c
}

addTwoNumbers()

We first define our addTwoNumbers function as usual but now with an extra -> Int part. This specifies the return type as Int, since the sum is an integer. Also, our print statement on line 6 has now changed into a return statement. But if we call this function, the console will still remain empty. Where did the value of c go?

Well, when you call a function that returns data, like addTwoNumbers, a call to that function is equivalent to the value it returns. What does that mean? In our case, the example above is equivalent to:

func addTwoNumbers() -> Int {
    let a = 1
    let b = 2
    let c = a + b
    
    return c
}

3

Clearly, the 3 on its own won’t get itself printed to the console. Therefore, we need to capture and use the value returned somehow. Check this out:

func addTwoNumbers() -> Int {
    let a = 1
    let b = 2
    let c = a + b
    
    return c
}

let sum = addTwoNumbers()
print(sum)

Now I defined a constant sum assigned to the result, or returned data, from addTwoNumbers. Printing out sum on the next line would now output 3 in the console.

As previously mentioned, because a function call is equivalent to its return value, the last two lines above are, in fact, equivalent to:

let sum = 3
print(sum)

That’s why we see 3 printed to the console.

Input Parameters

So far, addTwoNumbers is a lame function since it only ever returns 3. What if we could actually tell it which two numbers to add? We can do exactly that by using input parameters.

They make up one more piece in our syntax for functions:

func name(argumentLabel parameterName: DataType) {
    // some code
}

All input parameters go in between the rounded parentheses. Let’s go over the syntax above from right to left.

For each input parameter, we need to specify what its data type and parameter name will be. Similar to variable names, we use the parameter name (which can be anything) inside a function to access the data the parameter holds. Notice the colon between the parameter name and data type.

You could also specify an optional argument label. The argument label and parameter name only have a space in between. But what’s the argument label for? It can help function calls read more like natural English by changing what you label a parameter as.

Let’s try adding a parameter to addTwoNumbers:

func addTwoNumbers(arg para: Int) -> Int {
    let a = 1
    let b = 2
    let c = a + b
    
    return c
}

We now have a single parameter with “arg” as its argument label and “para” as its parameter name. Then we add a colon and the input’s data type, Int. Here’s how this new parameter changes our resulting function call:

On line 9, you can see Xcode automatically detected that our function call is no longer correct (see highlighted text in red). To call addTwoNumbers now, I have to specify the value of that parameter. An easy way to do this is to start rewriting the function call, so we get a chance to use Xcode’s autocomplete feature, as shown below:

In the popup list, the “Int” type we see to the left of the function name tells us the return type of addTwoNumbers. If the function doesn’t return anything, it would say “Void” there instead. You can also see the parameter we added along with its data type. Choosing the first option gives us a placeholder for the parameter value, which we can replace with something like 5:

func addTwoNumbers(arg para: Int) -> Int {
    let a = para
    let b = 2
    let c = a + b
    
    return c
}

let sum = addTwoNumbers(arg: 5)
print(sum)

This is how you’d call addTwoNumbers with one parameter. Essentially, we’re passing 5 into the function from outside the function. On line 2, I wanted to assign 5 into a, so I use the parameter name, not argument label, inside the function. Argument labels cannot be used in your functions. As a result of using the parameter, running the code above would print 7 to the console.

Multiple Parameters

So far, we have this variable b that just contains the value 2. However, we can specify another parameter to assign to b on line 3 as well. Multiple parameters in a function simply need to be separated by commas. Note the similarities in their syntax below:

func name(arg1 param1: DataType, arg2 param2: DataType) {
    // some code
}

You can repeat this syntax pattern for 3, 4, 5 parameters, or however many you need. Let’s add another parameter to addTwoNumbers for b:

func addTwoNumbers(arg para: Int, arg2 para2: Int) -> Int {
    let a = para
    let b = para2
    let c = a + b
    
    return c
}

let sum = addTwoNumbers(arg: 5, arg2: 5)
print(sum)

In the parentheses, we first add a comma after the Int to separate our parameters, which we specify by arg2 para2: Int. Then we assign this parameter to b inside the function. We also need to rewrite our function call to specify the values of the two arguments, both 5s in this case. Running this code gives us 10 in the console from adding 5 and 5. Tip: you can use Tab to jump to the next placeholders in the code that autocomplete gives you.

Recall that argument labels are optional. If a parameter has no argument label, Swift will expect you to use its parameter name in the function call instead, like in the example below:

func addTwoNumbers(para: Int, para2: Int) -> Int {
    let a = para
    let b = para2
    let c = a + b
    
    return c
}

let sum = addTwoNumbers(para: 5, para2: 5)
print(sum)

Before we move on, let’s use different parameter names that are easier to understand. Remember to also update every spot that used the names (lines 2, 3, and 9). Changing the names shouldn’t affect our code’s behavior, so we should see the same output:

func addTwoNumbers(number1: Int, number2: Int) -> Int {
    let a = number1
    let b = number2
    let c = a + b
    
    return c
}

let sum = addTwoNumbers(number1: 5, number2: 5)
print(sum)

However, sometimes parameter names don’t make function calls read very naturally. For example, line 9 looks like it says “add two numbers, number one 5, number two 5.”

But by adding the right argument labels, we can have a much neater function call:

func addTwoNumbers(using number1: Int, and number2: Int) -> Int {
    let a = number1
    let b = number2
    let c = a + b
    
    return c
}

let sum = addTwoNumbers(using: 5, and: 5)
print(sum)

Now on line 9, the function call reads much more clearly: “add two numbers using 5 and 5.” Don’t forget that, inside your function, you still need to reference those parameters by their parameter names, and not their argument labels.

Surprisingly, you can also completely omit parameter names and labels from the function call by using underscores as your argument labels. In the example below, you can see the underscores before the parameter names. Because of this, the function call on the last line doesn’t need parameter names:

func addTwoNumbers(_ number1: Int, _ number2: Int) -> Int {
    let a = number1
    let b = number2
    let c = a + b
    
    return c
}

let sum = addTwoNumbers(5, 5)
print(sum)

Chapter Recap

  1. Using the return keyword allows your function to return a value back to its caller.
  2. Parameters let you supply data into your function when you call it.

Classes

Chapter 10: Classes

In this lesson, we will introduce you to classes, which are a highly critical part of the Swift programming language. Back in chapter 2, I mentioned that you can actually construct your own data types. Now, we’ll get to see how. Classes are like custom data types that help organize your information. Similar to how functions group pieces of code together, classes group related pieces of information together.

Let’s start off by looking at the following code snippet:

var name = "John"
var salary = 100
var role = "Director"

This has several pieces of information: two String variables and an Int variable. These variables seem to describe data for an employee. So far, we’re representing a single employee, John, but what if we wanted to represent other employees as well? Wouldn’t we need multiple sets of variables for them? In a way, yes, but what we can do is group these pieces of information together in a new data type. And in order to do that, we have to define a new class.

Syntax

class name {
    // class properties
}

We start with the class keyword followed by your class name. A good class name describes the kind of item we are trying to define with all our pieces of information. In this case, our class name can simply be Employee since we are defining an employee with certain data. Then we add a pair of curly braces, inside which you’ll define your class.

Here’s what a definition for our Employee class would look like:

class Employee {
    var name = ""
    var salary = 0
    var role = ""
}

Inside the curly braces, we have pieces of information that an employee would have. However, our variables, so far, do not describe a specific employee yet because we are creating a new data type, so we don’t know which employee they should describe. Just like how we assign values to String or Int variables when we declare them, the variables in our class will get specific pieces of information when we declare Employee variables. Imagine our Employee class as a general definition that we can use over and over again.

Before I show you how to use our Employee data type, let’s recall how we set up variables of other types we’ve seen before:

let a = 10
let b = "Ted"

On the first line, I created a piece of data (i.e. 10) to be stored in memory. Then I created a constant, a, to keep track of that data in memory. Similarly, I created a constant, b to store the piece of string data, “Ted,” in memory. This same idea applies to our new data types as well.

var c = Employee()

Here, we give c a new, empty Employee to store in memory which we create by using Employee(). This specific Employee that I’ve created in memory is called an object, or an instance of the class, in this case, an instance of the Employee class. This is because it functions as a specific copy of our original Employee template with its own values for name, salary, and role.

For now, to create instances of a class, you’d type its name followed by a pair of parentheses. However, this can get slightly more complex for other classes which we will discuss later on.

Dot Notation

Variables a and b from above have simple data types, specifically Int and String. However, c has a custom type: Employee. Right now, new Employee objects have those boring "" and 0 values we gave them. However, we can change and access the data for a specific Employee by using what we call dot notation. Let’s see how that works:

c.salary = 1000
print(c.salary)

c.name = "Tom"
c.role = "Art Director"

First we type c, which points to our new Employee object, and then a dot, telling Swift we are trying to access something inside c. As an example, I set the salary of c to 1000, which we can then print out to see that it indeed holds 1000. Similarly, we can change the values of the other variables in c as demonstrated in the following lines.

In addition to specifying what pieces of data a class should store, you can also define functions in your class. Below, we add a function, doWork, to our Employee class:

class Employee {
    var name = ""
    var salary = 0
    var role = ""
    
    func doWork() {
        print("Hi my name is (name) and I'm doing work")
        salary += 1
    }
}

var c = Employee()

c.salary = 1000
print(c.salary)

c.name = "Tom"
c.role = "Art Director"

c.doWork()

The doWork function prints a message from the employee and increases their salary. These functions need to be called on specific objects of the class, so we need dot notation with the variable c to call doWork in this case. After running the code, we would see “Hi my name is Tom and I’m doing work” in the console.

Let’s now create another Employee object:

var d = Employee()
d.name = "Sarah"
d.role = "Manager"
d.salary = 1000
d.doWork()

On the first line, we assign a new Employee object in memory to the variable d, and then set the name to “Sarah”, the role to “Manager”, and the salary to 1000. Finally, we call doWork() on d, which should print the same message as before but with Sarah’s name instead.

Keep in mind c and d are two separate Employee objects. Modifying any data for d won’t affect c and vice-versa. Similarly, calling doWork() on d has nothing to do with calling this function on c.

Some commonly-used terminology with classes: a class’ functions are called its methods, and a class’ variables are called its properties. A class’ methods always have access to its properties because they’re defined in the same class.

We’ve covered a lot here so far, but there’s, in fact, much more to classes. I hope this introduction made it a bit clear how they are fundamental to organizing your information.

Subclassing

Chapter 11: Subclassing

Up until this point, we’ve seen many different ways of organizing your code, like functions that organize repeatable chunks of code and classes that can organize variables and functions that logically go together. In fact, we can even organize our classes by using what’s called inheritance.

Also known as subclassing, inheritance allows you to create classes that build upon or extend other classes that have already been defined. This saves us a lot of time and effort from defining classes that do similar things.

Remember our point about duplicated code when we discussed functions? Having duplicated code can make your project hard to manage. With functions, however, you can call them from multiple places when you needed to execute the same block of code instead.

This idea also relates to classes because sometimes you might have two classes that have similar methods or properties. Instead of defining them separately, you can use subclassing and have one class extend from the other.

Let’s look at an example using the Employee class from the previous chapter:

class Employee {
    var name = ""
    var salary = 0
    var role = ""
    
    func doWork() {
        print("Hi my name is (name) and I'm doing work")
        salary += 1
    }
}

The variables on lines 2-4 are the properties of the Employee class, whereas the doWork() function is a method of the Employee class. Let’s say I want to create another custom data type called Manager. Managers will have names, salaries, roles, and of course, they also do work. Thus, if I were to separately define a Manager class, I would essentially be recreating the Employee class. Instead, we can have the Manager class become a subclass of Employee:

class Manager: Employee {
    
}

After the class name, we add a colon and then specify Employee. This syntax tells Swift that the Manager class subclasses Employee, making Employee the parent class of Manager. Now, Manager will automatically inherit all properties and methods of the Employee class.

Let me show you what I mean:

var m = Manager()
m.name = "Maggie"
m.salary = 2000
m.role = "Manager of IT"
m.doWork()

We first created a new Manager object and assigned it to m. We then set up its name, salary, and role, just like with Employee objects, and call doWork() on m. This would print “Hi my name is Maggie and I’m doing work” to the console.

Let’s say a Manager also needs to track the size of the team they are managing. We can add this functionality by adding a new property, teamSize, in the Manager class:

class Manager: Employee {
    var teamSize = 0
}

var m = Manager()
m.name = "Maggie"
m.salary = 2000
m.role = "Manager of IT"
m.teamSize = 10
m.doWork()

The code above shows how a Manager object has all the properties and methods that it inherited from the Employee class and its own property called teamSize, which we assigned to 10 for our variable m. Essentially, we were able to create a new class based off an existing class and add additional functionality to it.

A subclass can also modify existing functionality from its parent class:

class Manager: Employee {
    var teamSize = 0
    
    override func doWork() {
        print("I'm managing people")
        salary += 2
    }
}

m.doWork()

Let’s say when a Manager does work, they are also managing people. Thus, I changed this definition of doing work by overriding doWork() in the Manager class. After running the code above, we should get “I’m managing people” in the console because Swift is using the overridden method on line 4.

What if we wanted to extend the functionality of the inherited doWork() method instead of just replacing it? We can do this by calling the doWork method of Employee:

override func doWork() {
    super.doWork()
    print("I'm managing people")
    salary += 2
}

Notice the new function call, super.doWork(). This calls the doWork function of the parent class, in this case, Employee. Therefore, m will execute the Employee doWork method followed by the additional code on lines 3-4. Running the code should give us “Hi my name is Maggie and I’m doing work” followed by “I’m managing people” in the console. The reason why this super keyword is called as such is that a parent class is also called a superclass. In this case, Employee is the superclass of Manager.

Besides redefining superclass methods, Manager can also have its own new methods:

class Manager: Employee {
    var teamSize = 0
    
    override func doWork() {
        super.doWork()
        
        print("I'm managing people")
        salary += 2
    }
    
    func firePeople() {
        print("I'm firing people")
    }
}

var m = Manager()
m.name = "Maggie"
m.salary = 2000
m.role = "Manager of IT"
m.teamSize = 10
m.doWork()
m.firePeople()

Notice the new function, firePeople, on line 11, which I call on line 22. An Employee object couldn’t call this method because inheritance works from bottom to top, not from top to bottom.

You’ve finally got a taste for how powerful subclassing can be. It can save you a lot of work from not having to redefine two different, but somewhat similar, things. Our demonstration above uses one level of subclassing or inheritance, but we can set up as many levels as we like:

class Person {
    var name = ""
}

class Employee: Person {
    var salary = 0
    var role = ""
    
    func doWork() {
        print("Hi my name is (name) and I'm doing work")
        salary += 1
    }
}

In the example above, we can declare another class called Person that contains the name property and have Employee inherit from Person. Now, I don’t have to define name inside Employee since it subclasses Person as we specified on line 5.

Now it’s evident that you can end up building these hierarchies and trees as a way of organizing your properties and functions. This might start to reveal how variables, functions, and classes are all starting to come together.

Chapter Recap

You can save time by using subclassing to define your new classes based on already existing classes and improving or customizing them without having to redefine everything from scratch.

UIKit

Chapter 12: UIKit

UIKit is an Apple framework that contains many classes for common elements that most iOS apps share, i.e. for labels, buttons, text fields, and images. If we tried building these components from scratch, creating an app would be time-consuming. Thus, UIKit, comes with all of these pre-built elements that we can use in our apps instead.

Although this somewhat strays from Swift programming, we will see how UIKit frequently makes use of inheritance and subclassing to create the elements that make up the framework.

Exploring UIKit

You can access the documentation for UIKit here. To find a page on any UIKit element, search for “UIKit” + the element name on Google. Notice the UIKit docs don’t just detail user interface elements, but also things like classes for animation, touches, gestures, drag and drop.

Another really handy way to explore the UIKit, which I recommend even more, is through Xcode. You can access it from “Window” > “Developer Documentation.” Under “Swift” in the menu to the left, you will find UIKit near the top.

One thing that’s since been removed is a kind of hierarchy showing how each class extends other classes above it. However, I found this diagram on Google showing the inheritance relationships:

In the rightmost column, you can see UIButton, the classic or standard iOS button you would put in your view. By following the lines along the UIKit, you’ll see that UIButton is a subclass of UIControl, a subclass of UIView, a subclass of UIResponder, which is ultimately a subclass of NSObject:

If you are curious about what any of these classes do, you can find them in the documentation I showed earlier. The “Declaration” part in particular shows what a class derives from. You can even click on the parent classes and travel back through the entire class hierarchy:

On the page for the UIControl class, you can see that it’s the “base class for controls, which are visual elements that convey a specific action in response to user interactions.” Pretty cool!

UIControl extends from UIView, which is something you can put on screen for the user to see. UIView extends UIResponder, “an abstract interface for responding to and handling events.” This explains why so many user interface elements are ultimately a subclass of UIResponder. But UIResponder in itself is a subclass of the NSObject which is described as “the root class of most Objective-C class from which subclasses inherit a basic interface to the runtime system and the ability to behave as Objective-C objects.” It’s basically what makes an object tick, if you will.

The reason it says Objective-C is that was the language originally used to build apps before Swift. That explains why much of UIKit is still written in Objective-C, with Swift just a layer on top of it all. One dead giveaway is the NS prefix in front of the class name, such as NSObject. Usually when you see this prefix in a class name, you can expect that it is an Objective-C class.

I hope this chapter has shown you how Apple has created the UIKit library through the use of inheritance and subclassing. You have this UIKit library at your disposal with so many pre-built components that you can use to build your app. All of these classes will come in handy once you finish this series on Swift and then learn about Xcode and building user interfaces.

Initializers

Chapter 13: Initializers

In chapter 9, we learned how to use dot notation to set up the properties of a new object. However, setting these up directly as we’ve done is quite tedious and error-prone. To help with this, classes have functions called initializer methods to help set up new objects just how you want and make sure they are ready for use.

class Person {
    var name = ""
}

class Employee: Person {
    var salary = 0
    var role = ""
    
    func doWork() {
        print("Hi my name is (name) and I'm doing work")
        salary += 1
    }
}

class Manager: Employee {
    var teamSize = 0
    
    override func doWork() {
        super.doWork()
        
        print("I'm managing people")
        salary += 2
    }
    
    func firePeople() {
        print("I'm firing people")
    }
}

What we’ve got here are the classes we created during our discussion on subclassing. As a reminder, we have class definitions for Person, Employee which extends Person, and Manager which extends Employee. Let’s first create a new Person object. Recall that we would do this using Person():

let myPerson = Person()

You might notice the syntax Person() looks oddly similar to a function call, right? Function calls use a function name followed by two parentheses, plus any input parameters in between the parentheses. In fact, when we call Person(), we are calling a function of that class. But we didn’t declare any functions in Person yet. Even so, every class gets a default initializer function that we don’t see. We can explicitly declare this function as well, so let’s see what it looks like:

class Person {
    var name = ""
    
    init() {
        name = "Joe"
    }
}

To explicitly declare the Person initializer function, we’d simply use the init keyword and two parentheses like on line 4, with no func keyword required (Xcode would throw an error if you tried using func here anyway). Then we add any sort of initialization code we want. In this case, we set the name property to “Joe.” Our initializer method will get called when we request to create a new Person object with Person(). Thus, it also runs our custom code on line 4. To prove this, we can print out the name and should see “Joe” in the console:

let myPerson = Person()
print(myPerson.name)

An interesting thing about initializer functions is we can have as many as we’d like, including ones with their own input parameters. On line 8 below, we have an initializer with a String parameter, name, but with the basic initializer function above it as well:

class Person {
    var name = ""
    
    init() {
        
    }
    
    init(_ name: String) {
        self.name = name
    }
}

In this new initializer, I want to set our name property to the name input parameter that gets passed in. So we don’t confuse the two names, we use the self keyword with our property: self.name. We use self as like a special variable that refers to the object that’s running this code. In this context, it distinguishes between the name belonging to the object itself, and the name parameter passed into our method. Our code should output “Tom” in the console:

let myPerson = Person("Tom")
print(myPerson.name)

We know that from inheritance, Employee will inherit all functions from the Person class. Do you think that also includes the Person initializers? Let’s try calling one:

let anotherEmployee = Employee("Joe")

We know the Employee class itself doesn’t have an initializer with a name parameter. Thus, the code above shows how it does have the init functions it inherited from Person, which explains why we could pass in the name “Joe” to its initializer. That’s pretty cool!

Overriding Init Functions

class Manager: Employee {
    var teamSize = 0
    
    override func doWork() {
        super.doWork()
        
        print("I'm managing people")
        salary += 2
    }
    
    func firePeople() {
        print("I'm firing people")
    }
}

Remember how the Manager class could override the Employee doWork function? It called super.doWork() which executes the Employee doWork function, then ran the extra code on lines 7-8. This same idea applies for init functions.

class Person {
    var name = ""
    
    init() {
        
    }
    
    init(_ name: String) {
        self.name = name
    }
}

class Employee: Person {
    var salary = 0
    var role = ""
    
    override init(_ name: String) {
        
        // This is calling the init(_ name: String) function of the Person class
        super.init(name)
        
        // Additional init code
        self.role = "Analyst"
    }
    
    func doWork() {
        print("Hi my name is (name) and I'm doing work")
        salary += 1
    }
}

Take a look at the Employee initializer. Note that since Person is its superclass, calling super.init(name) actually invokes the Person initializer with the name parameter. This takes care of setting up the Person properties, like name, so we don’t have to. Afterwards, we can also set up our new Employee property, role, with the code on line 23.

Also notice that our call to Person‘s initializer occurs before the incoming role is assigned to Employee‘s role property. However, Swift actually requires that you call superclass initializers after initializing all necessary subclass properties unless the subclass properties have default values (which they do in the example above) or are optional (which you will learn about later).

let myEmployee = Employee("Joe")
print(myEmployee.name)
print(myEmployee.role)

Now, after creating an Employee object called myEmployee using the initializer we discussed above, I am able to access myEmployee‘s role and name. The code above should output “Joe” for the name and “Analyst” for the role.

Chapter Recap

In this chapter, you learned that that even with init functions, you can use the override keyword and provide a custom implementation of that initializer function and call the superclasses’ implementation as well. Later on, we’ll dive a little deeper and explore the two different types of initializers: designated and convenience initializers.

Optionals

Chapter 14: Optionals

In this chapter, we’re going to talk about optionals, probably one of the most confusing topics for beginners. If you’ve seen Swift code with exclamation marks or question marks, then you’ve come across optionals.

How exactly do optionals work? Up until this point, we’ve always assigned something into our variables or constants right away when declaring them. However, in some cases, you may want to declare your variable but not assign data to it right away. Xcode won’t like that since it wants to make sure all your variables and constants are actually keeping track of some sort of data. That’s what they’re for, right?

Nil

This is where optionals come into play, to help declare a variable as “empty”. But how do you define emptiness? We use the nil keyword which stands for empty or nothing. If you don’t want to assign actual data to a variable, you can assign it nil:

var a = nil

Even so, this will give us the error message “nil requires a contextual type.” What does this mean? Variables in Swift can only store data of their specific data type. But if Swift sees a variable assigned to nil, it can’t automatically conclude what the variable’s data type should be.

This is because nil doesn’t have a data type. Thus, we have to explicitly label our variable’s data type. For example, let’s tell Swift we want an empty Int variable:

var a: Int = nil

If we run this code, we still see an error because integer variables can’t actually store nil. In fact, none of our usual data types (Int, String, etc.) allow nil as a valid value. This is where we use optionals instead by adding a question mark to the data type as follows:

var a: Int? = nil

Notice we now have a data type of Int? instead of just Int. Our variable is now an “optional integer,” meaning it could either store an integer as usual or nil. The same thing goes for variables of other data types, like String.

Let’s set up another example for other optional data types:

class XmasPresent {
    func surprise() -> Int {
        return Int.random(in: 1...10)
    }
}

let present = XmasPresent()
print(present.surprise())

I’ve declared a class called XmasPresent with a function called surprise that returns a random integer from 1 to 10.

Below the class, I created a new XmasPresent object and printed the result of invoking the surprise function on it. You should see a number from 1 to 10. If I explicitly declared the data type of the constant on line 7, then it would be XmasPresent

Checking the Optional Type

What if we label the constant as an optional by adding a question mark and assign it nil?

Xcode throws an error saying the value of our “optional type, XmasPresent, must be unwrapped to refer to the function surprise.” This is because optional variables could either contain an actual object, or nil. One issue with that is you can’t access any members (functions or properties) of nil, so we need a way to check if an optional variable is not empty before we try accessing its members. In this case, even if we know present is nil because we set it up that way, we must check that it actually contains an object, specifically an XmasPresent object, before we can call surprise on it.

Ways to Check an Optional Type

There are several ways to check if an optional variable/constant is nil before using it. Let’s see some examples of these:

Optional Unwrapping

let present: XmasPresent? = nil

// Check the optional to see if it contains an object
if present != nil {
    // it contains an object
}

One way is to simply compare the optional variable/constant to nil using an if statement. We can either choose to take action if the variable is equal (==) or not equal (!=) to nil. In this case, because we want to use present only if it isn’t empty, we check that it’s not equal to nil.

Now we can add in our call to surprise in the if statement so our code reads that we want to call surprise on present only when it’s not nil:

// Check the optional to see if it contains an object
if present != nil {
    present.surprise()
}

After attempting to run the code above, we get yet another error. Before you can access the value inside an optional, you have to unwrap the optional, regardless of whether the variable is empty or not. To understand why, it might help to imagine the optional variable as a Christmas present or a box. You won’t know about its contents (or lack thereof) unless you unwrap the box first.

You might be wondering why we have to unwrap the optional if we’ve already checked that it isn’t nil. This is because our optional variable is, in a way, covering up the object that it’s carrying. Since we want to call a function on the object inside, and not the wrapper, we need to further open up the optional first.

Another way to think about this is running the if statement is similar to shaking a box and listening to it like when you were a kid trying to find out what was inside your present. Shaking the box merely tells you whether there’s something inside. If you are shaking a box that is empty, it won’t sound like anything, but if there’s something inside, you’ll hear it rattle around. Thus, our if statement tells us whether present contains an object or nil, but you cannot access or use the object until you unwrap present.

let present: XmasPresent? = nil

// Check the optional to see if it contains an object
if present != nil {
    // it contains an object
    // call the surprise function
    print(present!.surprise())
}

To unwrap our present, we use an exclamation mark after the variable name, which is analogous to tearing the optional box open, giving us access to whatever’s inside. This would throw an error if the box was empty, because we’re trusting that it in fact carries something. But our if statement verifies that the optional is not empty, so we can safely call surprise on its contents. Because present is currently nil, this code should not print anything, meaning our if statement worked as intended.

Let’s put a present inside the box instead:

let present: XmasPresent? = XmasPresent()

// Check the optional to see if it contains an object
if present != nil {
    print(present!.surprise())
}

Now that present contains a new XmasPresent object, this code should output a random number surprise to the console.

Optional Binding

Now let’s learn a more advanced technique called optional binding to check if an optional variable or constant actually contains an object:

// Optional Binding
if let actualPresent = present {
    print(actualPresent.surprise())
}

In the code above, I’m using what’s called an if let statement to check if present is nil or not. If it is nil, the code inside the if statement will not be executed. Otherwise, Swift will automatically unwrap present and assign its contents to a constant called actualPresent. Thus, inside the if statement, you can use actualPresent just like any other object and do not need to unwrap anything. Running the code should print out a random number as expected.

Optional Chaining

Another technique we can use to work with optionals is called optional chaining.

let present: XmasPresent? = XmasPresent()

//Optional Chaining
present?.surprise()

In the example above, I added a question mark after present followed by calling surprise. The question mark operator works almost identical to the exclamation mark from earlier. The only difference is if the optional turns out to be nil, the code will not crash and the entire line is ignored. That’s also why we don’t need any if statements. If it actually contains an object, then Swift will unwrap the optional as usual. In this case, it will call surprise on present since it contains an object.

Optionals in Swift

Why do Swift even have optionals? Well, Swift is a type-safe language so it forces you to specify the exact data types you’re working with at every point in your program. In the long run, this helps tremendously by forcing you to think of things such as whether a variable will ever need to be empty or always contain a value. Swift’s type-safety also prevents you from accidentally storing different kinds of data from what you intended in a variable.

One thing I haven’t demonstrated with optionals is that Swift will, in fact, give them a default value of nil if you don’t assign them any data at first:

var c: String?

In this case, c is automatically given a value of nil, so you don’t have to explicitly assign nil to it. I recommend that you declare optionals this way if you intend them to be nil at first.

var d: String!

There’s also another optional data type that uses an exclamation mark as shown above. I know that, so far, I’ve shown how using an exclamation mark unwraps a variable or constant to access what’s inside them. But in the context of data types, this is called an implicitly unwrapped optional. This is a normal optional, but when you use it in code, Swift automatically unwraps it for you, so you don’t need to use extra “?” or “!”s to get at its contents.

When would you want to use implicitly unwrapped optionals versus the kind with the question mark? In my opinion, you’d want to use the optional with the question mark in general because it forces you to consider the appropriate action to take if the optional turns out to be nil every time you use it.

Only in some cases where you know for sure that the optional variable is nil only initially, but then always contains a value after a certain point, it might make sense to use an implicitly unwrapped optional. Then you can ignore all of the optional binding, optional chaining, and any other checks for nil.

Let’s recap what we’ve learned about the two optional data types with another quick example:

var present1: XmasPresent? = nil
var present2: XmasPresent! = nil

present1.surprise()
present2.surprise()

We’ve declared two variables above, the first a regular optional and the second an implicitly unwrapped optional, and then we try calling surprise on both of them directly.

Because we aren’t using optional chaining on present1, Xcode prevents us from running our code entirely until we unwrap it. It also provides some information on which unwrapping method to use.

On the other hand, Xcode doesn’t warn us about calling surprise on present2, but instead crashes when we try running that line, since it will always automatically unwrap present2. Sometimes, this can be helpful when debugging a program. If your optional was never meant to be nil at some point, but does end up being nil there, Xcode will highlight this point when the app crashes and you’ll know exactly where to look to fix the issue.

And that was your introduction to optionals! Practically speaking, you’ll use them most of the time, like how we covered in this lesson. In the future, we’ll cover more of the complex aspects of optionals. If the purpose of optionals is still not entirely clear, let me say when we get to building apps together, you’ll know what they are and how they’re used.

Properties

Chapter 15: Properties

In this chapter, we’re going to learn about properties, which you’ve been using ever since we introduced them in our lesson on classes. However, we will now see some additional things you can do with properties.

Property Scope

Before we start, let’s revisit the idea of property scope. Remember that when you declare a variable or constant inside a function, you can’t access it outside that function, because each function has its own variable scope. Similarly, any properties inside a class are accessible by any of the class’ methods since they are all at class level.

Here’s the class hierarchy we created over the past few chapters:

class Person {
    var name = ""
    
    init() {
        
    }
    
    init(_ name: String) {
        self.name = name
    }
}

class Employee: Person {
    var salary = 0
    var role = ""
    
    override init(_ name: String) {
        super.init(name)
        self.role = "Analyst"
    }
    
    func doWork() {
        print("Hi my name is (name) and I'm doing work")
        salary += 1
    }
}

class Manager: Employee {
    var teamSize = 0
    
    override func doWork() {
        super.doWork()
        
        print("I'm managing people")
        salary += 2
    }
    
    func firePeople() {
        print("I'm firing people")
    }
}

We can access the salary property inside the doWork method because both salary and doWork are declared in Employee. Even other methods of Employee can also access salary and role.

Let’s now look at some unique things we can do with properties in addition to accessing them inside methods. Say we want to add a “bonus” property to determine how much of a year-end bonus a Manager would receive as part of their compensation:

class Manager: Employee {
    var teamSize = 0
    var bonus = 0
    
    init(_ name: String, _ teamSize: Int) {
        super.init(name)
        
        self.teamSize = teamSize
        self.bonus = teamSize * 1000
    }
    
    override func doWork() {
        super.doWork()
        
        print("I'm managing people")
        salary += 2
    }
    
    func firePeople() {
        print("I'm firing people")
    }
}

Above, we added an Int property called bonus, and let’s say its amount will depend on the number of people a Manager manages. In the Manager initializer function, we can say the bonus property equals the teamSize times 1000, meaning a Manager gets an extra $1,000 for each team member he or she manages.

Computed Properties

Of course, we could explicitly assign bonus to teamSize * 1000, but if our teamSize changes, we’d have to recalculate bonus as well. Also, teamSize could be changed anywhere in our class, so tracking all its changes and updating bonus accordingly in every one of those spots would be annoying.

Instead, we can make bonus a computed property which, unlike variables that store values, stores a calculation. To declare a computed property, we specify the name and data type, as with normal properties, but then we add a pair of curly braces, like with functions:

class Manager: Employee {
    var teamSize = 0
    var bonus: Int {
        // This is a computed property
        // When it's accessed, the code in here will run
        // Then, it will return the value
        
        return teamSize * 1000
    }
    
    init(_ name: String, _ teamSize: Int) {
        super.init(name)
        
        self.teamSize = teamSize
    }
    
    override func doWork() {
        super.doWork()
        
        print("I'm managing people")
        salary += 2
    }
    
    func firePeople() {
        print("I'm firing people")
    }
}

(Recall how self helps to distinguish between properties and arguments of the same name.)

Now, in our declaration of bonus, we’re saying its value is whatever the result of teamSize * 1000 is at a given time. This definition is much more flexible since we don’t need to constantly update bonus whenever something it depends on changes.

We can access bonus like a normal property since it is still a class property. Running the code below will print “11000” to the console, thanks to our computed property:

let m = Manager("Kate", 11)
print(m.bonus)

There are other aspects of properties like getters and setters and property observers, so we’ll have to do another lesson on properties later on. For now, this is a great start!

More Initializers

Chapter 16: More Initializers

Let’s revisit initializer methods by discussing designated and convenience initializers. To start, here’s a variation on the Person class we’ve used in the past chapters.

class Person {
    var name = "None"
}

The class has one property, name, initialized to the string “None.” Remember we learned that all classes have a default initializer that takes no arguments, so we can create a new Person object using Person(). We also discussed how the initializer method was meant to make sure a new object is set up and ready to go. Along with allocating memory for that object behind the scenes, an initializer method also makes sure all the object’s properties are initialized.

Let’s see how that works in action:

class Person {
    var name = "None"
    var netWorth:Int?
    var gender:String!
}

Above, we have an optional netWorth property, which Swift initializes to nil by default. We also have an implicitly unwrapped optional gender property, also initialized to nil. Without doing anything else, all our properties of the Person class are initialized, which is why we can create a new Person object using the default initializer.

What if some properties weren’t initialized? For example, let’s not initialize name to “None”:

Above, we see Xcode displays an error saying the Person class needs to have an initializer that sets up name with a String value. Now it is the responsibility of an initializer method to initialize name before a Person object can be created for us to use, otherwise it would not have any values. Let’s now add an initializer to actually initialize name. In this case, we set it to “None”:

class Person {
    var name: String
    var netWorth:Int?
    var gender:String!
    
    init() {
        name = "None"
    }
}

Now, let’s create a new Person object, which will call our initializer to do that, and print out its name. We should see “None” for a.name and “nil” for a.netWorth because it is optional.

let a = Person()
print(a.name)
print(a.netWorth)

Initializer methods that ensure all properties are initialized are designated initializers. These can be guaranteed to return an object to you where all properties are ready to be used.

The other type of initializer method is a convenience initializer. These methods allow you to pre-configure some properties of an object in a certain way, but they may rely on a designated initializer to make sure all properties still get initialized.

Let’s look at an example of a convenience initializer:

class Person {
    var name: String
    var netWorth:Int?
    var gender:String!
    
    // Designated Initializer
    init() {
        name = "None"
    }
    
    // Convenience Initializer
    convenience init(_ gender: String, netWorth: Int) {
        // Call the designated initializer to ensure that the object is ready to go
        self.init()
        
        //Set any other properties or custom code to initialize for this scenario
        self.gender = gender
        self.netWorth = netWorth
    }
}

You can see the convenience initializer is just a normal initializer labeled with the convenience keyword. First, it calls the designated initializer to initialize the name property and then sets up the gender and netWorth properties accordingly. This convenience initializer now helps us conveniently create Person objects that are rich:

// Creating a new Person object
let a = Person()

// Creating new rich Person objects
let b = Person("Male", netWorth: 10224334532)
let c = Person("Female", netWorth: 143211234321)

Notice our convenience initializer lets us easily create specific Person objects with whatever values we want. However, it must call the designated initializer first to make sure that all properties are initialized. This is something all convenience initializers must do before changing any properties of a class.

Chapter Recap

Designated initializers, which are required, guarantee that all class properties are initialized when creating new objects. Convenience initializers, on the other hand, are optional and help create objects in specific ways that you might find convenient in your program.

Arrays

Chapter 17: Arrays

For our final chapters, we’ll learn about some “collection” data types that help us organize and manage collections of data. In this chapter, you’ll learn how to manage data in what are called arrays. After a point, managing several pieces of data might prove difficult using just constants and variables, but arrays can make this much easier.

At their core, arrays are ordered collections of data. They only store one type of data, like how variables have just one data type. This way, we know exactly what to expect from them.

In the screenshot above from the documentation on collection types, you can see array values are stored in a specific order. You use the numbers on the left, called indexes, to access these values. Notice that the indexes start at 0, then go 1, 2, 3, etc.

Let’s create a motivating example for using arrays:

var a = "Dog"
var b = "Cat"
var c = "Bird"

Above, we have 3 simple String variables. Say we want to change them to “My Dog”, “My Cat”, and “My Bird” respectively. The code below presents one possible solution:

a = "My " + a
b = "My " + b
c = "My " + c

We simply reassigned each variable to a new string starting with “My “ followed by the string itself. For example, a becomes “My Dog”, and so on. Repeating this action all 3 variables isn’t much of a hassle, but for 100 or 200 items, this method would be quite tedious.

For a more streamlined approach, let’s start by putting our strings into an array:

var myArray = ["Dog", "Cat", "Bird"]

We denote our array using a pair of square brackets and separating the items “Dog”, “Cat”, and “Bird” with commas. Note that commas are only used in between the actual items. We say that “Dog” is at index 0, “Cat” is at index 1, and “Bird” is at index 2.

print(myArray[0])

In order to access the items “Dog”, “Cat”, and “Bird”, we use the array’s name followed by square brackets around the index of the value we want. If we wanted to print “Dog”, for example, we’d use index 0 in the brackets. Similarly, we’d use index 2 to get the value “Bird.”

Loops

The previous example demonstrated how to access values in an array. We can also assign or change values at certain indexes using the assignment operator, just like with variables:

myArray[0] = "My " + myArray[0]
print(myArray[0])

This example changes the first element in the array to “My Dog.” However, this system is still not efficient since we would need to do this for every index. Instead, we can use loops in conjunction with arrays to simplify this process:

for counter in 0...2 {
    print(myArray[counter])
}

Above, we have a for-in loop which will loop from 0 to 2. Inside the loop, we print the values in myArray using counter as the index. Running this should print “Dog”, “Cat”, and “Bird”. We’ll build upon this idea and try adding “My ” to each of our elements:

for counter in 0...2 {
    myArray[counter] = "My " + myArray[counter]
    print(myArray[counter])
}

We used counter to access each value of myArray again, added “My ” in front of the value, and reassigned it to the same index. If we had 200 items in my array, we could just change the upper range limit of my loop to 199. Now you can see how powerful it is to use arrays with loops.

Count

Until now, we’ve assumed myArray stores 3 elements, so our loop range was simply 0…2. What if we didn’t know how many elements myArray had? To help with this, arrays come with a handy property called count which gives gives us the number of items in an array. 

Notice our loop range always starts at 0, corresponding to the first index of our array. Now, we can end the range with myArray.count so it automatically runs as many loops as there are items in the array:

var myArray = ["Dog", "Cat", "Bird"]

for counter in 0...myArray.count {
    myArray[counter] = "My " + myArray[counter]
    print(myArray[counter])
}

However, Xcode gives us “fatal error: Index out of range” in the console when we try running this. That means Swift accessed an index that doesn’t exist in our array. How did this happen? Using myArray.count gave us the total number of items, in this case, 3. This meant our loop went from 0 to 3 inclusive, but we wanted a range from 0 to 2 inclusive. When it tried accessing index 3, there was nothing there, explaining why it crashed and gave us that error message.

Instead, we’ll need to manually shrink our range to prevent the loop from going too far:

var myArray = ["Dog", "Cat", "Bird"]

for counter in 0...myArray.count - 1 {
    myArray[counter] = "My " + myArray[counter]
    print(myArray[counter])
}

Notice we precisely changed our loop range by adding “-1” after the count. Sometimes such minor adjustments are necessary to get just the range you need. Regardless, there’s actually an even easier way to write this for loop without using ranges:

var myArray = ["Dog", "Cat", "Bird"]

for item in myArray {
    print(item)
}

The output now should not have changed from before. Notice this for loop has quite similar syntax to the previous one, only we’re now using an array in the range’s position. This is the simplest syntax for a loop over an array you can use. Our loop variable will now contain an element from the array on each loop, instead of a number like before. Use this method when you only want to work with the elements of the array directly. Otherwise, to write loops that need indexes to, for example, reassign values in an array, you will need to use the for loop with the ranges instead (from our previous example).

Declare an empty array

Declaring empty arrays is a tricky edge case. The empty array, [], is similar to nil in that assigning a variable to [] doesn’t help Swift determine the variable’s type. In this case, you can do one of two things:

var emptyArray: [String] = []
var emptyArray2 = [String]()

First, you can specify the variable’s type if you want it to be an empty array initially. For array variables, we specify data types by surrounding their values’ data type with square brackets. Because our array here stores Strings, its data type is [String]. Second, you can create a new array object of that array type, which would be equivalent to an empty array.

Add and Remove Items

Swift offers several ways to add items to an array:

var myArray = ["Dog", "Cat", "Bird"]

myArray.append("Raccoon")
myArray.insert("Frog", at: 0)
myArray += ["Frog", "Bear"]

for item in myArray {
    print(item)
}

We can add something to the end of an array, as on line 3. Otherwise, we can insert a new element at a particular index using the approach on line 4. This doesn’t replace the item at that index, but pushes everything back instead. On line 4, we add “Frog” to the beginning of the array, which appears when we run the following for loop to print out the elements.

We can also append arrays onto arrays, as line 5 demonstrates. Using the addition shorthand notation, we can attach two arrays together into one larger array. Our resulting array should now have “Frog” and “Bear” at the end of it.

Swift also has several functions for removing items from an array. For example, to remove a specific item, we simply supply its index to the remove function. The line below will remove the item at index 0 and shift all the other elements down to fill the gap:

myArray.remove(at: 0)

We can also use removeAll, removeFirst, removeLast, or removeFirst number of elements. Pick a certain function depending on what you want to do.

Search for items

Occasionally, you may want to search for something in your array or check that it exists at all. One way we can do that is by using the firstIndex method, which returns the first index where the specified value appears, as seen in the example below.

myArray.firstIndex(of: "Cat")

Alternatively, there’s the lastIndex method which returns the last index of the item you’re looking for. Like for adding or removing items, there are many options for searching, which you can explore further on your own.

Dictionaries

Chapter 18: Dictionaries

Finally, we will examine another collection type: dictionaries. These are common especially when using data from a remote database or a third party API.

Dictionaries are like special arrays that can be indexed by anything. Arrays allowed us to access data using numeric indexes, and dictionaries allow us to retrieve values using indexes of other data types. How does this work? Well, data is added to dictionaries in key-value pairs. For every value you want to add to your dictionary, you must supply a corresponding key that you would use to access that value later on.

Recall that arrays could only store data of a specific type. Similarly, dictionaries can only use keys and values of a specific type. The key and value types don’t have to match, but all keys should have the same type, as for values.

In a way, Swift dictionaries resemble actual dictionaries where each word is a key, and the word’s definition is like the value associated with the key. In the diagram below from the documentation on collection types, the values are the airport name, and the keys the airport codes.

Declaring a Dictionary    

Let’s learn how to declare dictionaries and perform basic operations similar to those we’ve seen with arrays.

var myDictionary = [String:String]()

Notice our declaration resembles that of an array. In fact, this code also creates a new dictionary object in memory. Dictionary data types are specified with the key data type, followed by a colon, then the value data type. The example above uses [String:String] as our data type, meaning both our keys and values will be Strings.

Say we want to create a database of registered cars with their license plates. Using license plates as keys is a good example because they’re unique. Here’s how we would add an entry for a fictitious car to this dictionary:

myDictionary["SJD 293"] = "Red Ferrari"
print(myDictionary["SJD 293"])

Our key in this case is “SJD 293” and the value is “Red Ferrari.” Just like how we access data in arrays using indexes, we can access data in dictionaries with keys.

Notice we got “Optional(“Red Ferrari”)” when printing what we just added. But we never added an optional into our dictionary, so where did this come from? Swift automatically wraps all your dictionary values in an optional type. That way, if you try using a key that doesn’t exist in your dictionary, it can conveniently give you nil in that case. Otherwise, you’ll get your value as an optional which you would unwrap later using any of the methods we’ve discussed before.

What if we wanted to change cars but keep the same license plate? We can simply assign a different value to our license plate key in the dictionary:

myDictionary["SJD 293"] = "Black Lambo"

Now what if we sold the car and no longer have it? To remove a value for a key, we just assign nil to it:

myDictionary["SJD 293"] = nil

Iterating Through a Dictionary

Finally, we’ll learn how to iterate or loop over items in a dictionary. Even though dictionaries have no inherent order, we can still use a for loop to process each of its elements:

myDictionary["SJD 293"] = "Red Ferrari"
myDictionary["UDS 111"] = "Silver Porsche"

for (key, value) in myDictionary {
    print("(key) is a (value)")
}

This is the end of our chapter on dictionaries. These are most useful when you want to store two kinds of data that have a natural association. Swift has many other collection types you can use, but for the most part, you’ll store a lot of your data in arrays and dictionaries.

Conclusion

If you made it this far, give yourself a well deserved pat on the back. This is a huge achievement and now you’ve got a solid foundation in the Swift programming language. You’re well on your way to Swift mastery!

Did you enjoy this Swift tutorial? Our team worked really hard to put this together!

If it was helpful for you, please leave a comment below and let us know! If you’d like to continue learning, make sure you’re subscribed to the CodeWithChris YouTube channel where we post weekly video tutorials.


Основы :

=>Переменные и константы
=>Коментарии
=>Точка с запятой
=>Целые числа
=>Числа с плавающей точкой / Floating-Point Numbers
=>Тип безопасности и вывод типа
=>Числовые литералы / Numeric Literals
=>Числовое Преобразование Типа / Numeric Type Conversion
=>Тип Алиасы / Type Aliases
=>Логические типы / Booleans
=>Кортеж / Tuples
=>Optionals
=>Обработка ошибок

Swift — это новый язык программирования для разработки приложений для iOS, macOS, watchOS и tvOS.
Тем не менее, многие части Swift будут знакомы из вашего опыта разработки на C и Objective-C.

Swift предоставляет свои собственные версии всех основных типов C и Objective-C, включая Int для целых чисел,
Double и Float для значений с плавающей запятой, Bool для логических значений и String для текстовых данных.
Swift также предоставляет мощные версии трех основных типов коллекций, Array, Set и Dictionary,
как описано в Collection Types

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

В дополнение к знакомым типам, Swift представляет расширенные типы, которых нет в Objective-C, такие как кортежи.
Кортежи позволяют создавать и передавать группы значений.
Вы можете использовать кортеж для возврата нескольких значений из функции в виде одного составного значения.

Swift также вводит необязательные/Optionals типы, которые обрабатывают отсутствие значения.
Необязательные/Optionals говорят, что «есть значение, и оно равно x», или «значение вообще не существует».
Использование Optionals похоже на использование nil с указателями в Objective-C,
но они работают для любого типа, а не только для классов.
Optionals опции не только более безопасны и более выразительны,
чем nil указатели в Objective-C, но и лежат в основе многих наиболее мощных функций Swift.

Swift — это типобезопасный язык, который означает, что он поможет вам понять типы значений, с которыми может работать ваш код.
Если часть вашего кода требует String, безопасность типа не позволяет вам передать ему Int по ошибке.
Аналогично, безопасность типов предотвращает случайную передачу optional String в фрагмент кода,
для которого требуется nonoptional 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

|Если сохраненное значение в вашем коде не изменится, 
|всегда объявляйте его как константу с ключевым словом <b>let</b>. 
|Используйте переменные только для хранения значений, которые должны быть в состоянии изменить.

Тип Аннотации

Вы можете предоставить аннотацию типа при объявлении константы или переменной, чтобы иметь представление о типе значений, которые константа или переменная могут хранить.
Напишите аннотацию типа, поместив двоеточие после имени константы или переменной, за которым следует пробел, а затем имя используемого типа.

Этот пример предоставляет аннотацию типа для переменной с именем welcomeMessage, чтобы указать,
что переменная может хранить значения String:

| var welcomeMessage: String

Двоеточие в объявлении означает «… тип…», поэтому приведенный выше код можно прочитать как:

«Объявите переменную с именем welcomeMessage типа String».

Фраза «тип String» означает «может хранить любое значение String».
Думайте о нем как о значении «тип вещи» (или «вид вещи»), который может быть сохранен.

Переменная welcomeMessage теперь может быть установлена на любое строковое значение без ошибки:

| welcomeMessage = "Hello"

Вы можете определить несколько связанных переменных одного и того же типа в одной строке,
разделенных запятыми, с аннотацией одного типа после окончательного имени переменной:

| var red, green, blue: Double


  На практике редко нужно писать аннотации типов. Если вы укажете начальное значение для константы или
  переменной в точке, которую она определила, Swift почти всегда может определить тип, 
  который будет использоваться для этой константы или
  переменной, как описано в Тип безопасности и Вывод типа. 
  В приведенном выше примере welcomeMessage начальное значение не
  указывается, поэтому тип переменной welcomeMessage указывается с аннотацией типа, 
  а не выводится из начального значения.

Именование констант и переменных :

Имена констант и переменных могут содержать практически любой символ, включая символы Юникода:

        1 | let π = 3.14159
        2 | let 你好 = "你好世界"
        3 | let 🐶🐮 = "dogcow"

Имена констант и переменных не могут содержать пробельные символы, математические символы, стрелки,
скалярные значения Unicode для частного использования или символы рисования линий и рамок.
И при этом они не могут начинаться с числа, хотя числа могут быть включены в другом месте в имени.

После того, как вы объявили константу или переменную определенного типа,
вы не можете объявить ее снова с тем же именем или изменить ее для хранения значений другого типа.
Вы также не можете изменить константу в переменную или переменную в константу.

    | Если вам нужно присвоить константе или переменной то же имя, 
    | что и зарезервированному ключевому слову Swift, заключите его в кавычки (') 
    | при использовании его в качестве имени.
    | Однако избегайте использования ключевых слов в качестве имен, 
    | если у вас нет абсолютно никакого выбора.

Вы можете изменить значение существующей переменной на другое значение совместимого типа.
В этом примере значение friendlyWelcome изменяется с «Hello!» на «Bonjour!»

        1 | var friendlyWelcome = "Hello!"
        2 | friendlyWelcome = "Bonjour!"
        3 | // friendlyWelcome is now "Bonjour!"

В отличие от переменной, значение константы не может быть изменено после ее установки.
Попытка сделать это сообщается как ошибка при компиляции вашего кода:

        1 | let languageName = "Swift"
        2 | languageName = "Swift++"
        3 | // This is a compile-time error: languageName cannot be changed.

Print() Константы и переменные

Вы можете напечатать текущее значение константы или переменной с помощью
функции print (_: separator: terminator :):

        1 | print(friendlyWelcome)
        2 | // Prints "Bonjour!"

Функция print (: separator: terminator :) — это глобальная функция, которая печатает одно или несколько значений в соответствующий вывод.
Например, в XCode функция print (
: separator: terminator :) выводит свой вывод в «консольную» панель XCode.

Параметры (_: separator: terminator :) — имеют значения по умолчанию, поэтому их можно опустить при вызове этой функции.
По умолчанию функция завершает строку, которую печатает, добавляя разрыв строки.
Чтобы напечатать значение без разрыва строки после него, передайте пустую строку в качестве терминатора, например

    1 | print (someValue, terminator: "").            

Для получения информации о параметрах со значениями по умолчанию
см. Значения параметров по умолчанию.
Default Parameter Values.

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

Оберните имя в круглых скобках и экранируйте его обратной косой чертой перед открывающей скобкой:

    1 | print("The current value of friendlyWelcome is (friendlyWelcome)")
    2 | // Prints "The current value of friendlyWelcome is Bonjour!"
    
Все параметры, которые вы можете использовать со строковой интерполяцией, 
описаны в разделе 
<a href="https://docs.swift.org/swift-book/LanguageGuide/StringsAndCharacters.html#ID292">Строковая интерполяция.</a>

Коментарии :

Используйте комментарии, чтобы включить неисполняемый текст в свой код, как примечание или напоминание для себя.
Комментарии игнорируются компилятором Swift при компиляции вашего кода.

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

Многострочные комментарии начинаются с косой черты, за которой следует звездочка (/ ),
и заканчиваются звездочкой, за которой следует косая черта (
/):

    /* This is also a comment
    but is written over multiple lines. */

В отличие от многострочных комментариев в C, многострочные комментарии в Swift могут быть вложены в другие многострочные комментарии.
Вы пишете вложенные комментарии, запуская блок многострочных комментариев, а затем начиная второй многострочный комментарий в первом блоке.
Затем закрывается второй блок, за которым следует первый блок:

    /* This is the start of the first multiline comment.
     /* This is the second, nested multiline comment. */
    This is the end of the first multiline comment. */

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

Точка с запятой

В отличие от многих других языков, Swift не требует от вас писать точку с запятой (;)
после каждого оператора в вашем коде, хотя вы можете сделать это, если хотите.
Однако точки с запятой необходимы, если вы хотите написать несколько отдельных операторов в одной строке:

        1 | let cat = "🐱"; print(cat)
        2 | // Prints "🐱"

Целые числа

Целые числа — это целые числа без дробной составляющей, такие как 42 и -23.
Целые числа либо со знаком (положительный, ноль или отрицательный), либо без знака (положительный или ноль).

Swift предоставляет целые числа со знаком и без знака в 8, 16, 32 и 64-битных формах.

    Эти целые числа следуют соглашению об именах, аналогичному C, 
    в котором 8-разрядное целое число без знака имеет тип UInt8, 
    а 32-разрядное целое число со знаком имеет тип Int32.

Как и все типы в Swift, эти целочисленные типы имеют заглавные имена.

Целочисленные границы

Вы можете получить доступ к минимальным и максимальным значениям каждого целочисленного типа с помощью свойств min и max:

    1 | let minValue = UInt8.min  // minValue is equal to 0, and is of type UInt8
    2 | let maxValue = UInt8.max  // maxValue is equal to 255, and is of type UInt8

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

Int

В большинстве случаев вам не нужно выбирать конкретный размер целого числа для использования в вашем коде.
Swift предоставляет дополнительный целочисленный тип Int, который имеет тот же размер,
что и собственный размер текущей платформы:

    * On a 32-bit platform, Int is the same size as Int32.
    * On a 64-bit platform, Int is the same size as Int64.

Если вам не нужно работать с определенным размером целого числа,
всегда используйте Int для целочисленных значений в вашем коде.

Это помогает согласованности кода и совместимости.
Даже на 32-разрядных платформах Int может хранить любое значение от -2 147 483 648 до 2 147 483 647
и является достаточно большим для многих целочисленных диапазонов.

UInt

Swift также предоставляет целочисленный тип без знака, UInt, который имеет тот же размер, что и собственный размер текущей платформы:

        * On a 32-bit platform, UInt is the same size as UInt32.
        * On a 64-bit platform, UInt is the same size as UInt64.


Используйте UInt только тогда, когда вам нужен целочисленный тип без знака с тем же размером, 
что и собственный размер платформы.
Если это не так, предпочтительнее Int, даже когда известно, что значения, 
которые должны быть сохранены, неотрицательны.
Последовательное использование Int для целочисленных значений способствует совместимости кода,
устраняет необходимость преобразования между различными типами чисел и 
соответствует выводу целочисленных типов, 
как описано в разделе  
<a href="https://docs.swift.org/swift-book/LanguageGuide/TheBasics.html#ID322">Type Safety and Type Inference.</a>

Числа с плавающей точкой / Floating-Point Numbers

Числа с плавающей точкой — это числа с дробной составляющей, такие как 3.14159, 0.1 и -273.15.

Типы с плавающей точкой могут представлять гораздо более широкий диапазон значений, чем целочисленные типы,
и могут хранить числа, которые намного больше или меньше, чем могут быть сохранены в Int.

Swift предоставляет два типа чисел с плавающей точкой:

- Double represents a 64-bit floating-point number.
- Float represents a 32-bit floating-point number.

Double имеет точность не менее 15 десятичных цифр, тогда как точность Float может составлять всего 6 десятичных цифр.
Соответствующий тип с плавающей точкой для использования зависит от природы и диапазона значений,
с которыми вам нужно работать в вашем коде. В ситуациях, когда подходит любой тип, предпочтительнее Double.

Тип безопасности и вывод типа

Swift — это язык, безопасный для типов.
Безопасный тип языка предлагает вам четко определить типы значений, с которыми может работать ваш код.
Если часть вашего кода требует String, вы не можете передать ему Int по ошибке.

Поскольку Swift является безопасным в использовании типов, он выполняет проверки типов при компиляции кода
и помечает любые несовпадающие типы как ошибки.
Это позволяет вам выявлять и исправлять ошибки как можно раньше в процессе разработки.

Проверка типов помогает избежать ошибок при работе с различными типами значений.
Однако это не означает, что вы должны указывать тип каждой константы и переменной, которые вы объявляете.
Если вы не указали требуемый тип значения, Swift использует определение типа для определения подходящего типа.

Вывод типа позволяет компилятору автоматически определять тип определенного выражения при компиляции вашего кода, просто проверяя предоставленные вами значения.

Из-за вывода типов Swift требует гораздо меньше объявлений типов, чем такие языки, как C или Objective-C.
Константы и переменные по-прежнему явно типизированы, но большая часть работы по определению их типа сделана за вас.

Вывод типа особенно полезен, когда вы объявляете константу или переменную с начальным значением.
Это часто делается путем присвоения литерального значения (или литерала) константе или переменной в точке, в которой вы ее объявляете.
(Литеральное значение — это значение, которое отображается непосредственно в вашем исходном коде, например, 42 и 3.14159 в приведенных ниже примерах.)

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

    let meaningOfLife = 42
    // meaningOfLife is inferred to be of type Int

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

    let pi = 3.14159
    // pi is inferred to be of type Double

Swift всегда выбирает Double (вместо Float), когда выводит тип чисел с плавающей точкой.

Если вы комбинируете целочисленные литералы и литералы с плавающей точкой в ​​выражении,
тип Double будет выведен из контекста:

    let anotherPi = 3 + 0.14159
    // anotherPi is also inferred to be of type Double

Буквальное значение 3 не имеет явного типа само по себе,
и поэтому соответствующий тип вывода Double выводится из-за
присутствия литерала с плавающей запятой как части сложения.

Числовые литералы / Numeric Literals

Целочисленные литералы могут быть записаны как:

    - Десятичное число без префикса 
    - Двоичное число с префиксом -> 0b 
    - Восьмеричное число с префиксом -> 0o 
    - Шестнадцатеричное число с префиксом -> 0x

Все эти целочисленные литералы имеют десятичное значение 17:

    1 | let decimalInteger = 17           // 17 в десятичной системе записи
    2 | let binaryInteger = 0b10001       // 17 в двоичной записи
    3 | let octalInteger = 0o21           // 17 в восьмиричной записи
    4 | let hexadecimalInteger = 0x11     // 17 в шестнадцатиричной записи

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

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

    1.25e2 means 1.25 x 102, or 125.0.
    1.25e-2 means 1.25 x 10-2, or 0.0125.

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

    0xFp2 means 15 x 22, or 60.0.
    0xFp-2 means 15 x 2-2, or 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

Числовое Преобразование Типа / Numeric Type Conversion

Используйте тип Int для всех целочисленных констант общего назначения и переменных в вашем коде
даже если известно, что они неотрицательны.
Использование целочисленного типа по умолчанию в повседневных ситуациях означает,
что целочисленные константы и переменные немедленно взаимодействуют в вашем коде
и будут соответствовать выведенному типу для целочисленных литеральных значений.

Используйте другие целочисленные типы только в том случае, если они особенно необходимы для выполняемой задачи,
из-за явного размера данных из внешнего источника или для производительности,
использования памяти или другой необходимой оптимизации.

Использование типов с явно заданными размерами в этих ситуациях помогает уловить
любые случайные переполнения значений
и неявно документирует природу используемых данных.

Целочисленное преобразование

Диапазон чисел, которые могут быть сохранены в целочисленной константе или переменной,
различен для каждого числового типа.

    Константа или переменная Int8 может хранить числа от -128 до 127

в то время как

    Константа или переменная UInt8 может хранить числа от 0 до 255

Число, которое не помещается в константу или переменную целочисленного типа размера,
сообщается как ошибка при компиляции кода:

    let cannotBeNegative: UInt8 = -1
    // UInt8 cannot store negative numbers, and so this will report an error
    
    let tooBig: Int8 = Int8.max + 1
    // Int8 cannot store a number larger than its maximum value,
    // and so this will also report an error

Поскольку каждый числовой тип может хранить различный диапазон значений,
вы должны выбрать преобразование числового типа в каждом конкретном случае.
Этот дополнительный подход предотвращает скрытые ошибки преобразования
и помогает четко определить намерения преобразования типа в вашем коде.

Чтобы преобразовать один конкретный тип номера в другой, вы инициализируете новый номер нужного типа с существующим значением.
В приведенном ниже примере константа 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.

SomeType (ofInitialValue) является способом по умолчанию для вызова инициализатора типа Swift
и передачи начального значения.
Под капотом UInt16 имеет инициализатор, который принимает значение UInt8,
и поэтому этот инициализатор используется для создания нового UInt16 из существующего UInt8.
Однако вы не можете передавать здесь любой тип — это должен быть тип, для которого UInt16 предоставляет инициализатор.
Расширение существующих типов для обеспечения инициализаторов, которые принимают новые типы (включая ваши собственные определения типов), рассматривается в главе «Расширения» Extensions.

Целочисленное преобразование и преобразование с плавающей точкой

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

   | let three = 3
   | let pointOneFourOneFiveNine = 0.14159
   | let pi = Double(three) + pointOneFourOneFiveNine
    
    // pi equals 3.14159, and is inferred to be of type Double
    //Здесь значение константы три используется для создания нового значения типа Double, 
    //так что обе стороны сложения имеют одинаковый тип.

Без этого преобразования добавление не будет разрешено.

Преобразование с плавающей точкой в ​​целое также должно быть сделано явным.
Целочисленный тип может быть инициализирован значением Double или Float:

    |let integerPi = Int(pi)
    
    // integerPi equals 3, and is inferred to be of type Int

Значения с плавающей точкой всегда усекаются при использовании для
инициализации нового целочисленного значения таким способом.

    Это означает, что 4,75 становится 4, а -3,9 становится -3.


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

Тип Алиасы / Type Aliases

Тип Алиасы — определяют альтернативное имя для существующего типа.
Тип Алиасы — определяются с помощью ключевого слова typealias.
Тип Алиасы -полезны, когда вы хотите сослаться на существующий тип по имени, которое контекстуально более уместно,
например, при работе с данными определенного размера из внешнего источника:

    typealias AudioSample = UInt16

Определив Тип Алиасы, вы можете использовать алиасы везде, где вы можете использовать исходное имя:

    var maxAmplitudeFound = AudioSample.min
    // maxAmplitudeFound is now 0

Здесь AudioSample определяется как псевдоним для UInt16.
Поскольку это алиас, вызов AudioSample.min фактически вызывает UInt16.min,
который предоставляет начальное значение 0 для переменной maxAmplitudeFound.

Логические типы / Booleans

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.")
        }
        
        // Prints "Eww, turnips are horrible."     

Условные операторы, такие как оператор if, более подробно описаны в главе Control Flow.

Безопасность типов Swift предотвращает замену Bool не-булевыми значениями.

Следующий пример сообщает об ошибке времени компиляции:

    let i = 1
    
    if i {
    // this example will not compile, and will report an error
    }

Тем не менее, приведенный ниже альтернативный пример действителен:

    let i = 1
    if i == 1 {
        // this example will compile successfully
    }

Результат сравнения i == 1 имеет тип Bool, поэтому второй пример проходит проверку типа.
Сравнения типа i == 1 обсуждаются в разделе «Основные операторы».

Как и в случае с другими примерами безопасности типов в Swift, этот подход позволяет избежать случайных ошибок и гарантирует, что цель конкретного раздела кода всегда чистая.

Кортеж / Tuples

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

В этом примере (404, «Not Found») — это кортеж, который описывает код состояния HTTP.
Код состояния HTTP — это специальное значение, возвращаемое веб-сервером каждый раз, когда вы запрашиваете веб-страницу.
Код состояния 404 Not Found возвращается, если вы запрашиваете несуществующую веб-страницу.

    let http404Error = (404, "Not Found")
    // http404Error is of type (Int, String), and equals (404, "Not Found")

Кортеж (404, «Not Found») группирует Int и String, чтобы дать коду статуса HTTP два отдельных значения:
число и удобочитаемое описание.
Его можно описать как «кортеж типа (Int, String)».

Вы можете создавать кортежи из любой перестановки типов, и они могут содержать столько разных типов, сколько вам нужно.
Ничто не мешает вам иметь кортеж типа (Int, Int, Int) или (String, Bool),
или даже любую другую перестановку, которая вам требуется.

Вы можете разложить содержимое кортежа на отдельные константы или переменные,
к которым вы затем получите доступ, как обычно:

    let (statusCode, statusMessage) = http404Error
    print("The status code is (statusCode)")    // Prints "The status code is 404"
    
    print("The status message is (statusMessage)")   // Prints "The status message is Not Found"

Если вам нужны только некоторые значения кортежа, игнорируйте части кортежа с подчеркиванием (_)
при decompose / разворачивании кортежа:

    let (justTheStatusCode, _) = http404Error
    print("The status code is (justTheStatusCode)")   // Prints "The status code is 404"

В качестве альтернативы, получить доступ к значениям отдельных элементов в кортеже, используя номера индекса,
начинающиеся с нуля:

    let http404Error = (404, "Not Found")
    print("The status code is (http404Error.0)")   // Prints "The status code is 404"
    print("The status message is (http404Error.1)")  // Prints "The status message is Not Found"

Вы можете назвать отдельные элементы в кортеже, когда кортеж определен:

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

Если вы называете элементы в кортеже, вы можете использовать имена элементов для доступа к значениям этих элементов:

    let http200Status = (statusCode: 200, description: "OK")
    print("The status code is (http200Status.statusCode)")  // Prints "The status code is 200 "
    print("The status message is (http200Status.description)")  // Prints "The status message is OK"

Кортежи особенно полезны в качестве возвращаемых значений функций.
Функция, которая пытается получить веб-страницу, может возвращать тип кортежа (Int, String),
чтобы описать успех или неудачу поиска страницы.

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

Для получения дополнительной информации см. Функции с несколькими возвращаемыми значениями.

        Кортежи полезны для временных групп связанных значений. 
        Они не подходят для создания сложных структур данных. 
        Если ваша структура данных может сохраняться за пределами временной области, 
        смоделируйте ее как класс или структуру, а не как кортеж.
        Для получения дополнительной информации см. Структуры и классы.

Optionals

Вы используете дополнительные функции в ситуациях, когда значение может отсутствовать.
Optionals представляет две возможности:
Либо есть значение, и вы можете развернуть Optional для доступа к этому значению,
либо значение вообще не существует.

    Концепция Optionals не существует в C или Objective-C. 
    Самая близкая вещь в Objective-C - это возможность вернуть nil из метода, 
    который иначе возвратил бы объект, при этом nil означает «отсутствие действительного объекта.»
    Однако это работает только для объектов - это не работает для структур, базовых типов C 
    или значений перечисления. 
    Для этих типов методы Objective C обычно возвращают специальное значение (такое как NSNotFound), 
    чтобы указать на отсутствие значения.


    Этот подход предполагает, что вызывающий метод знает, что существует 
    специальное значение для проверки, и не забывает проверять его. 
    Optionals Swift позволяют вам указать отсутствие значения для любого типа 
    вообще без необходимости использования специальных констант.

Вот пример того, как необязательные параметры могут использоваться, чтобы справиться с отсутствием значения.
Тип Int в Swift имеет инициализатор, который пытается преобразовать значение String в значение Int.

Однако не каждая строка может быть преобразована в целое число.
Строка «123» может быть преобразована в числовое значение 123, но строка «привет, мир»
не имеет очевидного числового значения для преобразования.

В приведенном ниже примере используется инициализатор, чтобы попытаться преобразовать String в Int:

    let possibleNumber = "123"
    let convertedNumber = Int(possibleNumber)
    // convertedNumber is inferred to be of type "Int?", or "optional Int"

Поскольку инициализатор может потерпеть неудачу, он возвращает необязательный Int, а не Int.
Необязательный Int записывается как Int?, а не Int.

Знак вопроса ? указывает, что значение, которое оно содержит, является необязательным,
то есть оно может содержать некоторое значение типа Int или вообще не содержать значения.
(Он не может содержать ничего другого, например значение Bool или значение String.
Это либо Int, либо вообще ничего.)

nil

Вы устанавливаете необязательную переменную в состояние «ничего» , присваивая ей специальное значение nil:

    var serverResponseCode: Int? = 404
    // serverResponseCode содержит фактический Int со значением 404
    serverResponseCode = nil
    // serverResponseCode сейчас содержит значение - ничего 

ПРИМЕЧАНИЕ:
Вы не можете использовать nil с nonoptional константами и переменными. 
Если константа или переменная в вашем коде должны работать с отсутствием значения 
при определенных условиях, 
всегда объявляйте его как optional значение соответствующего типа.

Если вы определяете необязательную переменную без указания значения по умолчанию,
для нее автоматически устанавливается nil:

    var surveyAnswer: String?
    // surveyAnswer is automatically set to nil

ПРИМЕЧАНИЕ:
nil Свифта не совпадает с nil в Objective-C. В Objective-C nil — указатель на несуществующий объект.
В Swift nil не является указателем — это отсутствие значения определенного типа.
Необязательные типы любого типа могут быть установлены на nil, а не только на типы объектов.

Оператор if и принудительное развертывание

Вы можете использовать оператор if, чтобы выяснить, содержит ли опциональное значение ,
сравнивая опциональное значение с nil.
Вы выполняете это сравнение с помощью оператора «равно» (==) или оператора «не равно» (! =).

Если необязательный параметр имеет значение, он считается «не равным» nil:

    if convertedNumber != nil {
        print("convertedNumber contains some integer value.")
    }
    // Prints "convertedNumber contains some integer value."

Убедившись в том, что optional содержит значение, вы можете получить доступ к его базовому значению,
добавив восклицательный знак (!) В конце имени дополнительного элемента.

Восклицательный знак фактически говорит: «Я знаю, что эта опция определенно имеет значение; пожалуйста, используйте его ».
Это называется принудительным развертыванием значения необязательного параметра:

    if convertedNumber != nil {
        print("convertedNumber has an integer value of (convertedNumber!).")
    }
    // Prints "convertedNumber has an integer value of 123."

Для получения дополнительной информации об операторе if см. ,a href=»https://docs.swift.org/swift-book/LanguageGuide/ControlFlow.html»>Control Flow.

   ПРИМЕЧАНИЕ
   Пытаясь использовать! доступ к несуществующему необязательному значению вызывает ошибку 
   во время выполнения. 
   Всегда убедитесь, что необязательный параметр содержит не-nil значение перед 
   использованием ! что-бы принудительно развернуть его значение.

Опциональная привязка

Вы используете необязательную привязку, чтобы выяснить, содержит ли необязательное значение,
и если да, чтобы сделать это значение доступным в качестве временной константы или переменной.
Необязательное связывание может использоваться с операторами if и while для проверки значения внутри необязательного,
и извлечь это значение в константу или переменную, как часть одного действия. операторы if и while более подробно
описаны в Control Flow.

Напишите необязательную привязку для оператора if следующим образом:

         if let constantName = someOptional {
            statements
        }

Вы можете переписать пример possibleNumber из раздела Optionals, чтобы использовать Optionals привязку
вместо принудительной распаковки:

    if let actualNumber = Int(possibleNumber) {
        print("The string "(possibleNumber)" has an integer value of (actualNumber)")
    } else {
        print("The string "(possibleNumber)" could not be converted to an integer")
    }
    // Prints "The string "123" has an integer value of 123"

Этот код можно прочитать как:

«Если необязательный Int, возвращаемый Int (возможный номер),
содержит значение, установите новую константу с именем actualNumber в значение, содержащееся в необязательном элементе».

Если преобразование прошло успешно, константа actualNumber становится доступной
для использования в первой ветви оператора if. Он уже был инициализирован значением,
содержащимся в необязательном значении, поэтому нет необходимости использовать! суффикс для доступа к его значению.
В этом примере actualNumber просто используется для печати результата преобразования.

Вы можете использовать как константы, так и переменные с необязательной привязкой.
Если вы хотите манипулировать значением factNumber в первой ветви оператора if,
вы можете написать вместо него var actualNumber, и значение, содержащееся в необязательном элементе,
будет доступно как переменная, а не как константа.

Вы можете включить столько необязательных привязок и логических условий в один оператор if,
сколько вам нужно, через запятую. Если какое-либо из значений в необязательных привязках равно nil
или какое-либо логическое условие оценивается как ложное, условие целого if считается ложным.
Следующие операторы if эквивалентны:

        if let firstNumber = Int("4"), let secondNumber = Int("42"), firstNumber < secondNumber && secondNumber < 100 {
            print("(firstNumber) < (secondNumber) < 100")
        }
        // Prints "4 < 42 < 100"

        if let firstNumber = Int("4") {
            if let secondNumber = Int("42") {
                if firstNumber < secondNumber && secondNumber < 100 {
                    print("(firstNumber) < (secondNumber) < 100")
                }
            }
        }
        // Prints "4 < 42 < 100"
        
    ПРИМЕЧАНИЕ

    Константы и переменные, созданные с необязательной привязкой в операторе if, доступны только 
    в теле оператора if. 
    Напротив, константы и переменные, созданные с помощью оператора защиты, доступны в строках кода, 
    следующих за оператором охраны, как описано в разделе «Ранний выход».           

Неявно развернутые optionals

Как описано выше, необязательные параметры указывают, что константе или переменной
разрешено иметь значение «без значения». Необязательные могут быть проверены с помощью оператора if,
чтобы увидеть, существует ли значение, и могут быть условно развернуты с необязательным
связыванием для доступа к значению необязательного, если оно существует.

Иногда из структуры программы ясно, что необязательный параметр всегда будет иметь
значение после того, как это значение будет впервые установлено. В этих случаях полезно
избавиться от необходимости проверять и развертывать значение необязательного значения при
каждом обращении к нему, поскольку можно с уверенностью предположить, что оно всегда имеет значение.

Эти типы дополнительных опций определены как неявно развернутые дополнительные.
Вы пишете неявно развернутый необязательный элемент, помещая восклицательный знак (String!)
Вместо знака вопроса (String?) После типа, который вы хотите сделать необязательным.

Неявно развернутые необязательные параметры полезны, когда подтверждается, что значение
необязательного значения существует сразу после того, как необязательное значение впервые определено,
и определенно можно предположить, что оно существует в каждой точке после этого.
Основное использование неявно развернутых необязательных опций в Swift происходит
во время инициализации класса, как описано в «Неизвестных ссылках» и «Неявно развернутые необязательные свойства».

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

        let possibleString: String? = "An optional string."
        let forcedString: String = possibleString! // requires an exclamation mark

        let assumedString: String! = "An implicitly unwrapped optional string."
        let implicitString: String = assumedString // no need for an exclamation mark

Вы можете думать о неявно развернутом дополнительном компоненте как о предоставлении
разрешения на автоматическое развертывание дополнительного необязательного содержимого
при каждом его использовании. Вместо того, чтобы ставить восклицательный знак после
имени дополнительного элемента каждый раз, когда вы его используете, вы ставите
восклицательный знак после типа дополнительного элемента при его объявлении.

        ПРИМЕЧАНИЕ
         Если неявно развернутый необязательный параметр равен nil 
         и вы пытаетесь получить доступ к его упакованному значению, 
         вы вызовете ошибку времени выполнения. Результат точно такой же, 
         как если бы вы поместили восклицательный знак после обычного необязательного,
         который не содержит значения.

Вы все еще можете обрабатывать неявно развернутый необязательный параметр как обычный необязательный,
чтобы проверить, содержит ли он значение:

        if assumedString != nil {
            print(assumedString!)
        }
        // Prints "An implicitly unwrapped optional string."

Вы также можете использовать неявно развернутый необязательный параметр с необязательным связыванием,
чтобы проверить и развернуть его значение в одном выражении:

        if let definiteString = assumedString {
            print(definiteString)
        }
        // Prints "An implicitly unwrapped optional string."
        
        
    ПРИМЕЧАНИЕ
    Не используйте неявно развернутый необязательный параметр, если существует вероятность того, 
    что переменная станет нулевой в более поздний момент. Всегда используйте обычный необязательный тип, 
    если вам нужно проверить значение nil в течение времени жизни переменной.

Обработка ошибок

Вы используете обработку ошибок, чтобы реагировать на ошибки, с которыми ваша программа может столкнуться во время выполнения.

В отличие от опций, которые могут использовать наличие или отсутствие значения для сообщения
об успехе или сбое функции, обработка ошибок позволяет вам определить основную причину сбоя и,
при необходимости, распространить ошибку на другую часть вашей программы. ,

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

    func canThrowAnError() throws {
        // this function may or may not throw an error
    }

Функция указывает, что может выдать ошибку, включив ключевое слово throws в свое объявление.
Когда вы вызываете функцию, которая может выдать ошибку, вы добавляете ключевое слово try к выражению.

Swift автоматически распространяет ошибки из их текущей области, пока они не будут обработаны предложением catch.

        do {
            try canThrowAnError()
            // no error was thrown
        } catch {
            // an error was thrown
        }

Оператор 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,
выполнение кода продолжается как обычно. Если условие оценивается как ложное,
текущее состояние программы недействительно; выполнение кода заканчивается, и ваше приложение прекращается.

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

Помимо проверки ваших ожиданий во время выполнения, утверждения и предварительные условия
также становятся полезной формой документации в коде. В отличие от условий ошибок,
описанных в разделе «Обработка ошибок» выше, утверждения и предварительные условия
не используются для исправляемых или ожидаемых ошибок.
Поскольку ошибочное утверждение или предварительное условие указывают
на недопустимое состояние программы, невозможно отыскать ошибочное утверждение.

Использование утверждений и предварительных условий не заменяет разработку вашего кода таким образом,
что вряд ли возникнут недопустимые условия. Однако использование их для обеспечения правильности данных
и состояния приводит к более предсказуемому завершению работы приложения
в случае возникновения недопустимого состояния и облегчает отладку проблемы.
Остановка выполнения при обнаружении недопустимого состояния также помогает ограничить ущерб,
вызванный этим недопустимым состоянием.

Разница между утверждениями и предварительными условиями заключается в том,
что они проверяются: утверждения проверяются только в отладочных сборках,
а предварительные условия проверяются как в отладочных, так и в производственных сборках.
В производственных сборках условие внутри утверждения не оценивается.
Это означает, что вы можете использовать столько утверждений,
сколько захотите в процессе разработки, не влияя на производительность в производственной среде.

Отладка с утверждениями

Вы пишете утверждение, вызывая функцию assert (_: _: file: line :) из стандартной библиотеки Swift.
Вы передаете этой функции выражение, которое оценивается как истинное или ложное, и сообщение,
которое отображается, если результатом условия является ложное.
Например:

        let age = -3
        assert(age >= 0, "A person's age can't be less than zero.")
        // This assertion fails because -3 is not >= 0.

В этом примере выполнение кода продолжается, если age> = 0, имеет значение true,
то есть, если значение age неотрицательно. Если значение age отрицательно,
как в приведенном выше коде, то age> = 0 оценивается как false,
и утверждение не выполняется, и приложение завершается.

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

Если код уже проверяет условие, вы используете функцию assertionFailure (_: file: line :),
чтобы указать, что подтверждение не выполнено.
Например:

    if age > 10 {
        print("You can ride the roller-coaster or the ferris wheel.")
    } else if age >= 0 {
        print("You can ride the ferris wheel.")
    } else {
        assertionFailure("A person's age can't be less than zero.")
    }

Выполнение предварительных условий

Используйте предварительное условие всякий раз, когда условие потенциально может быть ложным,
но оно обязательно должно быть истинным, чтобы ваш код продолжал выполнение.
Например, используйте предварительное условие, чтобы проверить, что нижний индекс не вышел за пределы,
или чтобы убедиться, что функции передано допустимое значение.

Вы пишете предварительное условие, вызывая функцию предварительного условия (_: _: file: line :).
Вы передаете этой функции выражение, которое оценивается как истинное или ложное, и сообщение,
которое отображается, если результатом условия является ложное.
Например:

       // В реализации нижнего индекса ...
    precondition(index > 0, "Index must be greater than zero.")     

Вы также можете вызвать функцию preconditionFailure (_: file: line :),
чтобы указать, что произошел сбой — например, если был выбран the default case of a switch,
но все действительные входные данные должны были обрабатываться одним из переключателей. других cases.

Понравилась статья? Поделить с друзьями:
  • Swift язык руководство по
  • От чего метронидазол в таблетках 250 мг инструкция по применению
  • Инструкция по охране труда при производстве погрузочно разгрузочных работ 2022
  • Аудитор 10мг таблетки инструкция по применению
  • Мануал по тайпу манги