SWIFTER | Блог про Swift
272 subscribers
4 photos
30 links
Swift для каждого на простом и понятном языке. Уроки программирования с интересными кейсами из реальных проектов, без воды и сложных терминов.
Download Telegram
Как легко cделать глубокую копию (deep-copy) объекта в Swift?

В Swift есть два основных типа данных: объекты (reference types) и структуры (value types). Одно из ключевых различий между value и reference типами сводится к копированию: две переменные могут указывать на один и тот же объект, поэтому если вы попытаетесь изменить одну переменную, изменения коснутся и второй. Если вы попытаетесь проделать этот же трюк со структурами, то обнаружите, что здесь изменения копии не влияют на оригинал.

Теперь вопрос: а как сделать так, чтобы изменение в одной части проекта не влияло на другие в reference типах? Для этого нам нужно сделать глубокую копию (deep-copy) объекта. Одним из вариантов решения является использование протокола NSCopying. И хотя этот способ имеет место быть, он создает много шаблонного (boilerplate) кода.

Сегодня я хочу показать вам, как для решения этой задачи можно использовать Codable. Идея достаточно простая: сначала мы кодируем объект в Data, а дальше из Data в нужный нам тип. Поскольку Data является value типом в Swift, при копировании выполняется deep-copy по умолчанию. На выходе мы получим новый объект с теми же данными, что и оригинал.

Ссылка
#tips #codable #intermediate
Как копировать текст в буфер обмена, используя UIPasteboard?

Вы можете писать в буфер обмена iOS и читать из него с помощью класса UIPasteboard. Для того, чтобы воспользоваться глобальным системным буфером, нужно использовать константу general. Это позволит обмениваться данными между всеми приложениями. Обычно пользователи взаимодействуют с системным буфером, используя "вырезать", "скопировать", "вставить" на выбранном контенте в UI.

В UIPasteboard можно хранить String, UIImage, URL, UIColors, но чаще всего вы будете использовать именно String. Чтобы записать или прочитать строку, достаточно просто считать или присвоить что-то переменной string.

Ссылка
#tips #pasteboard #basic
Взаимозаменяемое использование типов CGFloat и Double

SE-0307 в Swift 5.5 добавляет небольшое, но тем не менее очень полезное нововведение: компилятор будет автоматически уметь конвертировать между CGFloat и Double там где это нужно. Это значит, что мы сможем выполнять такие операции как умножение и деление без приведение типов.

Swift будет всегда отдавать предпочтение Double, чтобы предотвратить потерю точности. Более того, все описанное выше реализовано путем неявного использование нужного инициализатора для конвертации. Это значит, что это нововведение не меняет никакое с существующих API.

Source
#tips #core #basic
Используем String в качестве SectionIdentifierType для UITableViewDiffableDataSource

UITableViewDiffableDataSource предоставляет абсолютно новое API для работы с данными таблицы. Если вы уже имеете опыт использования, то знаете, что нам нужно предоставить два дженерика: SectionIdentifierType, ItemIdentifierType.

В качестве ItemIdentifierType обычно выступает модель, которую мы планируем отображать в таблице. SectionIdentifierType же, как следует из названия, описывает секции нашей таблицы. Классическим подходом будет использование отдельного enum для этих задач, если секций несколько. Тем не менее, если мы имеем дело только с одной, то таким образом создадим ненужный шаблонный (boilerplate) код.

Поскольку строки в Swift Hashable по умолчанию, можно использовать String в качестве SectionIdentifierType.

Source
#tips #tableView #intermediate
Как определить, включен ли у пользователя режим энергосбережения?

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

Существует два подхода к проверке подключения режима энергосбережения: получить информацию из переменной isLowPowerModeEnabled напрямую или подписаться на изменения подключения, используя NotificationCenter.

Source
#tips #lowPowerMode #basic
Typealiases с дженериками

Мало кто использует эту особенность Swift в своих проектах. Однако она может здорово улучшить некоторые участки кода и убрать ненужный шаблонный (boilerplate) код.

Typealiases могут использоваться в комбинации с generic типами.

Что самое интересное, мы можем явно указать конкретные типы для некоторых (или всех) generic параметров существующего типа. В обратную сторону, кстати, это не работает: новые параметры в typealiase вводить нельзя.

Source
#tips #generics #intermediate
Используем App Group для доступа к Realm из других приложений или расширений

App Group позволяет нескольким приложениям от одного разработчика получить доступ к общему контейнеру, в котором можно хранить и использовать какие-то данные. Поскольку Realm держит базу в файле, мы можем поместить этот файл в общий контейнер и получить доступ к нему из других наших приложений или расширений.

Для этого нужно в конфигурации Realm указать в качестве fileURL ссылку на этот общий контейнер. Получить ее можно, используя:

containerURL(forSecurityApplicationGroupIdentifier:)

Source
#tips #appGroup #advanced
Опциональное декодирование элементов массива с помощью Decodable

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

Для этого нам понадобится создать дженерик класс FailableDecodable с типом, который нужно будет десериализовать. Далее, используя singleValueContainer, получаем контейнер с декодера и пробуем получить нужную нам модель с помощью try? container.decode(Base.self).

Чтобы выполнить само декодирование элементов массива, вместо обычного типа данных, указываем тип в обертке с FailableDecodable и проходимся по нему с помощью compactMap, получая переменную base.

Таким образом, сначала мы получили массив с опциональными элементами, где могут встречаться nil, а дальше профильтровали этот массив, оставив только non-nil значения.

Source
#tips #decodable #intermediate
Как использовать tableHeaderView вместе с auto-layout?

Казалось бы, задача тривиальная, но на деле все немного сложнее. Для ее решения нам понадобится создать два метода: setTableHeaderView (1), updateTableHeaderViewIfNeeded (2). Логика следующая:

1. Мы создаем width constraint, где ширина tableHeaderView равна ширине самой tableView. Это позволит таблице получить нужный размер, когда мы будем вызывать layoutIfNeeded. Дополнительно мы центрируем наш хедер и крепим его вверху таблицы.

2. Реагируем на изменения размера в случаях вроде поворота девайса. Для этого позволяем tableView управлять frame самостоятельно, точно так же, как для ячеек. Достигается это с помощью beginUpdates/endUpdates.

Метод setTableHeaderView вызываем, когда делаем layout для нашего UI - viewDidLoad, а updateTableHeaderViewIfNeeded в viewDidLayoutSubviews.

Source
#tips #tableView #basic