Swift 4.0 в Xcode 9 уже доступен, и он принес нам несколько приятных изменений. Конечно, мы не получили ни такой кардинальной переработки синтаксиса, как в прошлом году для Swift 3, ни таких захватывающих возможностей, как в Swift 2, но есть несколько замечательных добавлений, которые могут существенно улучшить ваш код. Давайте посмотрим на них!
Сейчас очень много материала на тему «Что нового в Swift 4». Я взяла за основу знаменитую статью Ole Begemamm «Playground: What’s new in Swift 4.», дополнила ее очень интересными примерами из других статей, предоставив в ваше распоряжение Playground с примерами на русском языке.
Encoding and Decoding
Парсинг JSON — одна из наиболее обсуждаемых тем в Swift сообществе. Это просто замечательно, что кто-то взял на себя труд написать предложения SE-0166 и SE-0167 и протолкнуть идею обновления в Foundation фреймворке APIs архивирования и сериализации. Теперь в Swift 4 больше нет необходимости в парсинге (decode) или архивировании (encode) вашего класса class, структуры struct или перечисления enum вручную. За вас это будет делать компилятор.
В Swift 4 добавлены новые протоколы Encodable и Decodable, и вы можете заставить ваши классы, структуры и перечисления «подтверждать» их простым добавлением в список наследования ключевого слова Codable (который является алиасом для Decodable & Encodable). Затем вы можете использовать кодер JSONEncoder для архивирования (encode) экземпляра вашего типа:
Создаем экземпляр кодера jsonEncoder для преобразования структуры Conference в строчное представление JSON объекта jsonString. Как видим, формат представления даты, выполненный кодером jsonEncoder по умолчанию, оказался неудовлетворительным. Но вы удивитесь, как просто настроить нужный вам формат JSON объекта. Например, для даты вы можете определить стратегию кодирования даты dataEncodingStrategy следующим образом:
Обратный процесс парсинга (decode) работает абсолютно аналогичным образом благодаря классу JSONDecoder:
Мы передаем методу decode декодера jsonDecoder тип объекта ( в нашем случае Conference), давая ему понять, какой объект мы ожидаем получить из JSON данных. Если все происходит успешно, то мы получим готовый к использованию экземпляр объекта. который является нашей Моделью.
Конечно, новый API обладает гораздо большой мощностью, которая немного приоткрыта в посте «Парсим JSON в Swift 4». Там показано, как в случае необходимости вы очень просто можете создать свои собственные encode и decode благодаря простому и очень гибкому API классов JSONEncode и JSONDecode.
Код находится на Playground на странице «Codable».
Strings.
Прощайте string.characters.
Благодаря предложению SE-0163 в Swift 4 String опять (после того, как убрали в Swift 2) подтверждает протокол Collection. Теперь строку можно рассматривать как коллекцию символов и выполнять с ними всевозможные операции, присущие Collection, такие, как reversed(), filter(), dropFirst() и т.д.
В Swift 2 & 3 также можно было делать эти операции, но со свойством characters, что приводило к не очень хорошей читаемости кода.
Новый тип данных Substring.
Swift 4 представляет новый способ взаимодействия с подстроками, используя для этого совершенно новый тип Substring, который появляется как результат операций, разделяющих строку на части или фрагменты, таких как split (separator:) :
или prefix (_:) :
или извлечение слайса строки substring путем задания диапазонов через сабскрипт (subscripting):
На первый взгляд может показаться неуклюжим использование двух различных типов данных для хранения строк, но, как оказалось, это дает нам большое преимущество в плане предсказуемости памяти, занимаемой подстроками.
Для того, чтобы избежать создания множества избыточных копий строк, Swift использует механизм “copy on write” для того, чтобы выполнять копирование, когда это действительно необходимо. Это означает, что подстроки Substring часто разделяют то же самое пространство в памяти, что и их родительская строка String или подстрока Substring.
Подстрока Substring «держит» полную строку String, из которой она создана. Это может приводить к случайному очень большому увеличению использования «памяти» при передачи казалось бы маленьких подстрок Substring, которые держат большую строку String, в другие API. По этой причине большинство функций, которые получают в качестве аргумента строку String, должны принимать только строку String вам не следует делать эти функции generic и принимать любое значение, «подтверждающее» протокол StringProtocol.
Давая нам тип Substring вместо полноценного типа String, Swift фактически «заставляет» нас выполнять «копирование» (copy) ЯВНО, если это необходимо, освобождая тем самым «память» родительской строки. Это делается простой инициализацией строки String из подстроки Substring:
Подстроки Substrings не подходят для долговременного хранения из-за использования памяти оригинальной строки, так как «держат» в памяти полную строку. Если вам нужны Sustring для долговременного хранения, а не для выяснения каких-то условий выполнения куска кода, то лучше их преобразовать в String указанным выше способом.
Не все довольны таким решением Apple, некоторые считают его ошибочным.
Unicode 9
Swift 3 неправильно определял количество символов в строке, составленной из кластера эмодзи. Например, число символов в эмодзи «семья» «????».count Swift 3 определяет как 4, хотя должно быть 1. Swift 4 исправил эту ошибку:
Выделить слившиеся в одну графему Unicode символы можно с помощью следующего кода:
Теперь во всех случаях, представленных ниже, подсчет символов в Swift 4 производится правильно (чего не было в Swift 3):
Теперь у вас есть прямой доступ к свойству unicodeScalars у Character, без предварительной конвертации в String (SE-0178):
Многострочные строковые литералы
Иногда вам необходимо иметь в своем коде длинную, многострочную строку. Это может быть HTML шаблон, blob XML или длинное сообщение пользователю. В любом случае задание такой строки в Swift 3 выполняется «некрасивой» одной строкой, в которую добавляют «\n» символы для разделения ее на строки:
Swift 4 разрешил эту проблему с помощью многострочных литералов. Для задания многострочного литерала нужно использовать три двойных кавычки «»» вначале и в конце:
Нельзя разместить строку с тремя двойными кавычками в одной строке. В Swift 4 мы получим ошибку:
Исправляется это таким образом:
Отступ содержимого строки зависит от отступа от закрывающейся метки «»»:
Код находится на Playground на странице «Strings».
Односторонние диапазоны
Предложение [SE-0172] представляет новый протокол RangeExpression и множество prefix/postfix операторов для формирования односторонних диапазонов, то есть диапазонов, где либо верхняя, либо нижняя граница не определены. В односторонних диапазонах «пустая» сторона означает минимальное или максимальное значение в зависимости от контекста.
В случае сабскрипта (subscripting) в контейнерах это означает, что вы можете не указывать такие вещи, как string.endIndex или array.count.
Например, если вы хотите разделить массив на две половинки:
если вы хотите получить подстроку до какого-то индекса:
или получить подстроку после какого-то индекса:
Для односторонних диапазонов до какой-то заданной величины вы можете использовать ..< для исключающего диапазона или … для включающего диапазона точно также, как и для двусторонних диапазонов. Для одностороннего диапазона, начинающегося с заданного значения, разрешен только включающий диапазон … , так как нет никакой разницы между ... и ..<.
Вы можете использовать односторонний диапазон для конструирования бесконечной последовательности, то есть как более гибкое замещение enumerated(), если вы не хотите начинать с нуля:
Если вы используете односторонние диапазоны для санскрита (subscripting) внутри коллекции Collection, то startIndex или endIndex “заполнят” соответственно пропущенные нижнюю или верхнюю границы.
Односторонние диапазоны могут использоваться в конструировании шаблонов соответствия, то есть в выражениях case в предложении switch. Хотя, заметьте, что компилятор не может (пока?) определить, является ли оператор switch исчерпывающим, то есть покрывает все случаи.
Код находится на Playground на странице «One-sided ranges».
Умные key paths
Одна из главных особенностей Swift 4 — это новая модель KeyPaths, описанная в [SE-0161]. В отличии от строковых KeyPaths в Objective-C, в Swift 4 ключи пути строго типизированные.
Мы можем использовать указатель на функцию, не вовлекая ее саму в вычисления и не получая результата, потому что функции в Swift — это замыкания (closures). Точно также мы хотели бы поступать с свойствами объектов (а более широко — со свойствами типа). Это осуществляется с помощью динамических ссылок на свойства, так называемых ключей пути ( KeyPaths). Короче говоря, KeyPaths — это type-безопасный способ отделить ссылку на свойство типа от получения его значения. Мы могли уже это делать в Swift 3 с функциями, но до появления версии Swift 4 мы не могли это делать со свойствами, если только не «заворачивать » их в замыкание (closure) :
Или не использовать старый небезопасный #keyPath() синтаксис, который, по существу, формировал Objective-C строковые KeyPaths и годился только для классов:
В Swift 3 использование KeyPaths было ограничено NSObjects, реально они не работали со структурами. Именно это было основной мотивацией изменения этого API в Swift 4, которое изложено в предложении SE-0161 и которое пока не полностью реализовано в версии Swift 4.0.
Но в любом случае теперь в Swift 4 есть своя «родная» Модель для KeyPaths и возможность ссылаться с помощью KeyPaths на свойства любых типов для получения и установки их значений.
KeyPaths формируются, начиная с корневого типа, и затем мы можем идти вниз по иерархии до любой комбинации имен свойств. Вы формируете KeyPath, начиная с обратного слеша: \Book.title. Каждый тип автоматически снабжается сабскритом [keyPath: …] для получения и установки значения для определенного keyPath. KeyPaths могут опускаться вниз по иерархии до любого уровня. Они также работают и для вычисляемых (computed) свойств, как в нашем случае для свойства primaryAuthor.
Для KeyPaths так же можно использовать сабскрипты subscripts. Довольно удобный способ для работы с коллекциями, массивами или словарями. Эта функциональность пока еще не реализована в Swift 4.0, возможно мы увидим их в версии Swift 4.1.
KeyPaths являются объектами, которые можно хранить и ими можно манипулировать. Например, вы можете добавить дополнительный сегмент к KeyPath, чтобы спуститься еще дальше по иерархии.
Но самое замечательное состоит в том, что KVO API в Foundation существенно переделан и использует все преимущества новых type-безопасных KeyPaths. Давно известно, что KVO API был одним из самых смутных и непонятных API в Cocoa. Поэтому эти изменения столь долгожданны. Не только из-за того, что они стали type-безопасных, но из-за того, что прекратился этот ад.
Теперь в Swift 4 все стало просто:
Заметьте, что KVO зависит от Objective-C runtime. Оно работает только с subclasses NSObject, и любые наблюдаемые (observable) свойства должны декларироваться как @objc dynamic, или весь класс должен декларироваться с помощью нового ключевой слова @objcMembers. В связи с этим я рекомендую взглянуть на это предложение.
И прекрасной «вишенкой на торт» в новом KVO API является отсутствие необходимости удалять наблюдателя (observer) в методе deinit, и я знаю, что я — не единственная, кто был наказан за это в прошлом. Теперь мы можем сами управлять «жизненным циклом» наблюдателя observation:
observation.invalidate ()
Код находится на Playground на странице «Key paths».
На тему KeyPaths я настоятельно рекомендую посмотреть WWDC 2017 видео «What’s new in Foundation«, а также статьи:
Key Value Observation in iOS 11
Swift 4 KeyPaths and You
What’s new in Swift 4
Улучшения в Dictionary и Set.
В Swift 4 словари Dictionaries и множества Sets получили ряд новых методов и инициализаторов, которые позволяют выполнять довольно распространненые задания значительно легче, чем прежде. Такие операции, как группировка, фильтрация и преобразование значений values могут теперь выполняться за один шаг, позволяя получить более выразительный и эффективный код.
Мы будем исследовать эти новые преобразования на примере данных о продуктах в супермаркете. Структура GroceryItem имеет в качестве свойств имя продукта name и отдел department, в котором он представлен:
Группировка значений values по ключу
Новый группирующий инициализатор позволяет нам создать словарь Dictionary из последовательностизначений values, сгруппированных по ключам, которые вычисляются из самих этих значений values. Давайте построим словарь для продуктов, сгруппированных по отделам супермаркета, в котором они находятся, и используем новый инициализатор для формирования этого словаря:
или словарь для продуктов, сгруппированных по первой букве в названии продукта:
Преобразование значений values в словаре Dictionary
Вы можете преобразовывать значения values словаря Dictionary, сохраняя при этом те же самые ключи keys, если будете использовать специальный метод mapValues(_:). Этот код преобразует массив продуктов, находящихся в отделе супермаркета, в их количество, и, таким образом, мы получаем справочную таблицу, содержащую количество продуктов в каждом отделе:
Или справочную таблицу, содержащую список продуктов по названиям, представленных заглавными буквами, в каждом отделе:
Из-за того, что словарь сохраняет те же самые ключи keys, просто для других значений values, то используется то же самое внутреннее взаиморасположение (internal layout), что и у оригинального словря и нет необходимости в перерасчете hash values. Это делает вызов mapValues(_:) более быстрым, чем создание словаря «с нуля».
Создание словаря Dictionary из пар key- value
Теперь вы можете создавать словари Dictionaries из последовательностей (sequences) пар key-value, используя два различных инициализатора: один — для уникальных ключей keys, другой — когда ключи keys повторяются.
Если вы начинаете с последовательности ключей keys и последовательности значений values, вы можете комбинировать их в единую последовательность пар с помощью функции zip(_: _»:). Например, следующий код создает последовательность кортежей с именами name продуктов и самими продуктами:
Каждый элемент массива zippedNames является кортежем (String, GroceryItem). Первым элементом этого массива является ( «Apple»,?
).
Так как каждый продукт имеет уникальное имя, то следующий код успешно создаст словарь, который будет использовать имена в качестве ключей, а сами продукты в качестве значений:
Используйте инициализатор Dictionary(uniqueKeysAndValues:) только, если вы уверены, что ваши даннын имеют уникальные ключи keys. Любые дублирующие ключи keys в последовательности приведут к ошибке на этапе runtime.
Если ваши данные имеют (или могут иметь) повторяющие ключи keys, используйте новый «сливающий» инициализатор Dictionary(_:uniquingKeysWith:). Этот инициализатор берет последовательность пар key-value наряду с замыканием , которое вызывается, когда ключ key повторяется. «Уникальное» замыкание берет в качестве аргументом первое и второе значения для одного и того же ключа key, и может вернуть существующее значение, новое значение или любую их комбинацию в зависимости от вашего решения.
Например, следующий код преобразует массив кортежей (String, String) в словарь с применением функции Dictionary(_:uniquingKeysWith:). Заметьте, что «dog» является ключом key в двух парах key-value.
Когда появляется вторая пара key-value с ключом «dog», то вызывается «уникальное» замыкание со старым old и новым new значениями values ("?"
и "?"
). Из-за того, что замыкание всегда возвращает второй аргумент, то "?"
будет значением value для ключа «dog».
Выбор нужных элементов Dictionary
Словари Dictionaries теперь имеют метод filter(_:), который возвращает словарь, а не массив пар key-value, как это было в более ранних версиях Swift. Методу filter(_:) передается замыкание, которое берет key-value пару в качестве его аргументов и возвращает true, если эта key-value пара должна войти в результирующий словарь Dictionary.
Этот код вызывает функцию isFirstLetter (_:in:) для каждого продукта item, сохраняя только те элементы словаря, значения которых имеют имена, начинающиеся с буквы «B»:
Следующий код выбирает только те отделы, у которых количество разнообразных продуктов превышает 2:
В filter(_:) можно играть с ключами.
В следующем примере выбираем только те ключи, которые начинаются с буквы «S»:
Выбираем только четные ключи:
Subscript со значением по умолчанию
Subscripts для словаря Dictionary в Swift 3 возвращал значение value, соответствующее указанному ключу key как Optional, так что обычно вам приходится использовать оператор ?? для установки значения по умолчанию для несуществующих ключей :
Запрошенное время года «Winter» не находится в словаре seasons, так что вы устанавливаете для него значение температуры 0, как значение по умолчанию.
В Swift 4 у словарей Dictionaries теперь есть второй новый subscript для ключей keys, который позволяет определить специальные значения для отсутствующих в словаре ключей keys. Благодаря этому новому subscript теперь нет необходимости в использовании оператора ?? для получения НЕ Optional значения:
Это особенно полезно, если вы хотите накапливать значение value через subscript:
Вначале мы имеем пустой словарь frequencies. Для каждого символа с в текстовой строке source мы ищем символ с в качестве ключа в словаре frequencies, получаем значение для этого ключа, увеличиваем значение на единицу и запоминаем новое значение в словаре frequencies с ключом с. Естественно, если мы впервые встречаем символ с в строке source, то получаем значение по умолчанию равное 0, добавляем 1 и запоминаем вновь полученную пару {с :1} в словаре frequencies.
Сливаем два словаря Dictionaries в один
В дополнение к более легкому способу инкрементных изменений в словаре Dictionary, мы получили более простой способ проведения изменений «оптом», когда происходит добавление одного словаря целиком в другой:
Перед вами два словаря, которые отражают набор продуктов в двух тележках cart и otherCart. Вы можете использовать изменяющий (mutating) метод merge(_:uniquingKeysWith:) для объединения продуктов, находящихся в двух тележках в одну. «Уникальное» замыкание unuquingKeysWith, которое передаются этому методу, работает точно также, как и инициализатор Dictionary(_:uniquingKeysWith:) : он вызывается тогда, когда появляются два значения values с одним и тем же ключом key; в этом случае возвращается одно значение value или другое значение value или их комбинация.
Для создания нового словаря с «объединенным» содержимым вместо «слияния» «по месту», следует использовать «неизменяющий» метод merging(_:uniquingKeysWith:).
Фильтр filter для Set также возвращает теперь Set, а не Array.
Код находится на Playground на странице «Dictionary and Set enhancements».
Вы можете найти больше информации об этих новых возможностях в документации Dictionary и Set, или прочитать предложения по изменению в Swift custom keys
and values
collections и other dictionary and set enhancements.
Dictionary and Set Improvements in Swift 4.0
What’s new in Swift 4 by example
Композиция классов classes и протоколов potocols
Одной из «пропущенных» возможностей типизованной системы Swift была невозможность ограничивать класс class определенными протоколами protocol. Это исправлено в Swift 4—теперь вы можете определить тип объекта и протоколы, которые он должен «подтверждать», И все это благодаря предложению SE-0156. Вы, например, можете в качестве аргумента функции использовать UIView, которое подтверждает протокол Reloadable, применив следующий синтаксис:
Еще один пример. Мы хотим декларировать свойство как UIView, подтверждающее протокол HeaderView :
Заставляем метку UILabel подтвердить протокол HeaderView:
Теперь любую метку UILabel мы можем использовать для ParallaxView:
Мы не можем передать просто UIView, которое не подтверждает протокол HeaderView:
У нас возникает ошибка, сообщающая о том, что аргумент типа UIView() не соответствует ожидаемому типу UIView & HeaderView.
Код находится на Playground на странице «Composing classes and protocols».
Generic Subscripts
Swift поддерживает generic методы с самого начала, но во всех предществующих Swift 4 версиях, он не поддерживал generic subscripts. Вы могли бы «перегружать» subscripts реализацией более одного с различными типами, но вы никогда не могли использовать generics. Теперь, благодаря предложению [SE-0148], вы можете это делать!!!
Канонический пример использования generic subscripts — это тип, который представляет JSON данные: вы можете определить generic subscript для того, чтобы контекст вызова определил ожидаемый возвращаемый тип.
Другой пример: subscript Collection, который берет generic последовательность индексов и возвращает массив значений для этих индексов indices:
Код находится на Playground на странице «Generic subscripts».
Ограничения (constraints) для associatedtype
[SE-0142]: теперь associatedtypes в протоколах protocols могут быть ограничены с помощью предложений where. Это казалось бы небольшое изменение делает систему типизации Swift более выразительной и содействует значительному упрощению стандартной библиотеки. В частности, в Swift 4 работать с последовательностью Sequence и коллекцией Collection стало намного проще и интуитивнее.
Теперь последовательность Sequence имеет свой собственный associatedtype —Element. Это стало возможным благодаря новым возможностям generics, так как теперь в типизованной системе Swift 4 можно написать следующее выражение associatedtype Element where Element == Iterator.Element.
Везде, где раньше вы писали Iterator.Element в Swift 3, вы можете теперь просто написать Element:
Другой пример. В Swift 3 нижеприведенное расширение extension требовало больше ограничений из-за того, что система типизации Swift 3 не могла выразить идею, что associatedtype Indices для Collection представляет собой Collection.Index:
Ограничения типа реально очень полезны при определении APIs с применением протоколов protocols.
Допустим, нам необходимо определить «разделяемый» API для произвольных объектов. Этот API будет ответсвенным за управление различными типами Моделей в вашем приложении. Для этого определим протокол protocol с именем ModelManager, у которого есть associatedtype Model, like this:
Давайте сделаем еще один шаг вперед и определим API для запроса у менеджера модели ModelManager коллекции моделей [Model]. Мы могли бы использовать для запроса query конкретный тип, например, String. В результате мы получили бы массив [Model] в качестве результата:
Этот код работает, но он недостаточно гибкий и, выражаясь языком Swift разработчиков слишком «Stringly» типизован. Вместо этого давайте использовать generic ограничения (constraints), чтобы включить в работу систему типизации Swift 4 и добиться большей гибкости, получив строго типизованные запросы queries, выполняемые нашем менеджером модели ModelManager.
Для того, чтобы это сделать, нам нужно добавить еще два новых associatedtypes в наш протокол ModelManager: один — для запроса Query тип, который может быть каким угодно, например, enum, и который выбирает реализующий протокол. Затем мы собираемся добавить тип Collection, который мы ограничим так, что тип элемента возвращаемой из запроса коллекции Collection соответствует типу модели Model. Конечный результат выглядит так:
С учетом вышеприведенного протокола, мы свободно можем реализовать любые менеджеры Модели с одним и тем же API, используя при этом типы и коллекции, которые нас устраивают.
Например, при реализации ModelManager для модели User, мы можем использовать массив Array в качестве типа Collection и перечисление enum для запроса Query, который позволит нам выбирать пользователей по имени name и возрастному диапазону ageRange:
Для других Моделей более подходящим типом для коллекции Collection может оказаться словарь Dictionary. Вот другой пример менеджера отслеживает фильмы на основе их жанра genre и позволяет делать запросы по присутствию определенного текста в названии фильма или по диапазону даты создания фильма:
Имея в распоряжении массив фильмов movies, мы можем делать запросы, содержащие, например, текст «Little» в заголовке фильма или диапазон года выпуска фильма Range(1993…2003) и получать перечень подходящих фильмов в виде словаря с ключом Genre (жанр):
Код находится на Playground на странице «Associated type constraints.
Ссылки:
Using generic type constraints in Swift 4
Метод MutableCollection.swapAt
[SE-0173] представляет новый метод для того, чтобы поменять местами два элемента в коллекции. В противоположность существующей функции swap(_:_:), метод swapAt(_:_:) берет индексы элементов, которые должны поменяться местами, а не сами элементы (с помощью inout аргументов).
Причина появления этого дополнительного метода swapAt(_:_:) состоит в том, что функция swap с друмя inout аргументами оказалась несовместимой с новыми исключающими правилами доступа к памяти, которые предложены в [SE-0176]. Существующая функция swap(_:_:) не будет больше работать с двуми элементами, которые меняются местами в одной и той же коллекции.
Код находится на Playground на странице «swapAt».
Ограничение автоматического экспонирования классов, методов и свойств в Objective-C с помощью @objc
Swift 4 минимизирует автоматическое экспонирование классов, методов и свойств с помощь @objc только теми случаями, когда само декларирование предполагает доступность для Objective-C ([SE-0160]).
Это уменьшает бинарный размер приложения за счет того, что нет избыточной компиляции Objective-C кода, который вы не используете, и дает нам возможность управлять тем, когда @objc экспонирование действительно требуется. Наследуемые от NSObject классы больше не экспонируются автоматически с помощью @objc:
Но есть несколько ситуаций, в которых Swift 4 продолжает выполнять неявное экспонирование с помощью @objc:
- Декларации, которые требут атрибута @objc
- Декларации, которые удовлетворяют требованиям @objc протокола
- Декларации, у которых есть атрибуты @IBAction, @IBInspectable, @IBOutlet, @NSManaged, @GKInspectable
Если вам требуется @objc экспонировние для целого класса, то вы можете использовать новый атрибут @objcmembers:
Для того, чтобы убрать @objc экспонирование для определенного расширения extension или функции — добавьте новый атрибут @nonobjc.
Заметьте также, что dynamic не вызывает больше неявного экспонирования @objc. Если вы хотите использовать возможности, которые зависят от динамического диспетчирования Objective-C runtime (таких, как KVO), то вам необходимо добавить при декларировании атрибуты @objc dynamic:
Код находится на Playground на странице «Limiting @objc inference».
«Видимость» private элементов любой структуры данных в ее расширении extension, находящихся в том же самом того же самого файле.
Предложение [SE-0169] изменяет правила доступа таким образом, что private декларирования также становятся «видимыми» внутри расширений extensions родительского типа в том же самом файле. Это делает возможным разделение определения типа в многочисленных расширениях extensions и позволяет использовать private для действительно private элементов, уменьшая необходимость привлечения ключевого слова fileprivate:
Переменная storage «НЕвидима» в следующем коде:
Она была бы «видна», если бы имела уровень доступа fileprivate.
Код находится на Playground на странице «private in same-file extensions».
Ссылки:
Большое спасибо за подробную статью!
Пожалуйста, но надо писать уже про Swift 5.1
Хотя про это уже есть немного на Хабре.
https://habr.com/ru/post/446522/
Сейчас идет конференция разработчиков Apple WWDC 2019 и там вовсю используется Swift 5.1 для новой очень интересной технологии SwiftUI.