Лого КРОК
0
Обложка: Особенности языка программирования Swift

Особенности языка программирования Swift

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

Опционалы

По сути, это контейнеры, которые либо имеют значение, либо нет — enum с одним из двух значений: nil или любое другое. Эти состояния обозначаются как Nope и Some соответственно. Соответственно, nil — это валидное значение, а не костыль, как в Objective-C, и при создании опциональной переменной присваивается по умолчанию, если значение не указано. Объявляются они так:

var number: Int?

А внутри выглядят таким образом (ссылка на исходный код опционала в открытом репозитории свифта: https://github.com/apple/swift/blob/main/stdlib/public/core/Optional.swift):

public enum Optional : ExpressibleByNilLiteral {
    case none
    case some(Wrapped)
    public init(_ some: Wrapped)
        ...
    /// Creates an instance initialized with `nil`.
    /// Do not call this initializer directly. It is used by the compiler when you initialize an `Optional` instance with a `nil` literal.
    public init(nilLiteral: ())
        ...
}

Благодаря опционалам у языка Swift есть несколько крутых фишек, которых нет в том же Objective-C:

  • Все переменные в любой момент в runtime всегда определены и инициализированы. Просто если вы не пишете прямой инициализатор, переменная (если это Optional тип) определяется как nil, а если не Optional, компилятор не позволит вам собрать приложение, ведь вы забыли инициализировать это и это. Так происходит потому, что nil — это особый вид значения, и вы можете сказать компилятору: «это пустая переменная, поэтому проинициализируй её сам».
  • Swift почти никогда не выдаёт nullpointerexception. Единственный случай, когда можно нарваться на подобную ошибку — попытаться force unwrap опционал, в котором лежит nil.

Оператор guard

Оператор guard работает примерно так же, как if, но с одним отличием: он запускается, когда условие НЕ выполняется.

Guard-конструкции — это спасение. Они позволяют лаконично описать, что делать, если найдена ошибка — и просто забыть, что эта самая ошибка здесь может быть. Как это выглядит:

APIService.getMyData { (data, error) in
guard error == nil else {
Logger.error(error!.description)
return
}
completion(data)
}

Здесь мы пытаемся запросить у API свои данные. В колбеке этого запроса мы проверяем, произошла ли ошибка в процессе: если да, то делаем return, если нет, то передаём полученную data в комплишен дальше.

Wildcard

Когда функция состоит из одного входного параметра, очень долго и лениво писать его название, чтобы передать туда переменную. Поэтому можно поставить нижнее подчёркивание перед параметрами, которые вы хотите опускать при вызове функции. Это называется «Wildcard».

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

func displayError(error: Error) {
errorLabel.text = error.description
}

Если бы в параметрах функции не было вайлдкарда, функцию с переменной myError, внутри которой лежит какая-то конкретная ошибка, пришлось бы вызывать таким образом:

displayError(error: myError)

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

func displayError(_ error: Error) {
errorLabel.text = error.description
}

И позже вызвать функцию:

displayError(myerror)

Ещё к нижнему подчёркиванию можно обратиться, переприсвоить его или передать куда-нибудь.

Константы

Ещё одной особенностью языка Swift является то, что в нём не предусмотрен атрибут константы, и ключевого слова const просто не существует. Вместо этого используется let:

let name: String = "Swift"
print(name)

let означает, что мы можем присвоить значение переменной только один раз. И потом либо что-то поменять внутри неё, если это ссылочный тип, либо передать что-то из неё в другое место. А при попытке что-то переприсвоить, компилятор скажет: «так нельзя, я это собирать не буду».

let часто используется для полей класса и параметров. Если переменная нужна в изменяемом виде, — например, это счётчик функции, — то она объявляется через var. Это иногда путает новичков — особенно тех, кто приходит из JavaScript, потому что там между var и let немного другие отношения.

Функции высшего порядка

Функции высшего порядка похожи на лямбда-выражения в Kotlin (но, строго говоря, ими не являются) и что-то делают для каждого элемента коллекции, к которой применяются.

Например, метод map(_:) используется для преобразования последовательностей (тип Sequence). Он очень лаконичный, можно быстро отсортировать коллекцию или посчитать сумму чисел Фибоначчи:

let fibonacci = sequence(first: (0, 1), next: { ($1, $0 + $1) })
.prefix(20).map{$0.0}
.filter {$0 % 2 == 0 && $0 < 4000}
print (fibonacci)
// [0, 2, 8, 34, 144, 610, 2584]

Читабельно ли это? Не очень. У неподготовленного человека или того, кто пишет в соответствии с императивным подходом, это может вызвать сложности.

Другой метод — reduce(_:_:) — уменьшает все элементы до одного значения:

let NumberSum = numbers.reduce(0) { $0 + $1}
print(NumberSum)

Иногда reduce(_:_:) используется для очень сложных вещей — например посчитать пересечения множеств. Это удобная штука для мелких задач — посчитать сумму массива, найти подмассив, отсортировать что-то.

Атрибуты доступа

В языке программирования Swift «все знают обо всём»: можно из любого файла обратиться к любому классу и любой зависимости, если они открыты. Единственный способ с этим что-то сделать, — например, вы не хотите, чтобы к  вашим классам и модулям обращались приложения извне, — это настроить атрибуты доступа. У Swift их шесть:

  • public
  • private
  • protected
  • open
  • closed
  • fileprivate

open и closed — это про наследование. Возможно ли наследоваться от этого класса и можно ли оверрайдить внутри него какие-то поля или методы. Если файл «открытый», от него может наследоваться кто угодно и оверрайдить внутри него что угодно. Если «закрытый», все знают о его существовании и могут делать то, что вы им разрешите. Но наследоваться от него и оверрайдить в нём что-то — нет.

fileprivate — очень интересная штука. Представим, что у вас есть класс, объявленный в Файле 1. В Файле 2 вы пишете для него расширения: добавляете поля, методы и так далее. И один из методов помечаете атрибутом fileprivate. Теперь он будет доступен только в Файле 2. А в других файлах, в том числе в Файле 1, к нему обратиться не получится.

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