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.

Documentation Index

This page describes the overall organization of documentation for the Swift toolchain.
It is divided into the following sections:

  • Tutorials
    gently guide you towards achieving introductory tasks,
    while assuming minimal background knowledge.
  • How-To Guides
    help you complete specific tasks in a step-by-step fashion.
  • Explanations
    discuss key subsystems and concepts, at a high level.
    They also provide background information and talk about design tradeoffs.
  • Reference Guides
    contain a thorough technical reference for complex topics.
    They assume some overall understanding of surrounding subsystems.
  • Recommended Practices
    suggests guidelines for writing code and diagnostics.
  • Project Information
    tracks continuous integration (CI), branching and release history.
  • Evolution Documents
    includes proposals and manifestos for changes to Swift.
  • The External Resources section provides links to
    valuable discussions about Swift development, in the form of talks
    and blog posts.
  • The Uncategorized section is for documentation which does
    not fit neatly into any of the above categories. We would like to minimize
    items in this section; avoid adding new documentation here.

Sometimes documentation is not enough.
Especially if you are a new contributor, you might run into roadblocks
which are not addressed by the existing documentation.
Or they are addressed somewhere but you cannot find the relevant bits.
If you are stuck, please use the development category on the Swift forums
to ask for help!

Lastly, note that we are slowly moving towards a more structured form of
documentation, inspired by the Django project [1]
[2]. Hence parts of this page are aspirational
and do not reflect how much of the existing documentation is written.
Pull requests to clean up the Uncategorized section,
or generally fill gaps in the documentation are very welcome.
If you would like to make major changes, such as adding entire new pieces of
documentation, please create a thread on the Swift forums under the
development category to discuss your proposed changes.

Tutorials

  • libFuzzerIntegration.md:
    Using libFuzzer to fuzz Swift code.

How-To Guides

  • FAQ.md:
    Answers «How do I do X?» for a variety of common tasks.
  • FirstPullRequest.md:
    Describes how to submit your first pull request. This is the place to start
    if you’re new to the project!
  • GettingStarted.md:
    Describes how to set up a working Swift development environment
    for Linux and macOS, and get an edit-build-test-debug loop going.
  • DebuggingTheCompiler.md:
    Describes a variety of techniques for debugging.
  • Building for Android:
    • Android.md:
      How to run some simple programs and the Swift test suite on an Android device.
    • AndroidBuild.md:
      How to build the Swift SDK for Android on Windows.
  • Building for Windows:
    • Windows.md:
      Overview on how to build Swift for Windows.
    • WindowsBuild.md:
      How to build Swift on Windows using Visual Studio.
    • WindowsCrossCompile.md:
      How to cross compile Swift for Windows on a non-Windows host OS.
  • Building for OpenBSD:
    • OpenBSD.md:
      Overview of specific steps for building on OpenBSD.
  • RunningIncludeWhatYouUse.md:
    Describes how to run include-what-you-use
    on the Swift project.

Explanations

  • WebAssembly.md:
    Explains some decisions that were made while implementing the WebAssembly target.

Compiler and Runtime Subsystems

  • Driver:
    • Driver.md:
      Provides an overview of the driver, compilation model, and the compiler’s
      command-line options. Useful for integration into build systems other than
      SwiftPM or Xcode.
    • DriverInternals.md:
      Provides a bird’s eye view of the driver’s implementation.

  • DependencyAnalysis.md:
    Describes different kinds of dependencies across files in the same module,
    important for understanding incremental builds.
  • DifferentiableProgrammingImplementation.md:
    Describes how automatic differentiation is implemented in the Swift compiler.
  • C and ObjC interoperability: Clang Importer and PrintAsClang
    • CToSwiftNameTranslation.md:
      Describes how C and ObjC entities are imported into Swift
      by the Clang Importer.
    • CToSwiftNameTranslation-OmitNeedlessWords.md:
      Describes how the «Omit Needless Words» algorithm works,
      making imported names more idiomatic.
  • Type-checking and inference:
    • TypeChecker.md:
      Provides an overview of how type-checking and inference work.
    • RequestEvaluator.md:
      Describes the request evaluator architecture, which is used for
      lazy type-checking and efficient caching.
    • Literals.md:
      Describes type-checking and inference specifically for literals.
  • Serialization.md:
    Gives an overview of the LLVM bitcode format used for swiftmodules.

    • StableBitcode.md:
      Describes how to maintain compatibility when changing the serialization
      format.
  • SIL and SIL Optimizations:
    • SILFunctionConventions.md:
    • SILMemoryAccess.md:
    • SILProgrammersManual.md:
      Provides an overview of the implementation of SIL in the compiler.
    • OptimizerDesign.md:
      Describes the design of the optimizer pipeline.
    • HighLevelSILOptimizations.rst:
      Describes how the optimizer understands the semantics of high-level
      operations on currency data types and
      optimizes accordingly.
      Includes a thorough discussion of the @_semantics attribute.
  • Runtime specifics:
    • Backtracing.rst:
      Describes Swift’s backtracing and crash catching support.

SourceKit subsystems

  • SwiftLocalRefactoring.md:
    Describes how refactorings work and how they can be tested.

Language subsystems

  • Swift’s Object Model
    • LogicalObjects.md:
      Describes the differences between logical and physical objects and
      introduces materialization and writeback.
    • MutationModel.rst: Outdated.
  • DocumentationComments.md:
    Describes the format of Swift’s documentation markup, including
    specially-recognized sections.

Stdlib Design

  • SequencesAndCollections.rst:
    Provides background on the design of different collection-related protocols.
  • StdlibRationales.rst:
    Provides rationale for common questions/complaints regarding design decisions
    in the Swift stdlib.

Reference Guides

  • DriverParseableOutput.md:
    Describes the output format of the driver’s -parseable-output flag,
    which is suitable for consumption by editors and IDEs.
  • ObjCInterop.md
    Documents how Swift interoperates with ObjC code and the ObjC runtime.
  • LibraryEvolution.rst:
    Specifies what changes can be made without breaking binary compatibility.
  • SIL.rst:
    Documents the Swift Intermediate Language (SIL).

    • TransparentAttr.md:
      Documents the semantics of the @_transparent attribute.
  • DynamicCasting.md:
    Behavior of the dynamic casting operators is, as?, and as!.
  • Runtime.md:
    Describes the ABI interface to the Swift runtime.

  • Lexicon.md:
    Canonical reference for terminology used throughout the project.
  • UnderscoredAttributes.md:
    Documents semantics for underscored (unstable) attributes.

ABI

  • CallConvSummary.rst:
    A concise summary of the calling conventions used for C/C++, Objective-C
    and Swift on Apple platforms. Contains references to source documents,
    where further detail is required.
  • CallingConvention.rst:
    Describes in detail the Swift calling convention.
  • GenericSignature.md:
    Describes what generic signatures are and how they are used in the ABI,
    including the algorithms for minimization and canonicalization.
  • KeyPaths.md:
    Describes the layout of key path objects (instantiated by the runtime,
    and therefore not strictly ABI).
    TODO: The layout of key path patterns (emitted by the compiler,
    to represent key path literals) isn’t documented yet.
  • Mangling.rst:
    Describes the stable mangling scheme, which produces unique symbols for
    ABI-public declarations.
  • TypeLayout.rst:
    Describes the algorithms/strategies for fragile struct and tuple layout;
    class layout; fragile enum layout; and existential container layout.
  • TypeMetadata.rst:
    Describes the fields, values, and layout of metadata records, which can be
    used (by reflection and debugger tools) to discover information about types.

Recommended Practices

Coding

  • AccessControlInStdlib.md:
    Describes the policy for access control modifiers and related naming
    conventions in the stdlib.

  • IndexInvalidation.md:
    Describes the expected behavior of indexing APIs exposed by the stdlib.
  • StdlibAPIGuidelines.rst:
    Provides guidelines for designing stdlib APIs.

  • StandardLibraryProgrammersManual.md:
    Provides guidelines for working code in the stdlib.
  • OptimizationTips.rst:
    Provides guidelines for writing high-performance Swift code.

Diagnostics

Project Information

  • Branches.md:
    Describes how different branches are setup and what the automerger does.
  • ContinuousIntegration.md:
    Describes the continuous integration setup, including the @swift_ci bot.

Evolution Documents

Manifestos

  • ABI Stability and Library Evolution
    • ABIStabilityManifesto.md:
      Describes the goals and design for ABI stability.
    • LibraryEvolutionManifesto.md:
      Describes the goals and design for Library Evolution.
  • BuildManifesto.md:
    Provides an outline for modularizing the build system for the Swift toolchain.
  • CppInteroperabilityManifesto.md:
    Describes the motivation and design for first-class Swift-C++ interoperability.
  • DifferentiableProgramming.md:
    Outlines a vision and design for first-class differentiable programming in Swift.
  • GenericsManifesto.md:
    Communicates a vision for making the generics system in Swift more powerful.
  • OwnershipManifesto.md:
    Provides a framework for understanding ownership in Swift,
    and highlights potential future directions for the language.
  • StringManifesto.md:
    Provides a long-term vision for the String type.

Proposals

Old proposals are present in the /docs/proposals directory.
More recent proposals are located in the apple/swift-evolution repository.
You can see the status of different proposals at
https://apple.github.io/swift-evolution/.

Surveys

  • CallingConvention.rst:
    This whitepaper discusses the Swift calling convention (high-level semantics;
    ownership transfer; physical representation; function signature lowering).
  • ErrorHandlingRationale.md:
    Surveys error-handling in a variety of languages, and describes the rationale
    behind the design of error handling in Swift.
  • WeakReferences.md:
    Discusses weak references, including the designs in different languages,
    and proposes changes to Swift (pre-1.0).

Archive

These documents are known to be out-of-date and are superseded by other
documentation, primarily The Swift Programming Language (TSPL).
They are preserved mostly for historical interest.

  • AccessControl.md
  • Arrays.md
  • Generics.rst
  • ErrorHandling.md
  • StringDesign.rst
  • TextFormatting.rst

External Resources

External resources are listed in ExternalResources.md.
These cover a variety of topics,
such as the design of different aspects of the Swift compiler and runtime
and contributing to the project more effectively.

Uncategorized

Needs refactoring

The documents in this section might be worth breaking up into several documents,
and linking one document from the other. Breaking up into components will
provide greater clarity to contributors wanting to add new documentation.

  • ARCOptimization.md:
    Covers how ARC optimization works, with several examples.
    TODO: Not clear if this is intended to be an explanation or a reference guide.
  • CompilerPerformance.md:
    Thoroughly discusses different ways of measuring compiler performance
    and common pitfalls.
    TODO: Consider breaking up into one high-level explanation explaining key
    concepts and individual how-to guides that can be expanded independently.
    Also, it’s not the right place to explain details of different compiler modes.
  • DevelopmentTips.md:
    Contains an assortment of tips for better productivity when working on the
    compiler.
    TODO: Might be worthwhile to make a dedicated how-to guide or explanation.
    It might also be valuable to introduce the tips in context, and have the
    explanation link to all the different tips.
  • Diagnostics.md:
    Describes how to write diagnostic messages and associated educational notes.
    TODO: Consider splitting into how-tos and recommended practices.
    For example, we could have a how-to guide on adding a new diagnostic,
    and have a recommended practices page which explains the writing style
    for diagnostics and educational notes.
  • HowSwiftImportsCAPIs.md:
    Contains a thorough description of the mapping between C/ObjC entities and
    Swift entities.
    TODO: Not clear if this is intended to be language documentation
    (for Swift developers), an explanation or a reference guide.
  • Modules.md: was written for Swift pre-1.0, but is still
    relevant and covers behavior that’s underspecified in either TSPL or the
    language reference.
  • OptimizerCountersAnalysis.md:
    TODO: Consider breaking up into a how-to guide
    on dumping and analyzing the counters
    and an explanation for the counter collection system.
  • Testing.md:
    TODO: Consider splitting into a how-to guide on writing a new test case
    and an explanation for how the compiler is tested.
  • SwiftIndent.md:
    TODO: Unclear if this is intended to be an explanation or a reference guide.
  • Random.md: Stub.

Archive

  • FailableInitializers.md:
    Superseded by documentation in The Swift Programming Language.
  • InitializerProblems.rst:
    Describes some complexities around initialization in Swift.
    TODO: It would be great to have an explanation, say InitializationModel.md,
    that covers the initialization model and new attributes like
    @_hasMissingDesignatedInitializers. Some of this is covered in
    TSPL’s initialization section but that doesn’t include newly added
    attributes.
  • Swift3Compatibility.md:
    Discusses the Swift 3 -> Swift 4 migration.
  • StoredAndComputedVariables.rst: for Swift pre-1.0.

Последнее обновление: 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

Понравилась статья? Поделить с друзьями:
  • Starline a63 v2 eco инструкция по применению
  • Фезам таблетки инструкция по применению цена отзывы врачей
  • Руководству по парашютной подготовке авиации досааф россии рпп 2010
  • Глазные капли офтан катахром инструкция для чего
  • Мансарда каркасного дома своими руками пошаговая инструкция с фото