Всем привет! Сегодня поговорим про библиотеку для работы с данными - SQLiteData, которая сочетает проверенную надежность SQLite с современным Swift-подходом. В основе библиотеки лежит идея предоставить разработчикам привычные инструменты, подобные тем, что есть в SwiftData, но с более предсказуемым поведением.
Модели данных:
Модели данных описываются с помощью структур и перечислений, что делает код интуитивно понятным и соответствующим духу Swift. Это обеспечивает:
Работа с запросами:
Для выборки данных предлагаются типобезопасные запросы, которые интегрируются в интерфейс через property wrappers. Важно, что эти обертки работают не только в SwiftUI, но и в любых
@Observable моделях, а также в контроллерах UIKit, что обеспечивает универсальность подхода.Интеграция с CloudKit:
Библиотека включает прямой доступ к возможностям CloudKit, обеспечивая:
Это избавляет от необходимости самостоятельной интеграции сторонних решений.
Технологическая основа:
В качестве основы используется SQLite через драйвер GRDB, что гарантирует высокую производительность и отказоустойчивость. Такой выбор технологии означает полный контроль над схемой данных и процессами миграции.
Пример:
// Модель
@Table
struct Task: Identifiable {
let id: UUID
var title: String
var isCompleted: Bool
}
struct TasksView: View {
// Запрос
@FetchAll(Task.where { !$0.isCompleted })
var pendingTasks
var body: some View {
List(pendingTasks) { task in
Text(task.title)
}
}
}
SQLiteData позиционируется как готовое решение для проектов, где важны стабильность, прозрачность работы с данными и современный подход к их обработке. Библиотека предлагает оптимальное сочетание надежности SQLite и современных Swift-практик.
Мобильный трудоголик
Please open Telegram to view this post
VIEW IN TELEGRAM
👍17 9🔥4❤2🤔1
Привет! Хочу порекомендовать канал практикующего staff инженера из Т-Банка.
Обзоры самых актуальных новостей из мира iOS разработки, обзоры нововведений в Swift Evolution, розыгрыши билетов на конференции и просто советы для разработчиков.
➡️ Подписывайтесь на @ios_broadcast
Обзоры самых актуальных новостей из мира iOS разработки, обзоры нововведений в Swift Evolution, розыгрыши билетов на конференции и просто советы для разработчиков.
Please open Telegram to view this post
VIEW IN TELEGRAM
👍10🔥4🗿4❤1
В SwiftUI есть мощнейший инструмент для работы с графикой - Canvas API. Этот инструмент кардинально меняет подход к созданию сложных визуальных эффектов и кастомной графики в приложениях.
Проблемы до Canvas:
До появления Canvas разработчики сталкивались с серьезными ограничениями:
Фактически, для любой нетривиальной графики приходилось покидать комфортную декларативную среду SwiftUI.
Что такое Canvas API:
Canvas - это View в SwiftUI, предоставляющая прямой доступ к низкоуровневому рендерингу:
Canvas { context, size in
// Рисуем эллипс
context.fill(
Path(ellipseIn: CGRect(origin: .zero, size: size)),
with: .color(.blue)
)
// Добавляем текст
context.draw(Text("Hello Canvas"), at: CGPoint(x: size.width/2, y: size.height/2))
}Ключевые возможности:
🔹 GraphicsContext: сердце Canvas, предоставляет:
🔹 Производительность: Canvas работает напрямую с GPU, обеспечивая 60 FPS даже при сложных вычислениях.
🔹 Интеграция с SwiftUI: полная совместимость с анимациями, жестами и системой layout.
Ограничения:
Canvas API - это не просто рядовая фича в SwiftUI, а фундаментальное расширение возможностей фреймворка. Разработчики могут создавать сложную графику и визуальные эффекты, не покидая декларативной парадигмы.
Для проектов, где важна визуальная составляющая: анимированные onboarding-экраны, кастомные графики, интерактивные элементы - Canvas становится обязательным инструментом в арсенале разработчика.
Мобильный трудоголик
Please open Telegram to view this post
VIEW IN TELEGRAM
Forwarded from Кот Денисова
Привет! У каждого из нас есть свои маленькие ритуалы. У кого-то это кофе определенным способом перед началом дня, у кого-то счастливая кружка, а у разработчиков это целый свод неписаных правил, которые, кажется, помогают коду работать.
У меня на столе, например, стоит собранный кубик Рубика и несколько сувениров, купленных в разных странах на удачу. Это не талисманы, конечно, но как-то спокойнее, когда они рядом.
Самые распространенные суеверия в нашей сфере:
Табу на пятничный деплой.
Это не суеверие, а правило выживания. Кто хоть раз разбирал последствия неудачного деплоя в пятницу вечером, тот понимает насколько неприятно исправлять проблемы в выходные, вместо отдыха. Это не суеверие, а скорее горький опыт, возведенный в традицию.
Феномен «озарения у доски».
Не можешь найти ошибку? Начни объяснять ее коллеге или просто проговори вслух и решение придет само. Именно из этого ритуала пошел мем с резиновым утенком. Выглядит со стороны очень забавно, но работает!
Проклятие «легкой задачи».
Стоит только сказать на планировании: «Да это за час сделается!», как задача обязательно превратится в многодневный марафон с переписыванием половины кодовой базы. Закон подлости? Или просто недооценка сложности?
Магический перезапуск.
Не работает? Не читай логи, а просто перезапусти! Не собирается? Перезапусти билд! И ведь часто это помогает!
«Не смотри на процесс сборки - упадет!» или «Смотри пристально, иначе сломается!».
Здесь разработчики делятся на два лагеря. Одни уверены: если ты уйдешь от монитора, пока идет билд, все обязательно сломается. Другие ровно наоборот: стоит только начать пристально следить за консолью, как система тут же выдаст ошибку. Парадокс! Каждому свое.
Почему это работает (или кажется, что работает)?
На самом деле, многие ритуалы - это способ вернуть себе ощущение контроля над сложными и непредсказуемыми системами. Психологи называют это «иллюзией контроля».
Когда мы выполняем ритуальные действия (не смотрим на процесс сборки, перезапускаем без чтения логов пайплайн), мы снижаем тревожность. А спокойный разработчик - это эффективный разработчик.
Please open Telegram to view this post
VIEW IN TELEGRAM
❤14 8👍4🔥1🫡1
Чтобы принять участие, нужно:
Please open Telegram to view this post
VIEW IN TELEGRAM
Telegram
Мобильный трудоголик
🍏 🏋️ Мое приложение для рассылки постов в социальных сетях — Social Poster.
В далеком 2018 году я решил разработать приложение Social Poster (SP) для автоматизации публикации объявлений по продаже б/у смартфонов в ВК группах (барахолках) и чатах объявлений…
В далеком 2018 году я решил разработать приложение Social Poster (SP) для автоматизации публикации объявлений по продаже б/у смартфонов в ВК группах (барахолках) и чатах объявлений…
👍8❤6🔥3 1
Мобильный трудоголик pinned «🛫 Дарим три подписки на полгода в приложении Social Poster - автопостинг по социальным сетям. Чтобы принять участие, нужно: 🔹 Подписаться на канал. 🔹 Нажать кнопку «Участвую!».»
Swift 6.2 принес долгожданную фичу - Raw Identifiers, которая ломает привычные ограничения именования переменных, функций и констант. Теперь мы можем использовать символы, которые раньше были под запретом.
До Swift 6.2 названия не могли:
Теперь достаточно заключить такое название в обратные кавычки (`), и ограничения снимаются!
Пример:
Вместо громоздких конструкций с аннотациями:
@Test("Пользователь нажимает кнопку сохранения без заполненных полей")
func testSaveButtonTapWithEmptyFields() {
}
Можно писать лаконично и понятно:
@Test
func `пользователь нажимает кнопку сохранения без заполненных полей`() {
}
Раньше для числовых значений приходилось искать обходные пути:
enum VideoFormat {
case resolution1080p
case resolution4K
case frameRate24
case frameRate60
}
Теперь называем вещи своими именами:
enum VideoFormat {
case `1080p`
case `4K`
case `24fps`
case `60fps`
}
Использование становится интуитивно понятным:
let format: VideoFormat = .`4K`
let frameRate: VideoFormat = .`60fps`
Важные нюансы:
Raw Identifiers - это не просто синтаксический сахар, а мощный инструмент для улучшения читаемости кода. Особенно полезно в тестировании и работе с внешними API, где точность формулировок критически важна.
Главное использовать новую возможность с умом, сохраняя баланс между выразительностью и практичностью.
Мобильный трудоголик
Please open Telegram to view this post
VIEW IN TELEGRAM
Всем привет! Многие из нас столкнулись с неприятным сюрпризом при обновлении до Swift 6 и новее: старый добрый код внезапно перестал компилироваться. Жесткие проверки конкуренции выявляют проблемы там, где раньше все работало. Особенно болезненно это проявляется при работе с легаси-API от Apple, которые еще не адаптированы под новые требования.
В чем именно проблема:
Возьмем классический пример: создание UIHostingController внутри методов, которые не помечены как
@MainActor. Swift 6 видит потенциальную гонку данных и отказывается компилировать такой код:
class CustomAttachmentViewProvider: NSTextAttachmentViewProvider {
override func loadView() {
// Ошибка компиляции в Swift 6 и новее
let hosting = UIHostingController(rootView: MySwiftUIView())
self.view = hosting.view
}
}
Обычные решения не работают:
Многие пытаются решить проблему стандартными способами:
@MainActor к классу или методу@MainActor in }Но эти подходы либо не решают проблему полностью, либо создают новые сложности с передачей данных между контекстами.
Решение: MainActor.assumeIsolated:
Именно здесь на помощь приходит MainActor.assumeIsolated - метод, созданный специально для таких случаев:
class CustomAttachmentViewProvider: NSTextAttachmentViewProvider {
override func loadView() {
let view = MainActor.assumeIsolated {
let hosting = UIHostingController(rootView: MySwiftUIView())
hosting.view.backgroundColor = .clear
return hosting.view
}
self.view = view
}
}
Особенности работы метода:
Важные замечания:
Подробный пример использования:
class CustomAttachmentViewProvider: NSTextAttachmentViewProvider {
override func loadView() {
view = createViewSafely()
}
private func createViewSafely() -> UIView {
if Thread.isMainThread {
return Self.createHostingView()
} else {
return DispatchQueue.main.sync {
Self.createHostingView()
}
}
}
private static func createHostingView() -> UIView {
MainActor.assumeIsolated {
let hosting = UIHostingController(rootView: MySwiftUIView())
return hosting.view
}
}
}
MainActor.assumeIsolated - это не хаки и не костыли, а официально одобренный Apple способ решать проблемы совместимости в переходный период. Он позволяет сохранить работоспособность старого кода соблюдая строгие правила Swift 6.
Главное - использовать этот инструмент осознанно: проверять выполнение на главном потоке, тестировать краевые случаи и постепенно мигрировать на полностью безопасные подходы. Временные решения должны оставаться временными, даже если они очень эффективны.
Мобильный трудоголик
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥17👍11 5🤯2🙏1👀1
Привет! С выходом iPadOS 26 работа с системным меню-баром вышла на совершенно новый уровень. Теперь iPad получил полноценное меню в стиле macOS с разделами File, Edit, View и другими, но что еще важнее: Apple наконец-то дала разработчикам полноценный инструментарий для тонкой настройки этого меню. Давайте разберемся, какие возможности открываются и как это изменит подход к разработке интерфейсов.
От хаков к нативным решениям:
Раньше кастомизация системного меню требовала нестандартных решений и обходных путей. Теперь в iPadOS 26 появился UIMainMenuSystem - специализированный подкласс UIMenuSystem, предназначенный именно для работы с главным меню-баром. Доступ к нему осуществляется через синглтон:
let configuration = UIMainMenuSystem.Configuration()
UIMainMenuSystem.shared.setBuildConfiguration(configuration) { menuBuilder in
// Ваша логика кастомизации
menuBuilder.remove(menu: .file)
}
Точечный контроль над структурой меню:
Ключевое нововведение - возможность модифицировать меню динамически, без необходимости пересобирать всю структуру целиком. Новые методы UIMenuBuilder позволяют:
Упрощенная настройка через Configuration:
Отдельного внимания заслуживает UIMainMenuSystem.Configuration - класс, который группирует свойства для быстрой настройки основных групп меню:
let configuration = UIMainMenuSystem.Configuration()
configuration.newScenePreference = .removed
configuration.findingConfiguration.style = .search
Это изящная альтернатива ручному удалению элементов через buildHandler.
Гибкая настройка поиска:
Особенно полезным оказалось findingConfiguration. В зависимости от типа приложения можно выбрать один из четырех стилей:
Практические сценарии применения:
Новые возможности особенно полезны для:
Эти обновления знаменуют важный этап в развитии iPadOS, платформа становится еще ближе к macOS, предоставляя разработчикам инструменты для создания понастоящему профессиональных интерфейсов. Это особенно важно для сложных приложений, где контекстно-зависимое меню напрямую влияет на пользовательский опыт.
Мобильный трудоголик
Please open Telegram to view this post
VIEW IN TELEGRAM
Всем привет! Одна из самых критичных задач в iOS-разработке: изменение структуры базы данных без потери пользовательских данных. Представьте: вы выпускаете обновление приложения, а у пользователей пропадают все сохраненные данные. Это катастрофа! К счастью, Core Data предлагает элегантное решение: легкую миграцию. Сейчас разберем, как это работает.
Что такое легкая миграция и когда она возможна:
Легкая миграция - это автоматический процесс, при котором Core Data самостоятельно определяет различия между старой и новой версией модели данных и применяет необходимые преобразования.
Что не поддерживается в легкой миграции:
Использование легкой миграции:
Для успешного автоматического обновления модели данных необходимо выполнить три последовательных действия:
Рассмотрим каждый этап подробно:
Активация механизма миграции:
При использовании современного подхода с NSPersistentContainer дополнительная настройка не требуется, система автоматически включает поддержку легких миграций.
Если же вы работаете с собственной реализацией стека Core Data, потребуется явно указать необходимые опции при подключении хранилища:
let persistentStoreCoordinator = NSPersistentStoreCoordinator(
managedObjectModel: dataModel
)
let migrationOptions: [AnyHashable: Any] = [
NSMigratePersistentStoresAutomaticallyOption: true,
NSInferMappingModelAutomaticallyOption: true
]
do {
try persistentStoreCoordinator.addPersistentStore(
ofType: NSSQLiteStoreType,
configurationName: nil,
at: databaseURL,
options: migrationOptions
)
} catch {
// Обработка неудачной миграции
}
Эти два параметра дают Core Data команду: автоматически определять различия между схемами данных и выполнять преобразования, когда это возможно.
Создание новой версии модели:
Для корректной миграции системе необходимо видеть как исходную, так и целевую версию модели данных.
Важное правило: никогда не редактируйте непосредственно существующий файл .xcdatamodeld после публикации приложения. Вместо этого:
Теперь можно безопасно вносить изменения, не затрагивая структуру, с которой работают пользователи текущей версии приложения.
Редактирование обновленной модели:
Вносите изменения исключительно в новую, только что созданную версию модели. Core Data сможет автоматически построить карту преобразований, если изменения соответствуют определенным шаблонам:
Проверка возможности автоматической миграции:
let canMigrateAutomatically = try? NSMappingModel.inferredMappingModel(
forSourceModel: previousModelVersion,
destinationModel: updatedModelVersion
) != nil
if canMigrateAutomatically {
// Легкая миграция возможна
} else {
// Требуется ручная (тяжелая) миграция
}
Если метод возвращает nil, это означает, что Core Data не может самостоятельно сопоставить версии моделей.
Легкая миграция Core Data - мощный инструмент, который избавляет разработчиков от ручного написания сложных скриптов миграции. При правильном использовании (создание новых версий модели, а не изменение существующих) система надежно перенесет данные пользователей между версиями приложения.
Мобильный трудоголик
Please open Telegram to view this post
VIEW IN TELEGRAM
Привет! Мобильная экосистема стоит на пороге важных изменений. Apple недавно внесла существенные правки в App Review Guidelines, которые кардинально меняют правила игры для разработчиков. Речь идет не просто о технических требованиях, а о фундаментальном принципе: оригинальность против плагиата.
Контекст проблемы: эпидемия клонов.
Вспомните ситуацию с релизом Sora 2 от OpenAI. Через несколько дней после официального запуска App Store заполонили десятки клонов с похожими названиями, иконками и описаниями. Пользователи путались, разработчики оригинального приложения теряли аудиторию, а экосистема деградировала.
Что именно изменилось в правилах:
Нововведения фокусируются на трех ключевых аспектах:
Технические детали обновлений:
Помимо антиплагиатных правил, Apple внесла и другие важные изменения:
Как подготовиться к новым правилам:
Эти изменения - не просто бюрократические правки. Это сигнал о зрелости мобильной экосистемы. Apple признает, что для здорового развития рынка нужны не сотни похожих приложений, а разнообразие уникальных идей.
Для разработчиков это означает переход от тактики «быстрого клонирования» к стратегии «устойчивой оригинальности». В долгосрочной перспективе это выгодно всем: пользователи получают качественные уникальные продукты, а разработчики - справедливые условия конкуренции.
Мобильный трудоголик
Please open Telegram to view this post
VIEW IN TELEGRAM
👍17👀10🤯5🔥2 2
Swift Concurrency с async/await стал стандартом современной iOS-разработки, но вокруг нас все еще живут тонны кода, написанного на completion handlers. Вместо болезненного рефакторинга всего легаси можно построить элегантный мост между старым и новым кодом. Сегодня разберем, как превратить старый код с устаревшим подходом в красивые async-функции.
Два разных мира асинхронности:
Представьте ситуацию: вы пишете современное приложение на Swift Concurrency, но вам нужно использовать библиотеку или системный API, который работает через completion handlers:
// Старый код (до async/await)
func fetchUserData(userId: Int, completion: @escaping (Result<User, Error>) -> Void) {
// Некоторая асинхронная операция
NetworkService.shared.request(.user(id: userId)) { result in
completion(result)
}
}
// Как мы хотим использовать это в новом коде:
let user = try await fetchUserData(userId: 123)
Решение - Continuations переводчики между эпохами:
Swift предоставляет специальные функции-континуации, которые становятся мостом между двумя моделями:
// Новый async-интерфейс поверх старого API
func fetchUserData(userId: Int) async throws -> User {
return try await withCheckedThrowingContinuation { continuation in
// Вызываем старый метод с completion handler
fetchUserData(userId: userId) { result in
switch result {
case .success(let user):
// Пробуждаем async функцию с результатом
continuation.resume(returning: user)
case .failure(let error):
// Пробуждаем с ошибкой
continuation.resume(throwing: error)
}
}
}
}
Типы континуаций и когда что использовать:
Swift предлагает четыре варианта, каждый для своего случая:
Ключевое правило: вызывать resume только один раз.
Самая частая ошибка при работе с континуациями: множественный вызов resume(). Checked-версии помогут отловить это в рантайме:
// Опасный код (может вызвать краш)
func dangerousWrap() async -> String {
await withCheckedContinuation { continuation in
unreliableAPI { value in
continuation.resume(returning: value)
// Если API вызовет completion дважды — будет ошибка
}
}
}
// Безопасная обертка
func safeWrap() async -> String {
await withCheckedContinuation { continuation in
var hasResumed = false
unreliableAPI { value in
guard !hasResumed else { return }
hasResumed = true
continuation.resume(returning: value)
}
}
}
Производительность: Checked vs Unsafe.
Continuation - это не просто синтаксический сахар, а мощный инструмент для постепенной миграции на Swift Concurrency. Они открывают возможность интегрировать легаси-код в современную асинхронную архитектуру, создавать удобные async-интерфейсы для API, работающих через колбэки и контролировать процесс миграции, не переписывая все и сразу.
Мобильный трудоголик
Please open Telegram to view this post
VIEW IN TELEGRAM
Forwarded from Кот Денисова
Всем привет! Сегодня поговорим про собеседования в ИТ - это болезненный и несовершенный процессов. Каждая компания изобретает собственный велосипед, состоящий из хаотичных этапов, непрозрачных критериев и субъективных оценок. В результате кандидаты проходят 5 кругов ада, тратя нервы и время, а компании месяцами не могут закрыть вакансии, теряя сильных специалистов из-за выгорания в процессе найма.
Успех на собеседовании сегодня зависит не столько от реальных компетенций, сколько от умения «продать» себя и удачно угадать, что хочет услышать интервьюер. Абсурдность ситуации достигает пика, когда фронтендера заставляют решать задачи по алгоритмам сортировки, которые он никогда не применял в реальной работе.
Но есть и хорошие новости. Крупные игроки рынка начали осознавать проблему и пересматривать свой подход к найму разработчиков. Вот ключевые изменения, которые стоит отметить:
Устранение дублирования.
Раньше кандидат мог по 3-4 раза проходить одинаковые технические собеседования в разные команды. Теперь введена единая техническая секция, ее результат засчитывается при рассмотрении в любую команду. Это простое решение экономит недели времени для всех участников процесса.
Задачи, приближенные к реальности.
Вместо абстрактных алгоритмических головоломок кандидаты теперь решают прикладные задачи. Например, бэкендеру могут предложить спроектировать API для микросервиса или оптимизировать запрос к базе данных - это то, с чем он сталкивается ежедневно в работе.
Смещение фокуса на архитектурные навыки.
Важно не просто написать работающий код, а объяснить, как он поведет себя под нагрузкой, какие ресурсы потребует и как его масштабировать. Это гораздо ценнее знания специфических фреймворков, которые можно быстро изучить на проекте.
Мое мнение:
Эти изменения не просто «снижение планки». Это признак зрелости рынка. Компании наконец-то поняли, что эффективный найм - это не фильтрация через сито с заведомо мелкими ячейками, а поиск специалистов, релевантных реальным задачам.
Я сам сталкивался с ситуациями, когда сильного разработчика разворачивали из-за неудачи в задаче, которая не имеет отношения к реальным, рабочим задачам. Теперь такие кейсы должны уйти в прошлое.
Что ждет нас дальше?
Очевидно, что тренд на гуманизацию собеседований будет нарастать. Уже сейчас в некоторых компаниях:
Кандидаты становятся более разборчивыми и компании вынуждены подстраиваться, делая процесс найма комфортным и предсказуемым.
Остается надеяться, что вскоре мы окончательно избавимся от архаичных практик вроде заучивания алгоритмов и перейдем к содержательному диалогу о реальных навыках и опыте.
Эти изменения - важный шаг к адекватной оценке разработчиков. Постепенно рынок отказывается от абстрактных задач в пользу реальных навыков. Остается надеяться, что этот тренд станет новым стандартом для всей индустрии.
Please open Telegram to view this post
VIEW IN TELEGRAM
👍15❤9🔥3👀2🤔1 1
Привет! Сегодня разберем паттерн Abstract Factory (Абстрактная фабрика) - мощный инструмент для создания семейств связанных объектов без привязки к конкретным типам.
Зачем это нужно:
Представьте, что вы разрабатываете приложение, которое должно работать с разными темами оформления (светлая/темная) или разными базами данных (SQLite/Realm). Abstract Factory позволяет создавать согласованные наборы объектов, которые гарантированно работают вместе.
Как это работает в Swift:
В отличие от Java/C++, в Swift нет абстрактных классов. Но мы можем использовать протоколы для той же цели.
Пример:
Абстрактные товары:
protocol Coffee {
func brew()
}
protocol Tea {
func steep()
}Конкретные товары:
// Для итальянской кофейни
struct Espresso: Coffee {
func brew() {
print("Варим эспрессо")
}
}
struct ItalianTea: Tea {
func steep() {
print("Завариваем итальянский травяной чай")
}
}
// Для английской кофейни
struct Cappuccino: Coffee {
func brew() {
print("Готовим капучино")
}
}
struct EnglishTea: Tea {
func steep() {
print("Завариваем английский черный чай")
}
}
Абстрактная фабрика:
protocol CafeFactory {
func makeCoffee() -> Coffee
func makeTea() -> Tea
}Конкретные фабрики:
struct ItalianCafeFactory: CafeFactory {
func makeCoffee() -> Coffee {
Espresso()
}
func makeTea() -> Tea {
ItalianTea()
}
}
struct EnglishCafeFactory: CafeFactory {
func makeCoffee() -> Coffee {
Cappuccino()
}
func makeTea() -> Tea {
EnglishTea()
}
}Использование фабрики:
final class CafeOrder {
private let factory: CafeFactory
init(factory: CafeFactory) {
self.factory = factory
}
func prepareCoffeeOrder() {
let coffee = factory.makeCoffee()
coffee.brew()
}
func prepareTeaOrder() {
let tea = factory.makeTea()
tea.steep()
}
}
let italianOrder = CafeOrder(factory: ItalianCafeFactory())
italianOrder.prepareCoffeeOrder()
italianOrder.prepareTeaOrder()
let englishOrder = CafeOrder(factory: EnglishCafeFactory())
englishOrder.prepareCoffeeOrder()
englishOrder.prepareTeaOrder()Преимущества:
🔹 Изоляция кода от конкретных классов.
🔹 Гарантия согласованности объектов.
🔹 Легкое добавление новых вариаций.
🔹 Упрощение тестирования.
Когда не стоит использовать:
🔹 Если у вас только один вариант продуктов.
🔹 Когда семейства объектов часто меняются.
🔹 В простых приложениях без сложной архитектуры.
Abstract Factory - отличный выбор для сложных приложений, где важна согласованность и гибкость архитектуры. Главное преимущество: способность легко добавлять новые семейства объектов без изменения существующего кода.
Мобильный трудоголик
Please open Telegram to view this post
VIEW IN TELEGRAM
Всем привет! Месяц назад Apple провела митап с глубоким разбором производительности SwiftUI. Сегодня обсудим самое важное, что вы могли пропустить.
Суть проблемы: SwiftUI не «тормозит» сам по себе. Проблемы начинаются, когда логика обновлений UI выходит из-под контроля. Каждая лишняя перерисовка View - это работа для системы, которая копится и превращается в лаги.
Что важно знать:
@StateObject для разделяемых данных. Класс с @Observable дает точный контроль: вью обновится только если она читает конкретное измененное свойство. Это резко снижает количество нежелательных перерисовок.@State или запись в Environment в таких местах запускает каскад проверок и перерисовок по всему дереву вьюх. Вместо этого выносите часто меняющиеся данные в отдельные, минимальные по объему вьюхи или передавайте их через Observable‑классы - это ограничит зону обновления только теми компонентами, которые действительно зависят от этих данных.Для достижения плавного интерфейса недостаточно проектировать только внешний вид, необходимо сознательно проектировать поток обновлений данных. Ключевая задача - сделать так, чтобы каждое изменение состояния затрагивало минимально необходимый набор вьюх, а не вызывало каскадную перерисовку.
Как проверить: используйте Instruments с шаблоном SwiftUI. Ваш главный ориентир не общее время выполнения, а столбец Updates. Если видите, что какая-либо вьюха обновляется десятки раз без визуальных изменений - вы нашли точку для немедленной оптимизации.
Мобильный трудоголик
Please open Telegram to view this post
VIEW IN TELEGRAM
2👍18❤9 4 2🙏1
Привет! Swift Concurrency предоставляет мощные инструменты для работы с асинхронными операциями, но понимание времени жизни задач - ключевой аспект, который часто упускают из виду. Давайте разберемся, как разные типы задач управляют своим жизненным циклом.
Structured Concurrency: автоматическое управление:
Structured Concurrency - это фундаментальный принцип, который связывает время жизни задачи с областью ее создания. Когда область видимости завершается, система автоматически отменяет все связанные с ней задачи.
Примеры structured задач:
struct ProfileView: View {
@State private var user: User?
var body: some View {
VStack {
// Задача автоматически отменится при исчезновении View
.task {
user = await loadUserData()
}
}
}
}Преимущество structured подхода - предсказуемость. Вы можете быть уверены, что при выходе из области видимости все дочерние задачи будут корректно отменены.
Unstructured задачи: ручное управление:
Когда вы создаете задачу через Task {} вне structured контекста, вы берете на себя ответственность за ее жизненный цикл. Такие задачи выполняются независимо и требуют явной отмены.
class DataLoader {
private var loadingTask: Task<User, Error>?
func loadUser() {
loadingTask = Task {
try await fetchUser()
}
}
func cancelLoading() {
loadingTask?.cancel()
}
}Важно понимать: вызов cancel() не останавливает задачу мгновенно. Он лишь устанавливает флаг isCancelled, который ваша асинхронная логика должна проверять.
Detached задачи: полная независимость:
Task.detached создает полностью изолированную задачу, которая не наследует контекст родителя, ни приоритета, ни актора, ни состояния отмены. Используйте их осторожно, только когда действительно нужна полная независимость от контекста вызова.
Работа с долгоживущими операциями:
Особого внимания требуют задачи, которые могут выполняться бесконечно долго, например, обработка AsyncStream:
class NotificationService {
private var listenerTask: Task<Void, Never>?
func startListening() {
listenerTask = Task {
for await notification in await notificationStream() {
process(notification)
}
}
}
func stopListening() {
listenerTask?.cancel()
}
}Когда вы отменяете задачу listenerTask, это останавливает только получение уведомлений, но не их генерацию. Источник продолжает создавать уведомления, даже если их никто не обрабатывает.
Чтобы полностью остановить поток данных, нужно управлять обеими сторонами:
Проверка отмены:
Системные API типа URLSession или Task.sleep автоматически проверяют отмену, но в своем коде вам нужно делать это явно:
func processLargeDataset() async throws {
for item in dataset {
try Task.checkCancellation() // Вызывает CancellationError
// или
if Task.isCancelled { return }
await process(item)
}
}Правильное управление временем жизни задач - это фундамент стабильных и эффективных асинхронных приложений. Рекомендуется использовать Structured Concurrency по умолчанию для максимальной предсказуемости поведения. Unstructured задачи стоит применять в тех случаях, когда вам нужен полный контроль над временем жизни операции. Detached задачи подходят только для полностью независимой работы, не связанной с контекстом вызова.
Мобильный трудоголик
Please open Telegram to view this post
VIEW IN TELEGRAM
Swift продолжает расширять границы применения. После успешного освоения серверной разработки, Linux и Windows, язык делает стратегический шаг в embedded-разработку. Теперь на Swift можно писать не только приложения для iPhone, но и программы для микроконтроллеров, датчиков и специализированных устройств.
Суть Embedded Swift:
Embedded Swift - это оптимизированная версия языка, созданная для работы в условиях крайне ограниченных ресурсов. Если обычный Swift полагается на операционную систему и runtime, то Embedded Swift работает напрямую с железом, где каждый байт памяти имеет значение.
Основные отличия от стандартного Swift:
Пример:
import EmbeddedHal
// Настройка пинов как в embedded-проектах
let ledPin = DigitalOutputPin(pin: 13)
let sensorPin = AnalogInputPin(pin: 0)
@main
struct DeviceController {
static func main() async {
while true {
// Чтение данных с датчика
let sensorValue = sensorPin.read()
// Логика управления на основе показаний
if sensorValue > threshold {
ledPin.write(true)
await Task.sleep(milliseconds: 500)
ledPin.write(false)
}
await Task.sleep(seconds: 1)
}
}
}
Области применения Embedded Swift:
Новые языковые возможности для embedded-разработки:
// Прямая работа с разделами памяти
@section(".__DATA, .sensor_data")
@used
var sensorReadings: [Float] = []
// Упрощённая интеграция с C-кодом
@c(readSensorData)
func readSensorData() -> Float {
// Взаимодействие с аппаратным датчиком
return readFromHardware()
}
Сравнение с традиционными подходами:
Язык C/C++ предлагает максимальный контроль и минимальный размер кода, но требует глубокого знания низкоуровневых деталей. Rust обеспечивает безопасность памяти на уровне компилятора, но имеет крутую кривую обучения. Embedded Swift занимает промежуточную позицию - предоставляет более высокоуровневые абстракции, сохраняя приемлемую производительность и размер бинарного файла.
Значение для iOS-разработчиков:
@c делает взаимодействие безопаснее и предсказуемее.Embedded Swift представляет собой не просто расширение функциональности языка, а развитие Swift как универсальной платформы для разработки. От мобильных приложений до серверных решений, от настольных программ до микроконтроллерных систем - один язык начинает охватывать все больше областей применения.
Для разработчиков iOS это открывает возможность войти в мир embedded-разработки, используя уже знакомые инструменты и паттерны. Можно создавать программы для физических устройств, не начиная с изучения совершенно новых языков программирования.
Мобильный трудоголик
Please open Telegram to view this post
VIEW IN TELEGRAM
Всем привет! С выходом iOS 26 работа с уведомлениями в Swift Concurrency кардинально меняется. Старый добрый NotificationCenter, который годами служил верой и правдой, теперь выглядит как отголосок из прошлого - он не понимает модель акторов и постоянно генерирует предупреждения компилятора, но у Apple готово элегантное решение: протоколы MainActorMessage и AsyncMessage. Давайте разберемся, как они работают и почему это настоящий прорыв.
Проблемы старого подхода:
Традиционный NotificationCenter сталкивается с двумя фундаментальными проблемами в Swift Concurrency:
@MainActorMainActorMessage: гарантия выполнения на главном потоке:
Новый протокол решает первую проблему кардинально - он явно указывает, что уведомление должно обрабатываться на главном акторе. Посмотрим на разницу:
// Старый подход
NotificationCenter.default.addObserver(
forName: UIApplication.didBecomeActiveNotification,
object: nil,
queue: .main,
using: { [weak self] _ in
self?.updateUI() // Warning: Call to main actor-isolated method
}
)
// Новый подход
private var observationToken: NotificationCenter.ObservationToken?
// Подписываемся и сохраняем token
observationToken = NotificationCenter.default.addObserver(
of: UIApplication.self,
for: .didBecomeActive
) { [weak self] message in
self?.updateUI() // Без предупреждений
}
// Отмена подписки
deinit {
observationToken?.cancel()
}
AsyncMessage: гибкость для фоновых задач:
Для сценариев, где не требуется главный поток, идеально подходит AsyncMessage. Он доставляет уведомления асинхронно, сохраняя при этом строгую типизацию:
struct DataSyncMessage: NotificationCenter.AsyncMessage {
typealias Subject = SyncResult
let result: Subject
}
// Отправка из любого контекста
let message = DataSyncMessage(result: syncResult)
NotificationCenter.default.post(message)
Внедрение MainActorMessage и AsyncMessage - это не просто следование трендам, а повышение качества кода. Мы получаем не только технические преимущества в виде компиляторных проверок и строгой типизации, но и совершенно иной уровень уверенности в своем коде.
Мобильный трудоголик
Please open Telegram to view this post
VIEW IN TELEGRAM
Всем привет! Переход на Swift Testing - это не просто смена синтаксиса, а изменение парадигмы тестирования. Особенно это касается параметризованных тестов, которые кажутся идеальным решением для замены множества похожих проверок. Но за кажущейся простотой скрываются риски, способные превратить ваши тесты в формальность вместо реального инструмента контроля качества.
Подробнее о проблема:
Проблема 1 - иллюзия покрытия: параметризованные тесты создают ложное ощущение полноты проверок. Рассмотрим классический пример:
@Test(arguments: UserRole.allCases)
func testAccess(role: UserRole) {
#expect(system.hasAccess(role) == true)
}
Что не так с этим подходом:
Проблема 2 - зависимость от порядка и структуры данных: использование CaseIterable для автоматической генерации тестовых данных создает хрупкие зависимости:
enum PaymentMethod: CaseIterable {
case card, applePay, googlePay // Порядок имеет значение!
}
@Test(arguments: PaymentMethod.allCases)
func testPaymentProcessing(method: PaymentMethod) {
// Тест зависит от порядка элементов в enum
}
Последствия:
Проблема 3 - смешивание тестовой логики и проверок: параметризованные тесты часто приводят к появлению условной логики внутри проверок:
@Test(arguments: ProductCategory.allCases)
func testPricing(category: ProductCategory) {
if category == .premium {
#expect(calculatePrice(category) >= 1000)
} else {
#expect(calculatePrice(category) < 1000)
}
}
Что здесь происходит:
Параметризованные тесты в Swift Testing - мощный инструмент, но не панацея. Их слепое применение может привести к обратному эффекту: вместо улучшения покрытия и читаемости вы получите хрупкие, сложные в поддержке проверки, которые маскируют реальные проблемы.
Ключевой принцип: параметризуйте только то, что действительно является вариациями одного и того же сценария. Если тестовые кейсы имеют разную природу, требования или критичность - лучше оставить их отдельными.
Мобильный трудоголик
Please open Telegram to view this post
VIEW IN TELEGRAM
👍16 9❤5🔥2🤔2