Swift 4.1 / 1.1. Что осталось за бортом



В наши дни люди всему знают цену, но ничего не умеют ценить.

Оскар Уайльд

В этом уроке


О чем эта глава?

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

Также здесь мы распишем несколько удобных функций Xcode, которые мы не рассмотрели в прошлом.


Цикл for-in-where

В Swift существует ещё один цикл, который мы не изучали ранее, он позволяет выполнить одновременно инструкции for и if:

var sum = 0
for i in 1...100 where i % 2 != 0 {
    sum += i
}
sum //2500

Он позволяет выполнить цикл для тех элементов, которые удовлетворяют условию в блоке where. В данном случае мы найдём сумму только для нечётных чисел от 0 до 100.

Эту операцию эффективнее выполнить с помощью reduce


Цикл for-case

Ещё один цикл с проверкой совпадения шаблону for-case.

Создадим массив из трёх кортежей с именами и датами рождения:

let team = [
    ("Slava", 1995),
    ("Valery", 1995),
    ("Asel", 1992)
]

Выведем имена только тех участников, чей год рождения - 1995:

for case let (name, 1995) in team {
    print(name)
}

//Slava
//Valery

Здесь работают любые модели совпадения паттернов, как и в инструкции switch-case.

И здесь traverse-функции filter, map помогли бы нам и это был бы лучший вид.

Данный подход можно использовать, чтобы обойти коллекцию из опциональных объектов и отсеять nil-значения:

let myNumbers = [1, 2, 3, nil, 5]

for case let .some(number) in myNumbers {
    print(number)
}

//1
//2
//3
//5

Это основано на том, что любой опционал построен в качестве шаблонного перечисления.

map, filter


if-case

if-case работает как обычный if, но использует совпадение по паттерну.

Так можно переписать предыдущий код с ещё большим оверхедом, вложив проверку внутрь цикла:

for people in team {
    if case let (name, 1995) = people {
        print(name)
    }
}
//Slava
//Valery

Switch-case-is

Создайте класс Person с единственным свойством name, а также соответствующим инициализатором:

class Person {
    let name: String

    init(name: String) {
        self.name = name
    }
}

Теперь создадим два пустых подкласса:

final class SwiftCoder: Person {

}

final class JavaCoder: Person {

}

Мы объявили их как финальные, так как нам не требуется улучшать их в данный момент. Это очень хороший подход объявлять классы финальными, даж если в последующем Вы захотите продолжить их родословную - снять квалификатор final дело одной секунды, а его наличие ускорит работу компилятора над данными классами.

Теперь создайте массив с объектами каждого из этих классов и произвольными именами.

let persons = [
    Person(name: "Slava"),
    SwiftCoder(name: "John"),
    JavaCoder(name: "Maria")
]

Если Вы нажмёте с зажатым ALT левой кнопкой по названию массива persons, то будет выведена панель с указанием типа [Person]:

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

Теперь воспользуемся конструкцией case-is для проверки типа и вывода нужных данных.

for person in persons {
    switch person {
    case is SwiftCoder:
        print("\(person.name) is \(type(of: person))")
    case is JavaCoder:
        print("\(person.name) is \(type(of: person))")
    case is Person:
        print("\(person.name) is \(type(of: person))")
    default:
        print("fuck")
    }
}

//Slava is Person
//John is SwiftCoder
//Maria is JavaCoder

Для получения класса мы использовали type(of:). Он возвращает реальный динамический тип объекта. Причём возвращается не строка, а сам класс.

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

print(type(of: SwiftCoder(name: "John") as Person)) //SwiftCoder