lazy теперь работает в локальном контексте (local context)
Используя ключевое слово lazy, мы можем реализовать свойство, начальное значение которого не вычисляется до первого использования. Начиная со Swift 5.5, можно использовать lazy внутри функций. На практике это позволит проводить оптимизацию, когда мы имеем какое-то условия if-else и не хотим проводить затратные вычисления на то, что нам не нужно.
Source
#languageGuide #lazy #intermediate
Используя ключевое слово lazy, мы можем реализовать свойство, начальное значение которого не вычисляется до первого использования. Начиная со Swift 5.5, можно использовать lazy внутри функций. На практике это позволит проводить оптимизацию, когда мы имеем какое-то условия if-else и не хотим проводить затратные вычисления на то, что нам не нужно.
Source
#languageGuide #lazy #intermediate
Как определить, включен ли у пользователя режим энергосбережения?
Когда пользователь включает режим энергосбережения, ваш код должен избегать слишком энергозатратных действий. Например, вы хотите выполнить какую-то миграцию или синхронизацию с облаком, но откладываете её на определенное время, чтобы не нагружать батарею.
Существует два подхода к проверке подключения режима энергосбережения: получить информацию из переменной isLowPowerModeEnabled напрямую или подписаться на изменения подключения, используя NotificationCenter.
Source
#tips #lowPowerMode #basic
Когда пользователь включает режим энергосбережения, ваш код должен избегать слишком энергозатратных действий. Например, вы хотите выполнить какую-то миграцию или синхронизацию с облаком, но откладываете её на определенное время, чтобы не нагружать батарею.
Существует два подхода к проверке подключения режима энергосбережения: получить информацию из переменной isLowPowerModeEnabled напрямую или подписаться на изменения подключения, используя NotificationCenter.
Source
#tips #lowPowerMode #basic
Typealiases с дженериками
Мало кто использует эту особенность Swift в своих проектах. Однако она может здорово улучшить некоторые участки кода и убрать ненужный шаблонный (boilerplate) код.
Typealiases могут использоваться в комбинации с generic типами.
Что самое интересное, мы можем явно указать конкретные типы для некоторых (или всех) generic параметров существующего типа. В обратную сторону, кстати, это не работает: новые параметры в typealiase вводить нельзя.
Source
#tips #generics #intermediate
Мало кто использует эту особенность Swift в своих проектах. Однако она может здорово улучшить некоторые участки кода и убрать ненужный шаблонный (boilerplate) код.
Typealiases могут использоваться в комбинации с generic типами.
Что самое интересное, мы можем явно указать конкретные типы для некоторых (или всех) generic параметров существующего типа. В обратную сторону, кстати, это не работает: новые параметры в typealiase вводить нельзя.
Source
#tips #generics #intermediate
Используем App Group для доступа к Realm из других приложений или расширений
App Group позволяет нескольким приложениям от одного разработчика получить доступ к общему контейнеру, в котором можно хранить и использовать какие-то данные. Поскольку Realm держит базу в файле, мы можем поместить этот файл в общий контейнер и получить доступ к нему из других наших приложений или расширений.
Для этого нужно в конфигурации Realm указать в качестве fileURL ссылку на этот общий контейнер. Получить ее можно, используя:
containerURL(forSecurityApplicationGroupIdentifier:)
Source
#tips #appGroup #advanced
App Group позволяет нескольким приложениям от одного разработчика получить доступ к общему контейнеру, в котором можно хранить и использовать какие-то данные. Поскольку Realm держит базу в файле, мы можем поместить этот файл в общий контейнер и получить доступ к нему из других наших приложений или расширений.
Для этого нужно в конфигурации Realm указать в качестве fileURL ссылку на этот общий контейнер. Получить ее можно, используя:
containerURL(forSecurityApplicationGroupIdentifier:)
Source
#tips #appGroup #advanced
Различия между let и var
Когда в Swift нужно связать имя (например, "username" или "balance") со значением определенного типа (например, строка "Den" или число 96.23), используются константы и переменные.
Чтобы объявить константу, используем ключевое слово let, для переменной – var. Изменить значение константы после того, как мы ее объявили, нельзя, переменную же мы можем менять сколько угодно раз.
На скриншоте видим:
maxPasswordLenght – константа со значением 64, поскольку максимальная длина пароля в приложении фиксированная, и менять ее мы не будем;
currentPasswordLenght – переменная, потому что ее значение напрямую зависит от значения, которое пользователь вводит в текстовом поле.
Source
#languageGuide #properties #basic
Когда в Swift нужно связать имя (например, "username" или "balance") со значением определенного типа (например, строка "Den" или число 96.23), используются константы и переменные.
Чтобы объявить константу, используем ключевое слово let, для переменной – var. Изменить значение константы после того, как мы ее объявили, нельзя, переменную же мы можем менять сколько угодно раз.
На скриншоте видим:
maxPasswordLenght – константа со значением 64, поскольку максимальная длина пароля в приложении фиксированная, и менять ее мы не будем;
currentPasswordLenght – переменная, потому что ее значение напрямую зависит от значения, которое пользователь вводит в текстовом поле.
Source
#languageGuide #properties #basic
Друзья, новость для вас❗️
Swifter расширяет свои границы, и теперь у нас есть аккаунт на Medium. Если среди вас есть активные пользователи данной платформы, будем рады взаимодействовать с вами и там🤗
Ссылка на SWIFTER | Блог про Swift в Medium:
https://medium.com/@BlogSwift
А с подписчиками нашего канала, которые верны Telegram, пообщаемся совсем скоро в новом обучающем посте. Ждите)
Swifter расширяет свои границы, и теперь у нас есть аккаунт на Medium. Если среди вас есть активные пользователи данной платформы, будем рады взаимодействовать с вами и там🤗
Ссылка на SWIFTER | Блог про Swift в Medium:
https://medium.com/@BlogSwift
А с подписчиками нашего канала, которые верны Telegram, пообщаемся совсем скоро в новом обучающем посте. Ждите)
Как создать UIImage со сплошной заливкой и заданным размером?
В сегодняшнем посте из серии #handyExtensions я покажу, как, используя UIGraphicsRendererContext, можно создать изображения со сплошной заливкой определенного размера.
Логика очень простая:
1. Создаем контекст с помощью UIGraphicsBeginImageContextWithOptions. Это своего рода полотно, где мы можем рисовать.
2. Устанавливаем цвет для заливки.
3. Делаем заливку нужного нам размера.
4. Рендерим в изображения.
После того, как мы зарендерим изображения, нужно очистить контекст, используя UIGraphicsEndImageContext.
Source
#handyExtensions #images #basic
В сегодняшнем посте из серии #handyExtensions я покажу, как, используя UIGraphicsRendererContext, можно создать изображения со сплошной заливкой определенного размера.
Логика очень простая:
1. Создаем контекст с помощью UIGraphicsBeginImageContextWithOptions. Это своего рода полотно, где мы можем рисовать.
2. Устанавливаем цвет для заливки.
3. Делаем заливку нужного нам размера.
4. Рендерим в изображения.
После того, как мы зарендерим изображения, нужно очистить контекст, используя UIGraphicsEndImageContext.
Source
#handyExtensions #images #basic
Улучшаем состояние AppDelegate с помощью сервисов
По мере того, как растет приложение, растет и логика в AppDelegate. Инициализация SDK, обработка диплинков, пуш-уведомления и многое другое. Читаемость этого файла сильно падает, он превращается в огромную кучу кода, который сложно поддерживать.
Разработчики по-разному избегают этой ситуации: с помощью команд, компоновщика, медиатора. Сегодня я поделюсь с вами библиотекой, которую использую в своих проектах.
PluggableApplicationDelegate решает описанную проблему с помощью сервисов, причем для каждой задачи можно создавать отдельный. Например, для настройки FacebookSDK вы создадите сервис, в котором сможете произвести инициализацию, обработать отложенные глубинные ссылки и т.д.
Source / PluggableApplicationDelegate
#shareLibrary #appDelegate #basic
По мере того, как растет приложение, растет и логика в AppDelegate. Инициализация SDK, обработка диплинков, пуш-уведомления и многое другое. Читаемость этого файла сильно падает, он превращается в огромную кучу кода, который сложно поддерживать.
Разработчики по-разному избегают этой ситуации: с помощью команд, компоновщика, медиатора. Сегодня я поделюсь с вами библиотекой, которую использую в своих проектах.
PluggableApplicationDelegate решает описанную проблему с помощью сервисов, причем для каждой задачи можно создавать отдельный. Например, для настройки FacebookSDK вы создадите сервис, в котором сможете произвести инициализацию, обработать отложенные глубинные ссылки и т.д.
Source / PluggableApplicationDelegate
#shareLibrary #appDelegate #basic
Как сделать аббревиатуру для числа?
Когда нужно отобразить огромное число в пользовательском интерфейсе (скажем, количество лайков или подписчиков), зачастую показывать число полностью не имеет смысла. Гораздо удобнее использовать агрегированное представление в виде "12M" или "1.5K".
Данное расширение позволяет получить в виде аббревиатуры числа вплоть до квадриллиона. Если кратко, то логика такая:
1. Есть список аббревиатур "KMBTQ". Проходимся по нему с конца, потому что представление должно быть как можно компактнее.
2. Для каждой аббревиатуры определяем фактор. Для тысяч это ЧИСЛО/10^3 (10 в степени 3).
3. Если фактор меньше нуля, число слишком маленькое для этой аббревиатуры, поэтому двигаемся дальше.
4. Если фактор больше нуля, значит к фактору добавляем нужную аббревиатуру и получаем результат.
Source
#handyExtensions #int #intermediate
Когда нужно отобразить огромное число в пользовательском интерфейсе (скажем, количество лайков или подписчиков), зачастую показывать число полностью не имеет смысла. Гораздо удобнее использовать агрегированное представление в виде "12M" или "1.5K".
Данное расширение позволяет получить в виде аббревиатуры числа вплоть до квадриллиона. Если кратко, то логика такая:
1. Есть список аббревиатур "KMBTQ". Проходимся по нему с конца, потому что представление должно быть как можно компактнее.
2. Для каждой аббревиатуры определяем фактор. Для тысяч это ЧИСЛО/10^3 (10 в степени 3).
3. Если фактор меньше нуля, число слишком маленькое для этой аббревиатуры, поэтому двигаемся дальше.
4. Если фактор больше нуля, значит к фактору добавляем нужную аббревиатуру и получаем результат.
Source
#handyExtensions #int #intermediate
#MockInterview
Вопрос: Как можно увеличить зону нажатия в UIButton, чтобы соответствовать HIG? 💬
Ответ: Есть два способа решения этой задачи. Первый – это использование contentEdgeInsets. Больше размер = больше зона нажатия. В этом подходе важно учитывать, что мы просто увеличиваем размер кнопки. Если у нас есть заданный frame или же установленные constraint, то UIButton будет просто сжимать текст / изображение, и эффекта это не даст.
Второй подход – сделать свой наследник UIButton и переопределить point-inside метод. Это позволит нам увеличить зону нажатия без изменения самого размера кнопки. Правда, если кнопка вложенная в какой-то parent view, то увеличить зону нажатия за его пределы не выйдет.
Объяснение: Вопрос очень базовый, но почему-то всех всегда сбивает с толку слово HIG. А это просто аббревиатура Human Interface Guidelines, где Apple пишет, что ваша кнопка должна быть минимум 44 пикселя (кстати, запомните это число, некоторые компании умудряются задавать даже такой вопрос).
По сути, подхода для решения поставленной задачи два. Если нужно увеличить зону, и есть возможность быстро поменять размер кнопки, – contentEdgeInsets. Если ситуация сложнее, например, кнопка в UIStackView, – наследование и переопределение point(point:with).
#UIButton #basic
Вопрос: Как можно увеличить зону нажатия в UIButton, чтобы соответствовать HIG? 💬
Ответ: Есть два способа решения этой задачи. Первый – это использование contentEdgeInsets. Больше размер = больше зона нажатия. В этом подходе важно учитывать, что мы просто увеличиваем размер кнопки. Если у нас есть заданный frame или же установленные constraint, то UIButton будет просто сжимать текст / изображение, и эффекта это не даст.
Второй подход – сделать свой наследник UIButton и переопределить point-inside метод. Это позволит нам увеличить зону нажатия без изменения самого размера кнопки. Правда, если кнопка вложенная в какой-то parent view, то увеличить зону нажатия за его пределы не выйдет.
Объяснение: Вопрос очень базовый, но почему-то всех всегда сбивает с толку слово HIG. А это просто аббревиатура Human Interface Guidelines, где Apple пишет, что ваша кнопка должна быть минимум 44 пикселя (кстати, запомните это число, некоторые компании умудряются задавать даже такой вопрос).
По сути, подхода для решения поставленной задачи два. Если нужно увеличить зону, и есть возможность быстро поменять размер кнопки, – contentEdgeInsets. Если ситуация сложнее, например, кнопка в UIStackView, – наследование и переопределение point(point:with).
#UIButton #basic
Опциональное декодирование элементов массива с помощью Decodable
Сегодня мы разберем случай, когда нужно отобразить массив данных с сервера, даже если некоторые элементы этого массива декодировать не удается. Предположим, в приложении есть секция (жанр музыки). В этой секции есть много разных элементов (песен). Если случится так, что какая-то песня придет с сервера с ошибкой (или с ней что-то будет не так), мы все равно сможем отобразить секцию без проблемного трека.
Для этого нам понадобится создать дженерик класс FailableDecodable с типом, который нужно будет десериализовать. Далее, используя singleValueContainer, получаем контейнер с декодера и пробуем получить нужную нам модель с помощью try? container.decode(Base.self).
Чтобы выполнить само декодирование элементов массива, вместо обычного типа данных, указываем тип в обертке с FailableDecodable и проходимся по нему с помощью compactMap, получая переменную base.
Таким образом, сначала мы получили массив с опциональными элементами, где могут встречаться nil, а дальше профильтровали этот массив, оставив только non-nil значения.
Source
#tips #decodable #intermediate
Сегодня мы разберем случай, когда нужно отобразить массив данных с сервера, даже если некоторые элементы этого массива декодировать не удается. Предположим, в приложении есть секция (жанр музыки). В этой секции есть много разных элементов (песен). Если случится так, что какая-то песня придет с сервера с ошибкой (или с ней что-то будет не так), мы все равно сможем отобразить секцию без проблемного трека.
Для этого нам понадобится создать дженерик класс 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
Казалось бы, задача тривиальная, но на деле все немного сложнее. Для ее решения нам понадобится создать два метода: 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
Асинхронные скрипты на Swift
Да, на Swift можно писать скрипты и, используя некоторые хитрости, делать это достаточно эффективно. А с добавлением executableTarget в SPM задача стала еще проще.
Проблема возникает тогда, когда вы пытаетесь сделать скрипт асинхронным (например, получить или обновить какие-то данные в сети). В таком случае нужно заставить скрипт ждать выполнения асинхронной задачи и только после этого завершать работу. В этом нам поможет RunLoop. Обычно, когда мы пишем приложения под iOS или macOS, система сама контролирует процесс создания RunLoop, но для скриптов мы должны сделать это самостоятельно.
Сегодня я хотел бы поделиться небольшой, но полезной библиотекой SwiftScriptRunner, которая реализует своего рода мьютекс, предотвращая завершение программы. Механизм достаточно простой: счетчик, на основе которого мы стартуем бесконечный цикл while, в котором запускаем RunLoop.current на 0.1 секунды. Как только счетчик становится равен 0, скрипт завершает работу.
Source / SwiftScriptRunner
#shareLibrary #scripts #advanced
Да, на Swift можно писать скрипты и, используя некоторые хитрости, делать это достаточно эффективно. А с добавлением executableTarget в SPM задача стала еще проще.
Проблема возникает тогда, когда вы пытаетесь сделать скрипт асинхронным (например, получить или обновить какие-то данные в сети). В таком случае нужно заставить скрипт ждать выполнения асинхронной задачи и только после этого завершать работу. В этом нам поможет RunLoop. Обычно, когда мы пишем приложения под iOS или macOS, система сама контролирует процесс создания RunLoop, но для скриптов мы должны сделать это самостоятельно.
Сегодня я хотел бы поделиться небольшой, но полезной библиотекой SwiftScriptRunner, которая реализует своего рода мьютекс, предотвращая завершение программы. Механизм достаточно простой: счетчик, на основе которого мы стартуем бесконечный цикл while, в котором запускаем RunLoop.current на 0.1 секунды. Как только счетчик становится равен 0, скрипт завершает работу.
Source / SwiftScriptRunner
#shareLibrary #scripts #advanced