iOS Dev
7.55K subscribers
956 photos
70 videos
1 file
1.09K links
🍏Канал об iOS-разработке, необычных подходах и решениях.
👨‍💻Автор: Виктор Грушевский (@Viktorianec)
Темы:
⭐️ Подготовка к собеседованиям.
⭐️ Архитектуры и алгоритмы.
⭐️ Код. Много кода.

⚒️База знаний: https://boosty.to/ios_dev

#ios #mobile #swift
加入频道
Какой алгоритм использует Swift для sort()?

💡 Этот вопрос из тех, которые вряд ли вы себе могли бы задать в пятницу, но если вам интересно, то внутри реализации алгоритма используется следующее:

1️⃣ Если менее, чем 20 элементов, то применяется сортировка вставками (insertion sort). В комментариях в коде уточняется, что именно этот тип лучше подходит для небольших областей.

2️⃣ Интроспективная сортировка (Introsort), включающая в себя быструю сортировку до определённой глубины рекурсии 2*floor(log(N)).

3️⃣ И переключается на пирамидальную сортировку (она же heapsort или сортировка кучей), когда глубина рекурсии превысит заранее установленный уровень.

Больше деталей в коде swift.

@iOS Dev — теперь вы, возможно, знаете больше😅
Упрощение внедрения зависимостей с помощью паттерна «Фасад» в iOS

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

Представьте, например, что в вашем приложении есть слой, который работает с сетевыми вызовами. Для этого вам, конечно, нужен JSONDecoder, несколько объектов DispatchQueue для работы с потоками и параллелизмом, возможно, объект менеджера флагов функций и так далее.

Представьте себе, насколько сложным может стать инициализатор для этого класса, поскольку вам нужно передать все эти экземпляры, и более того, чтобы сделать его тестируемым, все они должны быть мокабельны.

📖 Эта статья предлагает отличную комбинацию двух паттернов проектирования (Facade и Dependency Injection).

Используя возможности протоколов, рассказывается, как разделить Facade, позволяя каждому классу нашего приложения иметь внутри только те инструменты, которые ему необходимы для выполнения задач.

@iOS Dev
Chisel — набор команд LLDB для помощи в отладке iOS-приложений

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

Для LLDB есть возможность импортировать подключаемые модули, что может значительно расширить возможности отладки. Chisel, инструмент, который содержит множество плагинов lldb — например, команду border, которая добавляет яркую рамку к UIView, чтобы вы могли быстро найти его на экране, и все эти плагины работают благодаря умному использованию команд e/po.

Несколько примеров команд, кроме border:

🟢 pviews — рекурсивно просматривает и выводит описание для key window.

🟢 visualize — можно открыть UIImage, CGImageRef, UIView, CALayer, NSData (для картинки), UIColor, CIColor, или CGColorRef в Preview.app на вашем маке.

🟢 fv — ищет вьюху в иерархии, чьё имя класса соответствует заданной регулярке.

🟢 presponder — выводит всю responder chain, начиная с данного объекта.

🟢 и много чего ещё.

Кстати, ещё с помощью Chisel можно пилить собственные команды и использовать их для отладки.

🛠 Ссылка на #opensource проект: Github.

Делюсь также и двумя крутейшими статьями про LLDB, благодаря одной из которых я и узнал об этом инструменте:

📖 Расширенные приемы lldb для Swift — внедрение и изменение кода на лету.

📖 Танец в отладчике - вальс с LLDB. Пусть материал вышел и давно, но всё ещё может быть нам полезен.

👍 Рекомендую прочесть и сохранить себе в избранное.

@iOS Dev
Посетитель или же Visitor — это один из паттернов, описанных в учебнике «Банды четырех», под названием «Design Patterns: Elements of Reusable Object-Oriented Software».

Одним словом, шаблон может быть полезен, когда необходимо иметь возможность выполнять какие-либо однотипные действия над группой несвязанных объектов разных типов.

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

📖 Данный материал предназначен для знакомства с этим приемом. Автор подчёркивает, что знает о недостатках кода и возможностях его улучшения.

В любом случае, практически у любого паттерна есть свои преимущества и недостатки, и перед его использованием всегда следует подумать и принять решение осознанно.

Паттерны — это, с одной стороны, способ обобщения методов программирования для облегчения чтения и обсуждения кода. С другой стороны, есть способ решения проблемы (иногда искусственно вводимый). И, конечно, ни в коем случае не стоит фанатично доводить код до всех известных паттернов только ради самого факта их использования.

@iOS Dev
Создание переиспользуемой системы для сложных URL-запросов с помощью Swift

📖 В статье описывается, как обрабатывать элементы запроса, HTTP-заголовки, тело запроса и многое другое простым декларативным способом.

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

Foundation имеет встроенный механизм для этого — URLComponents с его свойством queryItems, но может оказаться довольно хлопотно каждый раз настраивать это для каждого запроса.

Поэтому в этой статье автор рассказывает, как построить многоразовую, гибкую систему, которая может пригодиться для сложных запросов.

@iOS Dev
iOS Dev
Photo
Каким будет результат выполнения со скриншота выше?
Anonymous Quiz
1%
3
54%
2
18%
1
1%
0
2%
nil
13%
Код не скомпилируется
10%
Будет ошибка Index out of bounds
iOS Dev
Каким будет результат выполнения со скриншота выше?
Вопрос со скриншота не такой простой на первый взгляд, как кажется

😉Но и для этого есть объяснение, всё дело в том, что:

После dropFirst() у нас будет не Array, а ArraySlice. Тип ArraySlice позволяет быстро и эффективно выполнять операции над разделами большего (исходного) массива.

Вместо копирования элементов среза в новое хранилище, экземпляр ArraySlice представляет собой вид на хранилище исходного массива.

А поскольку ArraySlice представляет тот же интерфейс, что и Array, вы можете выполнять над срезом те же операции, что и над исходным массивом.

Это означает, что элемент по индексу 0 в ArraySlice — это тот же элемент по индексу 0 из исходного массива.

Поэтому во время выполнения этого кода у нас будет ошибка в рантайме.

Это происходит потому, что ArraySlice не содержит элемента с индексом 0 исходного массива, а только элементы с индексами 1 и 2.

Если мы хотим получить первый или последний индекс получившегося фрагмента безопасным способом, мы должны использовать startIndex и endIndex. Ну, ещё можно использовать Array для доступа к нужному элементу (кому как больше нравится).

📖
Кстати, есть отличная статья, в которой рассматривается больше деталей.

@iOS Dev
Два способа сохранить картинку в галерею на устройстве

🟢 Первый способ — используем метод времён Objective-C.

UIImageWriteToSavedPhotosAlbum(image: UIImage, completionTarget: Any?, completionSelector: Selector?, contextInfo: UnsafeMutableRawPointer?)

🟢 Второй способ связан с использованием PHPhotoLibrary.

Добавьте ключ NSPhotoLibraryAddUsageDescription (нужен для записи в галерею, не для чтения или изменения) в Info.plist и воспользуйтесь creationRequestForAsset для вашего UIImage:

PHPhotoLibrary.shared().performChanges {
_ = PHAssetChangeRequest.creationRequestForAsset(from: image)

} completionHandler: { (success, error) in

}


Если у вас есть доступ к URL-адресу, то можно использовать creationRequestForAssetFromImage(atFileURL: imageUrl), где imageURL - адрес нашего изображения.

А если нужно сохранить видео, то используйте creationRequestForAssetFromVideo(atFileURL: videoUrl)

@iOS Dev
👍 Apple анонсировали серию презентаций с вопросами и ответами на тему App Store

Обещают рассказать, как максимально эффективно использовать функции App Store.

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

Прямые презентации с вопросами и ответами будут проходить в течение августа в разных часовых поясах и на разных языках.

Требование: наличие девелоперской учетки.

Узнать больше: тут.

Зарегистрироваться: здесь.

Будет точно полезно инди-разработчикам и маркетологам.

@iOS Dev
NSPredicate в Swift: определение, примеры и один интересный факт

Предикаты используются при построении поискового запроса или для фильтрации в памяти с помощью определения логических условий.

Хотя предикаты принято создавать непосредственно из экземпляров NSComparisonPredicate, NSCompoundPredicate и NSExpression, часто предикаты создаются с помощью обычных (форматных) строк.

Рассмотрим некоторые способы задания предикатов.

Простые сравнения. Пример: jobTitle == "Team Lead" или channelName == "iOS Dev".

Поиск без учета регистра и диакритических знаков, например, name contains[cd] "Bruce".

💡Интересный факт: здесь c это case-insensitive (без учёта регистра), а d - diacritic insensitive (игнорим диакритический знак, то есть если в поиске будет два символа c и ç, они попадут в условие).

Логические операции, например firstName like "Ivan" или lastName like "Ivanov".
Нельзя не уточнить, что like сработает как == для этого условия.

Ограничения временного диапазона, например, date between {$YESTERDAY, $TOMORROW}.

Относительные условия, например, group.name like "work*".

Агрегатные операции, например @sum.items.price < 1000.

БОНУС

🟢 Ключевое слово MATCHES можно использовать для поиска с помощью регулярки.
Пример: NSPredicate(format: "name MATCHES %@", query).

🟢 А для поиска строк, начинающихся или заканчивающихся заданным условием, пригодятся BEGINSWITH и ENDSWITH.

📖 Полный справочник по синтаксису можно посмотреть в документации.

📖 Вы также можете создавать предикаты, включающие переменные, используя метод evaluate(with:substitutionVariables:), чтобы предварительно определить предикат перед заменой конкретных значений в рантайме.

@iOS Dev
🎉 Live Activities доступны в iOS 16 beta 4

Live Activities помогают людям следить за тем, что происходит в вашем приложении в режиме реального времени, прямо с экрана блокировки. Теперь вы можете начать работу с Live Activities и новым фреймворком ActivityKit, который достепен в beta 4 iOS 16.

Пример использования

Спортивное приложение может позволить пользователю запустить Live Activity для прямой трансляции. Эта трансляция, в свою очередь, может отображаться на локскрине на протяжении всей игры и предлагать краткий обзор последних событий.

‼️ Обратите внимание, что Live Activities и ActivityKit не будут включены в первый публичный релиз iOS 16.

Отправлять приложения, включающие Live Activities, можно будет позже.

@iOS Dev
Как улучшить отклик приложения: 10 советов от инженеров Apple

Когда приложение реагирует на жесты и касания в режиме реального времени, у пользователей создается положительное впечатление.

Приложения с зависаниями (задержками отклика) или дёрганым поведением на экране разрушают эту иллюзию.

Это заставляет пользователя задуматься, правильно ли работает приложение. Чтобы избежать зависаний и заминок, при разработке и тестировании приложения нужно учитывать следующие приблизительные пороговые значения.

100 мс — это максимальная отсечка для задержек при непосредственном взаимодействии с пользователем. Более короткая задержка редко заметна.

5 мс — это порог для достижения плавного скролла на экране.

Для непрерывного скролла новый фрейм должен быть готов каждый раз при обновлении экрана. На устройствах Apple это может происходить 120 раз в секунду, или каждые 8,3 мс. В зависимости от системных условий и задач в приложении, у вас может не быть полных 8,3 мс для подготовки следующего обновления экрана.

Вот десять советов, которые помогут избежать зависаний и сбоев в приложении, а также несколько инструментов, которые помогут вам обнаруживать и анализировать эти типы проблем с реагированием.

1️⃣ Избегайте зависаний, освобождайте основной поток от работы, не связанной с пользовательским интерфейсом.

2️⃣ Проанализируйте, какие части вашего приложения должны выполняться в основном потоке, а какие нет.

3️⃣ Избегайте слишком большого количества потоков.
Dispatch и OperationQueue поддерживают внутренний пул рабочих потоков, настроенных на емкость и нагрузку устройства. Используйте эти технологии вместо создания собственных фоновых потоков для сохранения баланса.

4️⃣ Избегайте лагов, сводя к минимуму время обновления для view.
Чтобы обеспечить плавную анимацию, которая выглядит как непрерывное движение, устройства Apple обновляют экран до 120 раз в секунду. Когда ваше приложение находится в foreground (на переднем плане, кому как привычнее), код отрисовки в основном потоке должен быть завершен до того, как потребуется следующий фрейм, чтобы избежать пропуска кадров и появления рывков.

5️⃣ Оптимизируйте свое приложение для переменной частоты обновления.
Если ваше приложение напрямую взаимодействует с графической системой, например, когда вы выполняете собственный рендеринг, помните о дисплеях с переменной частотой обновления.

6️⃣ Напишите тесты производительности, чтобы обеспечить быстрое выполнение кода, привязанного к основному потоку.

7️⃣ Ищите зависания и потенциальные риски, которые могут к этому привести.

8️⃣ Попробуйте найти сами причины зависаний. Обратите внимание на Thread State Trace instrument.

9️⃣ В целом чаще используйте Instruments для обнаружения узких мест приложения.

🔟 Уделяйте внимание метрикам и отчётам непосредственно от приложения.

@iOS Dev — делюсь советами не только для плавного скролла😅
Защита данных в приложениях для iOS

Защита данных — это функция iOS для предотвращения несанкционированного доступа.

Она включается автоматически, после установки активного пароля устройства.

Процессы шифрования и дешифрования выполняются автоматически и аппаратно ускоряются.

Доступно четыре уровня защиты, каждый из которых определяет уровень доступа. При создании файла, iOS автоматически применит дефолтный, если не указано иное.

No protection.
Файл всегда доступен.

Complete until first user authentication.
Это уровень по умолчанию. Файл недоступен до первой разблокировки устройства. После неё файл остается доступным до выключения или перезагрузки устройства.

Complete unless open.
Открыть существующие файлы можно только после разблокировки устройства. Если файл уже открыт, вы можете продолжать получать доступ к нему даже после того, как пользователь заблокирует устройство. Вы также можете создавать новые файлы и получать к ним доступ, пока устройство заблокировано или разблокировано.

Complete.
Доступ к файлу возможен только после разблокировки устройства.

Методы для создания и изменения уровня защиты

1️⃣ Чтобы создать и зашифровать новый файл за один шаг, создайте объект данных с содержимым файла и вызовите метод write(to:options:).

2️⃣ Чтобы изменить уровень защиты данных существующего файла, используйте метод setResourceValue(_:forKey:) для NSURL.

Управление доступом к зашифрованным файлам

В зависимости от уровня защиты файла, попытки прочитать или записать его содержимое могут закончиться неудачей, если пользователь впоследствии заблокирует устройство. Чтобы убедиться, что ваше приложение может получить доступ к файлам, сделайте следующее:

🔘Выберите уровень защиты данных, соответствующий вашим потребностям.

🔘Используйте методы applicationProtectedDataWillBecomeUnavailable(_:) и applicationProtectedDataDidBecomeAvailable(_:) для закрытия и повторного открытия файлов с уровнем защиты completeFileProtection.

@iOS Dev — защищайте файлы пользователей.
Реализация API-клиента для работы с запросами в Swift с использованием Async/Await

Каждый бэкенд имеет свои причуды и обычно требует оптимизированного под него клиента.

📖 В этой статье собраны идеи, которые вы можете использовать для написания клиента, идеально соответствующего вашему бэкенду.

C недавними изменениями Swift автор расскажет, как наиболее эффективно использовать нативные технологии Apple: URLSession, Codable, Async/Await и Actors.

👏 Автор этой статьи — Alex Grebenyuk, инженер-программист с многолетним опытом работы в отрасли.

🛠 Среди его проектов, например, Nuke, Pulse и многие другие, о которых вы, скорее всего, слышали или даже применяли при работе над своими проектами.

@iOS Dev
Использование DateFormatter и оценка его производительности

Если вы работаете с iOS достаточно долго, есть вероятность, что вы знаете как минимум один совет по повышению производительности, касающийся DateFormatter.

💡Создание DateFormatterдорогостоящая операция.

Но насколько именно он медленный, и какие части наиболее неэффективны?

На эти вопросы попробуют помочь ответить две статьи:

📖 Цена использования DateFormatter — примеры и измерения.

📖 Как использовать DateFormatter в Swift.

@iOS Dev
This media is not supported in your browser
VIEW IN TELEGRAM
Добавляем код Swift в качестве собственной команды LLDB

😎 Знаете ли вы, что возможно определить свою собственную команду LLDB, используя исключительно код Swift?

📖 В этом материале автор покажет, что требуется для создания собственной команды LLDB. И вот о чём можно узнать:

1️⃣ Добавление вашей первой команды LLDB.
2️⃣ Добавление команды LLDB с аргументами.
3️⃣ Преобразование сложного кода Swift в команду LLDB.

Простейшую команду и принцип её добавления можно увидеть на гифке выше.

А ещё статью можно рассматривать как дополнение к этому посту.

@iOS Dev
Corner Radius, Shadow, и Border в Swift: создание, использование и комбинирование

📖 В этой статье на простых примерах объясняется, как скруглить углы, применить тень и границы ко всем видам вьюх, включая UIViews, UIButtons и UIImageViews.

Также рассказывается про решение для одновременного использования shadow и corner radius, что для новичков может оказаться непростой задачей.

@iOS Dev
Простой способ работать с асинхронным кодом в Swift Playground

Иногда для того, чтобы проверить то или иное решение, я использую плейграунды. Сложности могут возникнуть, если вы хотите поработать с асинхронным кодом.

Итак, чтобы ваш код сработал, есть решение в одну строку:

💡 Для этого нужно установить значение needIndefiniteExecution в true.

😎 Не забудьте импортировать PlaygroundSupport.

Кстати, чтобы остановить выполнение, можно воспользоваться следующим методом:

PlaygroundPage.current.finishExecution()

@iOS Dev
Как добавить кастомный шрифт в приложение для iOS?

Простой вопрос, казалось бы — но периодически я встречаю его в самых разных чатах. Давайте разберёмся, что нужно сделать, в несколько простых шагов.

1️⃣ Представим, что у нас есть шрифт, который нужно использовать. Добавьте его как обычный файл в ваш проект.

2️⃣ Откройте ваш Info.plist. Если в вашем проекте его нет, то вкладку Info.

3️⃣ Добавьте ключ Fonts provided by application. Нужный шрифт из первого шага необходимо добавить вместе с его расширением.

4️⃣ Пожалуй, самый важный пункт. Название семейства шрифтов может не совпадать с названием файла, скорее всего, так и будет чаще всего. Поэтому нам необходимо получить имя нашего шрифта с помощью следующего метода:

for family in UIFont.familyNames.sorted() {
let names = UIFont.fontNames(forFamilyName: family)
print("Family: \(family) Font names: \(names)")
}


Бонус: вы также можете найти нужное имя, используя приложение «Шрифты» (можно и вбить Font в Launchpad).

Находите нужный и дальше используете его так, как привыкли:

label.font = UIFont(name: "CUSTOM_FONT_NAME", size: 25)

Интересный факт

📖 Lorem ipsum, который выступает заглушкой во всех (ну ладно, почти во всех) текстах на самом деле не что иное как искажённый отрывок из философского трактата Марка Туллия Цицерона «О пределах добра и зла», написанного в 45 году до н. э. на латинском языке.

@iOS Dev — In iOS Dev verĭtas
Как улучшить читаемость крупных чисел в Swift?

💡 Для этого может пригодиться нижнее подчёркивание или же просто _.

При использовании этого символа вы не просто улучшите читаемость кода, но и сократите вероятность ошибки при его вводе.

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

@iOS Dev