Что такое discardableResult и как использовать этот атрибут?
Во время написания функций или методов в Swift мы в некоторых случаях хотим игнорировать возвращаемое значение, а в других – все же иметь возможность обработать его. Если попытаться вызвать метод, который возвращает значение, и никак его не обработать, получим предупреждение "Result of call to 'foo()' is unused".
Атрибут discardableResult позволяет убрать данное предупреждение. В случае правильного использования эта небольшая функция (о которой, тем не менее, стоит знать) улучшит чистоту вашего кода и позволит вам не использовать нижнее подчеркивание, чтобы игнорировать результат.
Перед написанием каждого метода хорошо подумайте, важно ли обрабатывать возвращаемое значение. В некоторых случаях лучше будет всё-таки заставить пользователя вашего метода хендлить его. Например, если есть необходимость сообщить важную информацию (о возникшей ошибке и пр.).
Source
#languageGuide #attributes #basic
Во время написания функций или методов в Swift мы в некоторых случаях хотим игнорировать возвращаемое значение, а в других – все же иметь возможность обработать его. Если попытаться вызвать метод, который возвращает значение, и никак его не обработать, получим предупреждение "Result of call to 'foo()' is unused".
Атрибут discardableResult позволяет убрать данное предупреждение. В случае правильного использования эта небольшая функция (о которой, тем не менее, стоит знать) улучшит чистоту вашего кода и позволит вам не использовать нижнее подчеркивание, чтобы игнорировать результат.
Перед написанием каждого метода хорошо подумайте, важно ли обрабатывать возвращаемое значение. В некоторых случаях лучше будет всё-таки заставить пользователя вашего метода хендлить его. Например, если есть необходимость сообщить важную информацию (о возникшей ошибке и пр.).
Source
#languageGuide #attributes #basic
Как декодировать property list используя Decodable?
Property list, обычно сокращенно plist, представляет собой XML-файл, содержащий данные в формате "ключ-значение". Вы можете использовать его в своих приложениях для iOS в качестве простого хранилища данных. Эта функция-утилита позволит вам задекодить данные в нужный удобный вам тип, используя Decodable.
Source
#handyExtensions #plist #basic
Property list, обычно сокращенно plist, представляет собой XML-файл, содержащий данные в формате "ключ-значение". Вы можете использовать его в своих приложениях для iOS в качестве простого хранилища данных. Эта функция-утилита позволит вам задекодить данные в нужный удобный вам тип, используя Decodable.
Source
#handyExtensions #plist #basic
Как использовать String в качестве ошибок?
Обычно, чтобы сообщать о том, что операция завершилась с ошибкой, в Swift используется протокол Error. Когда мы проектируем чистое API, обычной практикой для выполнения данной цели является создание собственного error enum, в котором прописываются нужные варианты ошибок под конкретный функционал.
Но что делать, если мы хотим получить простое решение для случаев, когда ошибки единичные и смысла создавать отдельный enum нет?
Тогда можно реализовать LocalizedError для String и использовать строку в качестве ошибки. Такой вариант идеально подойдет для возвращение простых, user-facing ошибок.
Source
#handyExtensions #error #basic
Обычно, чтобы сообщать о том, что операция завершилась с ошибкой, в Swift используется протокол Error. Когда мы проектируем чистое API, обычной практикой для выполнения данной цели является создание собственного error enum, в котором прописываются нужные варианты ошибок под конкретный функционал.
Но что делать, если мы хотим получить простое решение для случаев, когда ошибки единичные и смысла создавать отдельный enum нет?
Тогда можно реализовать LocalizedError для String и использовать строку в качестве ошибки. Такой вариант идеально подойдет для возвращение простых, user-facing ошибок.
Source
#handyExtensions #error #basic
Как копировать текст в буфер обмена, используя UIPasteboard?
Вы можете писать в буфер обмена iOS и читать из него с помощью класса UIPasteboard. Для того, чтобы воспользоваться глобальным системным буфером, нужно использовать константу general. Это позволит обмениваться данными между всеми приложениями. Обычно пользователи взаимодействуют с системным буфером, используя "вырезать", "скопировать", "вставить" на выбранном контенте в UI.
В UIPasteboard можно хранить String, UIImage, URL, UIColors, но чаще всего вы будете использовать именно String. Чтобы записать или прочитать строку, достаточно просто считать или присвоить что-то переменной string.
Ссылка
#tips #pasteboard #basic
Вы можете писать в буфер обмена 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
SE-0307 в Swift 5.5 добавляет небольшое, но тем не менее очень полезное нововведение: компилятор будет автоматически уметь конвертировать между CGFloat и Double там где это нужно. Это значит, что мы сможем выполнять такие операции как умножение и деление без приведение типов.
Swift будет всегда отдавать предпочтение Double, чтобы предотвратить потерю точности. Более того, все описанное выше реализовано путем неявного использование нужного инициализатора для конвертации. Это значит, что это нововведение не меняет никакое с существующих API.
Source
#tips #core #basic
Как добавить Pull-to-Refresh в UITableView или UICollectionView?
Swift предоставляет нам готовый функционал для этой цели, который реализован с помощью класса UIRefreshControl. Любой наследник UIScrollView имеет свойство refreshControl, а поскольку UITableView и UICollectionView как раз являются наследниками, нам достаточно присвоить экземпляр UIRefreshControl этому свойству.
Если вы хотите получить что-то более уникальное, например добавить свою анимацию, тогда можно обратить внимание на библиотеки как CRRefresh. Правда, по моему опыту, все они работают плохо и имеют те или иные проблемы.
Source
#languageGuide #UIRefreshControl #basic
Swift предоставляет нам готовый функционал для этой цели, который реализован с помощью класса UIRefreshControl. Любой наследник UIScrollView имеет свойство refreshControl, а поскольку UITableView и UICollectionView как раз являются наследниками, нам достаточно присвоить экземпляр UIRefreshControl этому свойству.
Если вы хотите получить что-то более уникальное, например добавить свою анимацию, тогда можно обратить внимание на библиотеки как CRRefresh. Правда, по моему опыту, все они работают плохо и имеют те или иные проблемы.
Source
#languageGuide #UIRefreshControl #basic
Как удалить десятичную дробь из числа с плавающей запятой, если она равна 0?
Это простое расширение убирает десятичную дробь из числа с плавающей запятой, если она равна 0. Это пригодится в тех случаях, когда нам нужно сделать красивый вывод числа где-то в пользовательском интерфейсе. Мы используем truncatingRemainder (тот же %), чтобы найти остаток от деления. Если оно равно нулю, то убираем плавающую точку, форматируя строку в виде "%.0f", в противном же случае просто конвертируем Double в String без каких-либо изменений.
Source
#handyExtensions #double #basic
Это простое расширение убирает десятичную дробь из числа с плавающей запятой, если она равна 0. Это пригодится в тех случаях, когда нам нужно сделать красивый вывод числа где-то в пользовательском интерфейсе. Мы используем truncatingRemainder (тот же %), чтобы найти остаток от деления. Если оно равно нулю, то убираем плавающую точку, форматируя строку в виде "%.0f", в противном же случае просто конвертируем Double в String без каких-либо изменений.
Source
#handyExtensions #double #basic
#MockInterview
Вопрос: Можно ли получить доступ к private свойству внутри extension? 💬
Ответ: Да, мы можем получить доступ к private свойству внутри extension, но только при условии, что это extension находится внутри одного и того же файла, что и class, struct или enum. Если мы попытаемся расширить тип данных за пределами файла, private свойства доступны не будут.
Объяснение: Расширения, которые находятся в том же файле, что и class, struct или enum, которые они расширяют, ведут себя так, как если бы код в extension был написан как часть самого исходного типа.
В результате мы можем:
- Объявить private свойство в самом типе и достучаться к этому свойству из расширений в том же файле.
- Объявить private свойство в одном расширении и получите доступ к этому свойству из другого расширения в том же файле.
- Объявить private свойство в расширении и получите доступ к этому свойству из самого типа в том же файле.
#accessControl #basic
Вопрос: Можно ли получить доступ к private свойству внутри extension? 💬
Ответ: Да, мы можем получить доступ к private свойству внутри extension, но только при условии, что это extension находится внутри одного и того же файла, что и class, struct или enum. Если мы попытаемся расширить тип данных за пределами файла, private свойства доступны не будут.
Объяснение: Расширения, которые находятся в том же файле, что и class, struct или enum, которые они расширяют, ведут себя так, как если бы код в extension был написан как часть самого исходного типа.
В результате мы можем:
- Объявить private свойство в самом типе и достучаться к этому свойству из расширений в том же файле.
- Объявить private свойство в одном расширении и получите доступ к этому свойству из другого расширения в том же файле.
- Объявить private свойство в расширении и получите доступ к этому свойству из самого типа в том же файле.
#accessControl #basic
Как определить, включен ли у пользователя режим энергосбережения?
Когда пользователь включает режим энергосбережения, ваш код должен избегать слишком энергозатратных действий. Например, вы хотите выполнить какую-то миграцию или синхронизацию с облаком, но откладываете её на определенное время, чтобы не нагружать батарею.
Существует два подхода к проверке подключения режима энергосбережения: получить информацию из переменной isLowPowerModeEnabled напрямую или подписаться на изменения подключения, используя NotificationCenter.
Source
#tips #lowPowerMode #basic
Когда пользователь включает режим энергосбережения, ваш код должен избегать слишком энергозатратных действий. Например, вы хотите выполнить какую-то миграцию или синхронизацию с облаком, но откладываете её на определенное время, чтобы не нагружать батарею.
Существует два подхода к проверке подключения режима энергосбережения: получить информацию из переменной isLowPowerModeEnabled напрямую или подписаться на изменения подключения, используя NotificationCenter.
Source
#tips #lowPowerMode #basic
Различия между 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
Как создать 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
#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
Как использовать 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