Swift 5.0. Что нового?

в 20:48, , рубрики: swift, swift 5, разработка под iOS, Разработка под MacOS

Swift 5 — долгожданный релиз, включающий в себя несколько десятков улучшений и исправлений. Но самой главной целью релиза Swift 5.0 было достижение ABI стабильности. В этой статье вы узнаете, что такое ABI и что стабильный ABI даст iOS/macOS разработчикам. А также проведём разбор нескольких новых фич Swift 5.

Swift 5.0. Что нового? - 1

ABI Stability

ABI — бинарный интерфейс приложения. ABI можно рассматривать как набор правил, позволяющих компоновщику объединять откомпилированные модули компонента.

Соответственно, в ABI описано следующее.

  1. То, как происходит вызов кода из разных модулей, в том числе системных.
  2. Формат передачи аргументов и получение возвращаемого значения из функций.
  3. Алгоритмы лэйаута данных в оперативной памяти.
  4. Управление памятью, ARC.
  5. Система типов, дженерики.

Swift 5 вместе со стабильным ABI предоставляет бинарную совместимость для приложений. Бинарная совместимость для iOS/macOS приложений означает, что скомпилированные приложения будут в рантайме совместимы с системными библиотеками, скомпилированными более ранними или более поздними версиями языка. Например, приложение, скомпилированное с Swift 5.0, будет совместимо с стандартными библиотеками, скомпилированными с Swift 5.1 или Swift 6.0.

Начиная с iOS 12.2 и macOS 10.14.4, операционные системы Apple будут содержать все необходимое для запуска свифтовых приложений. Это означает, что приложения, написанные на Swift 5 и более поздних версиях, не будут содержать рантайм и стандартную библиотеку языка. Поэтому приложения, написанные на Swift 5, станут весить примерно на 3-10 мегабайт меньше.

Важно отметить, что помимо ABI stability, есть еще Module stability. Если ABI stability позволяет совмещать разные версии свифта в рантайме, то Module stability отвечает за то, как компилируются бинарные фреймворки, написанные на разных версиях языка. Module stability появится в Swift 5.1. И тогда разработчики смогут распространять свои фреймворки не только с открытым исходным кодом, но и в скомпилированном виде.

Плюсы ABI стабильности.

  1. Приложения станут весить меньше.
  2. Ускорение запуска и производительности приложений.
  3. В теории, Apple может писать новые фреймворки полностью на Swift.

Минусы ABI стабильности.

Разработчикам придётся учитывать отсутствие в более старых версиях стандартной библиотеки какого-либо нового функционала. Например, если в iOS 13 будет встроен Swift 5.1 с какими-нибудь новыми классами/функциями в стандартной библиотеке, то при поддержке в приложении iOS 12.2 разработчики не смогут их использовать. (Нужно будет вставлять проверки #available(...) так же, как мы это делаем сейчас для Foundation, UIKit и других платформенных библиотек).

Тип Result в стандартной библиотеке

В стандартной библиотеке появился удобный способ передачи и обработки ошибок в асинхронном API. Также этот тип можно использовать в случае, если по каким-либо причинам нам не подходит стандартная обработка ошибок через try/catch.

Тип Result реализован через enum с двумя кейсами: success и failure:

public enum Result<Success, Failure> where Failure: Error {
    case success(Success)
    case failure(Failure)

    ...
}

На самом деле такой подход не нов для Swift-разработчиков. Ещё со времен первой версии Swift многие разработчики использовали похожий подход. Но теперь, когда в стандартной библиотеке появился свой Result, это упростит взаимодействие с кодом из внешних библиотек.

Пример использования в сервисе загрузок статей:

struct Article {
    let title: String
}

class ArticleService {

    func fetchArticle(id: Int64, completion: @escaping (Result<Article, Error>) -> Void) {
        // асинхронная загрузка статьи
        // ...
        completion(.success(Article(title: "Swift 5.0. Что нового?")))
    }

}

А вот пример обработки полученного результата. Так как Result — это всего лишь enum, то мы можем обработать все его состояния с помощью switch:

articleService.fetchArticle(id: 42) { result in
    switch result {
    case .success(let article):
        print("Success: (article)")
    case .failure(let error):
        print("Failure: (error)")
    }
}

Raw strings

В Swift 5 добавили так называемые raw strings, в которых кавычки и бэкслеш интерпретируются именно как символы, и для их использования в литерале не нужно использовать символ экранирования. Чтобы написать литерал такой строки, необходимо к двойным кавычкам по краям добавить символ #.

Пример использования кавычек:

// swift 4.2
print("Чтобы вывести строку в "кавычках" необходимо добавлять бэкслеш.")
print("Чтобы добавить переход на следующую строку, нужно использовать символы \n")

// swift 5
print(#"В "сырой" строке не нужны бэкслеши перед кавычками"#)
print(#"Чтобы добавить переход на следующую строку, нужно использовать символы n"#)

Эта фича особенно полезна при написании регулярных выражений:

// swift 4.2
let regex = "^\(*\d{3}\)*( |-)*\d{3}( |-)*\d{4}$"

// swift 5
let regex = #"^(*d{3})*( |-)*d{3}( |-)*d{4}$"#

Для интерполяции строк после бэкслеша надо добавлять символ #:

// swift 4.2
let string = "Строка с интерполяцией (variable)"

// swift 5
let string = #"Строка с интерполяцией #(variable)"#

Более подробно можете прочитать в этом предложении.

Обновленная интерполяция строк

С помощью интерполяции строк мы можем добавить в строковый литерал значение какой-либо переменной или результат выражения. Начиная с 5-ой версии языка, появилась возможность расширять то, как наши выражения добавляются в конечную строку.
В общем случае достаточно написать расширение к структуре DefaultStringInterpolation и добавить метод с названием appendInterpolation. Например, если мы хотим добавить в строку цену в отформатированном виде:

extension DefaultStringInterpolation {

    mutating func appendInterpolation(price: Decimal) {
        let formatter = NumberFormatter()
        formatter.numberStyle = .currency

        if let string = formatter.string(from: price as NSDecimalNumber) {
            appendLiteral(string)
        } else {
            appendLiteral(price.description)
        }
    }

}

print("Price of item: (price: 9.99)")
// Price of item: $9.99

Важно отметить то, что, по сути, конструкция (price: 9.99) в строке с помощью компилятора преобразовалась в вызов метода appendInterpolation(price: Decimal).
Также в методах appendInterpolation мы можем добавить неограниченное число аргументов, как именованных, так и не именованных, с дефолтными значениями или без них.

Более подробно можно прочитать в этом предложении.

Проверка кратности чисел

К числовым типам в стандартной библиотеке добавлен метод проверки кратности isMultiple(of:). Да, мы всё ещё можем использовать оператор взятия остатка от деления %. Но, кажется, isMultiple(of:) выглядит более наглядно.

let interger = 42
if interger.isMultiple(of: 3) {
    print("Кратно трем")
} else {
    print("Не кратно трем")
}

Метод compactMapValues в Dictionary

Метод compactMapValues позволяет преобразовать значения словаря, а также отфильтровать их, если само преобразование возвратило nil.

Например, маппинг строковых ключей в тип URL:

let dict = [
    "site": "https://www.site.ru/path/to/web/site/page",
    "other site": "invalid url"
]
let mappedDict: [String: URL] = dict.compactMapValues { URL(string: $0) }
print(mappedDict)
// ["site": https://www.site.ru/path/to/web/site/page]

Вторая пара «ключ/значение» была удалена после маппинга, так как строка не является валидным URL’ом.

Изменение поведения try?

В Swift 4.2 с помощью конструкции try? можно с лёгкостью получить опциональный тип с несколькими уровнями вложенности. В большинстве случаев это не то, чего ожидает разработчик. По этой причине в Swift 5 try? получил поведение, схожее c optional chaining. То есть при комбинации try? с optional chaining или optional casting результатом выражения будет опционал с одним уровнем вложенности.

Пример использования try? вместе с as?:

// Swift 4.2
let jsonDict = try? JSONSerialization.jsonObject(with: data, options: []) as? [String: Any]
// Тип jsonDict - [String: Any]??

// Swift 5
let jsonDict = try? JSONSerialization.jsonObject(with: data, options: []) as? [String: Any]
// Тип jsonDict - [String: Any]?

Пример использования try? вместе с методом опционального объекта:

// Swift 4.2
let article = try? storage?.getArticle()
// Тип article - Article??

// Распаковка
if let first = article, let second = first {
    first // тип Article?
    second // тип Article
}

// Или так
if case let value?? = article {
    value // тип Article
}

// Swift 5
let article = try? storage?.getArticle()
// Тип article - Article?

// Распаковка
if let value = article {
    value // тип Article
}

Более подробно можно прочитать в этом предложении.

Атрибут @dynamicCallable

Новый атрибут @dynamicCallable позволяет пометить тип как «вызываемый». Это означает, что мы сможем вызвать тип как обычный метод.
Если мы помечаем тип как @dynamicCallable, то должны реализовать один (или оба) из методов:

func dynamicallyCall(withArguments: <#Arguments#>) -> <#R1#>
func dynamicallyCall(withKeywordArguments: <#KeywordArguments#>) -> <#R2#>

Тип Arguments должен поддерживать протокол ExpressibleByArrayLiteral, тип KeywordArguments должен поддерживать протокол ExpressibleByDictionaryLiteral, а R1 и R2 могут быть любыми типами.

Например, структура Sum. При её вызове можно передать любое количество чисел и получить их сумму:

@dynamicCallable
struct Sum {
    func dynamicallyCall(withArguments args: [Int]) -> Int {
        return args.reduce(0, +)
    }
}

let sum = Sum()
let result = sum(1, 2, 3, 4)
print(result) // 10

По сути, компилятор преобразует sum(1, 2, 3, 4) в вызов sum.dynamicallyCall(withArguments: [1, 2, 3, 4]). Аналогично для метода dynamicallyCall(withKeywordArguments:).

Эта фича позволит добавить взаимодействие Swift кода с различными динамическим языками программирования, например, Python или JavaScript.

Более подробно можно прочитать в этом предложении.

Поддержка оператора «меньше» в директивах проверки версии компилятора и языка

Начиная с 5-ой версии Свифта можно использовать оператор «меньше» при проверках версии компилятора в коде:

// Swift 4.2
#if !swift(>=4.2)
// Этот код будет скомпилирован только для свифт 4.2 и меньше
#endif

// Swift 5
#if swift(<5)
// Этот код будет скомпилирован только для свифт 4.2 и меньше
#endif

Заключение

Это не все возможности и улучшения появившиеся в Swift 5. Всего было принято 28 предложений от комьюнити, также включающие в себя повышение производительности строк, улучшения Swift Package Manager и стандартной библиотеки. Полный список изменений и улучшений можно посмотреть в release notes.

Автор: aagarunov

Источник

* - обязательные к заполнению поля


https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js