Всем привет! Сегодня хочу поговорить об опасной тенденции, которая захватила мир разработки - vibe coding. Это когда ты быстро переключаешься между вкладками, копируешь код из ChatGPT, видишь, что все компилируется, и чувствуешь себя продуктивным. Но на самом деле ты не понимаешь, что происходит.
Что не так с вайбкодингом?
Представьте: вы едете домой и разговариваете по телефону. Через час вы не помните ни дороги, ни поворотов, ни светофоров. Точно так же работает вайбкодинг, все происходит как в тумане.
Типичная ситуация:
Разработчик за вечер создал несколько приложений с помощью ChatGPT. В одном была сложная логика расчетов. Код работал идеально, но разработчик не мог объяснить, как он работает. Это как собрать мебель по инструкции, не понимая, зачем нужны детали.
Почему это опасно для карьеры:
🔸 Вы не становитесь экспертом: знание предметной области часто важнее технических навыков.
🔸 Технический долг растет как снежный ком: вы не понимаете, какие проблемы создаете.
🔸 Вас легко заменить: любой может скопировать код из AI, но не каждый может его осмысленно улучшить.
Что важнее: скорость или понимание?
ИИ-инструменты это мощные помощники. Но когда они заменяют ваше мышление, любопытство и желание разбираться - вы перестаете расти.
Настоящий прогресс приходит через:
🔸 Борьбу с сложными ошибками.
🔸 Понимание КАК и ПОЧЕМУ что-то работает.
🔸 Глубокое погружение в проблему.
Правила осознанного использования ИИ:
Джуны, которые осмысленно работают с ИИ, растут быстрее, чем мидлы, просто копирующие код. Разница в подходе: первые используют ИИ как инструмент, вторые как костыль.
🔸 Используйте ChatGPT для рутины и изучения новых подходов.
🔸 Всегда анализируйте сгенерированный код перед использованием.
🔸 Задавайте вопросы «почему это работает?» и «как можно улучшить?».
🔸 Сохраняйте баланс между скоростью и качеством.
Vibe coding хорош для прототипов и MVP, но только при очень вдумчивом использовании. Если вы не понимаете код, который пишете, вы не можете ему доверять. А если не можете доверять, то не можете нести за него ответственность.
А как вы используете ИИ в работе?
Мобильный трудоголик
Please open Telegram to view this post
VIEW IN TELEGRAM
👍24❤19👀7🔥4🤔2🤯2
Swift Concurrency - это не просто синтаксический сахар. Под капотом происходит сложная трансформация кода, управляемая компилятором и рантаймом. Давайте разберём ключевые компоненты системы.
Компилятор и State Machine.
Когда компилятор встречает async функцию, он разбивает ее на состояния (state machine). Каждый await становится точкой приостановки, между которыми код делится на блоки.
func fetchData() async -> Data {
let data = await downloadData()
let processedData = await process(data)
return processedData
}
Suspension Points (Точки приостановки).
Каждый await это место, где:
🔹 Стек сохраняется в кучу (heap).
🔹 Поток освобождается для других задач.
🔹 Создаётся continuation (информация для возобновления).
Особенности:
🔹 Приостановка не блокирует поток (в отличие от DispatchQueue).
🔹 Swift Runtime управляет жизненным циклом данных.
Continuation (Продолжение).
Это объект, который хранит:
🔹 Точку возобновления (какой await был последним).
🔹 Локальные переменные.
🔹 Что делать после получения результата.
Пример:
withCheckedContinuation { continuation in
someAsyncOperation { result in
continuation.resume(returning: result)
}
}
Executor (Исполнитель).
Определяет где и когда выполнять код после await. Могут быть следующие варианты:
🔹 Главным потоком (для MainActor).
🔹 Глобальным исполнителем (для обычных async функций).
🔹 Кастомным (для акторов).
Особенности:
🔹 Если функция запущена на MainActor, продолжение тоже выполнится на нём.
🔹 Глобальные функции используют кооперативный пул потоков.
Co-operative Thread Pool (Пул потоков).
Особенности:
🔹 Фиксированное количество потоков (обычно = числу ядер CPU).
🔹 Потоки не блокируются: при await они переключаются на другие задачи.
🔹 Управляется Swift Runtime, а не операционной системой.
Почему это эффективно?
🔹 Нет накладных расходов на создание потоков.
🔹 Автоматическая балансировка нагрузки.
Как это работает вместе?
🔹 Мы вызываем async функцию, после чего компилятор создаёт state machine.
🔹 При первом await: состояние и переменные сохраняются в continuation, поток возвращается в пул.
🔹 Когда результат готов, executor решает, где возобновить выполнение.
🔹 State machine продолжает работу с последней точки.
Пример с потоками:
func loadData() async {
// 1. Выполняется на случайном потоке из пула
let data = await fetchFromNetwork() // 2. Поток освобождается
// 3. Возобновляется на том же или другом потоке
await MainActor.run {
self.data = data // 4. Гарантированно на главном потоке
}
}
🔸 Компилятор разбивает код на состояния.
🔸 Continuation хранит прогресс выполнения.
🔸 Executor выбирает поток для возобновления.
🔸 Пул потоков обеспечивает эффективное использование CPU.
Это делает Swift Concurrency легковесным и масштабируемым, в отличие от традиционных потоков.
Мобильный трудоголик
Please open Telegram to view this post
VIEW IN TELEGRAM
❤21 17👍9🔥3🙏3 2
Хотя мое сердце принадлежит Swift и разработке под устройства Apple, я также активно занимаюсь кроссплатформенной разработкой на Flutter. Именно поэтому не могу пройти мимо важнейшей новости из мира Android, которая затронет тысячи разработчиков, включая тех, кто работает с Flutter.
Google объявила о радикальных изменениях в политике безопасности, которые могут навсегда изменить экосистему Android.
Что происходит?
С 2026 года Google вводит обязательную верификацию разработчиков по паспорту. Теперь для подписи APK-файла потребуется:
🔹 Предъявить документ, удостоверяющий личность.
🔹 Пройти проверку в Android Developer Console.
🔹 Оплатить регистрационный взнос $25.
Что это значит на практике?
Для пользователей:
🔹 Установка приложений из сторонних источников усложнится.
🔹 F-Droid, RuStore и другие альтернативные магазины могут прекратить существование.
🔹 Свобода выбора приложений значительно сократится.
Для разработчиков:
🔹 Анонимная разработка станет невозможной.
🔹 Открытые проекты и экспериментальные приложения окажутся под угрозой.
🔹 Российские разработчики могут столкнуться с дополнительными сложностями из-за санкций.
Почему это важно?
Компания Google движется к модели закрытой экосистемы, похожей на Apple. Компания объясняет это борьбой с вредоносным ПО, но на деле получается монополизация рынка.
Под угрозой оказываются:
🔹 F-Droid: крупнейший магазин открытого ПО.
🔹 RuStore: российская альтернатива Google Play.
🔹 Независимые разработчики: те, кто не хочет связываться с Google.
🔹 Опенсорс-проекты: многие из них существуют на энтузиазме.
Технические детали:
Новая система проверки подписи может быть внедрена на уровне ядра Android. Это значит, что неподписанные приложения просто не запустятся на сертифицированных устройствах.
Android постепенно превращается в iOS. Если вы цените свободу выбора и открытость платформы, то сейчас самое время высказать свою позицию. Возможно, массовые обращения разработчиков смогут повлиять на решение Google.
Однако важно понимать, что новые требования не затронут AOSP (Android Open Source Project) и основанные на нем форки. Это создает уникальную ситуацию, при которой Google может потерять свою монополию на Android. Все больше производителей начнут переходить на альтернативные операционные системы, такие как HarmonyOS от Huawei и HyperOS от Xiaomi.
А что думаете вы? Считаете ли вы такие меры оправданными для безопасности или это чрезмерный контроль?
Мобильный трудоголик
Please open Telegram to view this post
VIEW IN TELEGRAM
😁14🤯11👍7🤔4👀3 2
При разработке на Swift часто встаёт вопрос: выбрать перечисления или структуры? Давайте разберёмся, когда какой тип подходит лучше.
🔸 Когда выбирать Enum:
Сейчас рассмотрим несколько вариантов, когда стоит выбрать перечисление:
Конечный набор состояний.
Enums отлично подходят, когда у тебя есть чётко определённые варианты, которые не будут меняться. Классический пример - состояние загрузки:
enum LoadingState {
case idle
case loading
case success(Data)
case failure(Error)
}
Преимущество: компилятор заставит обработать все кейсы в switch, что исключит ошибки.
Взаимоисключающие состояния.
Когда объект может находиться только в одном из четких состояний:
enum AuthState {
case loggedIn(User)
case loggedOut
case guest
}
Здесь невозможно одновременно быть авторизованным и гостем - это гарантирует типобезопасность.
Сопоставление с образцом (pattern matching).
Enums идеальны, когда нужно разное поведение для разных состояний:
switch authState {
case .loggedIn(let user): showProfile(user)
case .loggedOut: showLogin()
case .guest: showLimitedAccess()
}
🔸 Когда Struct работает лучше:
Рассмотрим несколько вариантов, когда стоит выбрать структуру:
Гибкость и расширяемость.
Structs позволяют создавать любые комбинации значений. Например, для тем оформления:
struct AppTheme {
var primaryColor: Color
var secondaryColor: Color
}
Можно легко добавлять новые темы без изменения существующего кода.
Конфигурации и настройки.
Когда нужно много параметров с возможностью кастомизации:
struct UserSettings {
var isDarkMode: Bool
var fontSize: CGFloat
var accentColor: Color
}
Пользователь может комбинировать параметры как угодно.
Сложные составные состояния.
Структуры могут хранить множество связанных данных:
struct NetworkState {
var isConnected: Bool
var connectionType: String
var speed: Double?
var lastError: Error?
}
Ключевые отличия:
🔹 Перечисления: обеспечивают безопасность на этапе компиляции, но ограничены фиксированным набором вариантов. Невозможно добавить новый кейс без изменения всех switch-блоков.
🔹 Структуры: дают гибкость, но требуют ручной проверки валидности данных. Нет гарантии, что все возможные значения будут обработаны.
Выбирайте перечисления, когда работаете с фиксированным набором взаимоисключающих состояний - это даст вам безопасность на этапе компиляции и удобный pattern matching. Структуры же идеалены для гибких конфигураций и сложных данных, которые могут расширяться со временем. Главное - заранее продумать, насколько предсказуемы будут изменения в твоей модели данных. Оба инструмента мощные, просто применяются для разных задач.
Мобильный трудоголик
Please open Telegram to view this post
VIEW IN TELEGRAM
❤22 14👍12🔥1🙏1🫡1
После разработки приложения для отказа от вредных привычек я решил создать что-то новое и необычное для себя. Идея пришла в голову сама, после очередной блокировки без причины на самой популярной доске объявлений в нашей стране. Поэтому родилась идея: сделать удобную альтернативу, без необходимости установки лишних приложений, прямо в Telegram. Так и появился бот бесплатных объявлений: freeBuySellBot.
Конечно, досок объявлений много, но я сделал бота, который работает именно так, как должно работать современное приложение: быстро, удобно и без лишних сложностей.
Что особенного в freeBuySellBot:
🔹 Полноценная доска объявлений прямо в Telegram: не нужно скачивать отдельное приложение.
🔹 Простая регистрация: никаких паролей и подтверждений почты.
🔹 Загрузка фото и редактирование объявлений в пару кликов.
🔹 Бесплатное поднятие объявлений раз в неделю: никаких скрытых платежей.
🔹 Удобный поиск по ключевым словам с пагинацией.
🔹 Избранное и история просмотров: не потеряете интересные предложения.
🔹 Встроенная связь с продавцом: вся коммуникация в одном месте.
Моя цель - получить интересный опыт при работе с новыми для себя технологиями и создать по-настоящему удобную площадку, где не нужно устанавливать дополнительные приложения, проходить сложные регистрации или подписываться на навязанные услуги. Бот уже помогает людям находить интересные вещи и избавляться от нужных, доказывая, что для комфортных сделок не нужны лишние сложности.
В планах добавление системы рейтинга пользователей и категорий объявлений для более удобного поиска.
Разработка:
🔹 Бот написан на JavaScript и Node.js.
🔹 Использовал фреймворк Telegraf для работы с Telegram Bot API.
🔹 База данных: SQLite для хранения пользовательских данных.
Мобильный трудоголик
Please open Telegram to view this post
VIEW IN TELEGRAM
👍14❤6🗿4 4😁3🔥2👏1🫡1
Всем привет! Сегодня разберем одну из самых важных тем в iOS-разработке: эффективное управление памятью. Правильная работа с памятью это то, что отличает профессиональные приложения от посредственных!
Реактивный vs проактивный подход: выбор стратегии.
Существует два принципиально разных подхода к управлению памятью. Большинство команд, особенно в условиях сжатых сроков, действуют реактивно: сначала выпускают продукт, а потом начинают «тушить пожары» когда пользователи жалуются на вылеты и тормоза. Распространенный случай, когда одна-единственная анимация Lottie умудрялась «съедать» целый гигабайт памяти только потому, что дизайнер экспортировал все кадры как отдельные bitmap-изображения!
Однако есть более зрелый подход: проактивная разработка. Это философия, при которой вы с самого начала проекта закладываете правильные практики:
🔹 Систематически используете defer для гарантированной очистки ресурсов.
🔹 Тщательно подбираете и оптимизируете графические ассеты.
🔹 Продумываете стратегию кэширования для разных типов данных.
🔹 Осознанно выбираете между value types и reference types.
🔹 Регулярно проводите профилирование в Instruments.
Практические советы по оптимизации:
Изображения - главный пожиратель памяти.
Золотое правило: никогда не загружайте изображения в полном разрешении если они отображаются в уменьшенном виде. Особенно это актуально для галерей и коллекций. Используйте фреймворк Image I/O для создания миниатюр, он позволяет декодировать изображения с точным контролем размера. Всегда применяйте асинхронную загрузку и не забывайте о метаданных, зачастую EXIF-данные занимают больше места чем само изображение!
Core Data и управление транзакциями.
Здесь важно найти баланс. Слишком частые сохранения создают нагрузку на диск и снижают производительность, а слишком редкие приводят к накоплению изменений в памяти. В больших транзакциях это может вылиться в десятки мегабайт! Экспериментируйте с разными стратегиями, иногда оптимальным решением оказывается batch-обработка с сохранением каждые 100-200 изменений.
Умное кэширование с NSCache.
NSCache это ваш верный союзник в борьбе за память, но важно понимать его философию. В отличие от обычных словарей, NSCache автоматически очищается при memory pressure, но только если вы правильно его настроили:
🔹 Всегда устанавливайте разумные лимиты по количеству объектов и суммарному размеру.
🔹 Используйте evictsObjectsWithDiscardedContent = true для автоматической очистки.
🔹 Помните что ключи должны быть наследниками NSObject.
🔹 Для сложных объектов реализуйте протокол NSDiscardableContent.
Lazy-загрузка ваш лучший друг.
В SwiftUI обязательно используйте LazyVStack и LazyHStack вместо обычных контейнеров, они создают элементы интерфейса только при попадании в область видимости. В коде применяйте lazy var для тяжеловесных объектов которые могут не понадобиться, например, кэшированных изображений, форматировщиков дат, или сложных вычислительных объектов.
Структуры вместо классов где это возможно.
Память для структур и перечислений выделяется на стеке и не создают проблем с подсчетом ссылок. Для моделей данных, конфигураций, параметров отображения, везде где не требуется передавать ссылку на объект, используйте value types. Это не только упрощает многопоточность, но и делает потребление памяти более предсказуемым.
Используйте инструменты отладки.
Memory Graph Debugger в Xcode это мощнейший инструмент который показывает не только утечки, но и циклические ссылки. Научитесь читать его отчеты как открытую книгу! Не забывайте про Allocations в Instruments, он помогает отслеживать общее потребление, а Leaks специализируется именно на утечках памяти.
Самое важное: начать думать о памяти с самого начала проекта. Реактивный подход всегда оказывается дороже и болезненнее. Регулярное профилирование, правильный выбор инструментов и понимание «под капотом» - вот ключ к созданию стабильных и отзывчивых приложений.
Мобильный трудоголик
Please open Telegram to view this post
VIEW IN TELEGRAM
🙏25👍16❤13🔥3🤝3 2 1
За годы работы в IT я собрал советы, которые значительно ускорили бы мне рост на старте карьеры.
Никогда не поздно сменить направление.
Многие остаются в нелюбимом направлении из-за комфортной зарплаты или страха начать заново. Но самый большой рост происходит именно тогда, когда вы слушаете себя, а не внешние обстоятельства. Если чувствуете, что перестали развиваться - это знак, что пора что-то менять.
Я начинал с web-разработки на PHP, но со временем понял, что мобильная разработка для меня гораздо интереснее.
Инструменты меняются, принципы остаются.
Не зацикливайтесь на конкретных технологиях. Языки и фреймворки приходят и уходят, а фундаментальные принципы, такие как архитектура, алгоритмы, проектирование остаются актуальными годами. Умение адаптироваться ценнее знания конкретного стека.
Командная работа - вот настоящая суперсила.
Современная разработка невозможна в одиночку. Системы контроля версий, ревью кода, CI/CD - это не просто инструменты, а необходимость. Чем раньше вы освоите культуру совместной работы, тем быстрее будете расти.
Умение искать информацию важнее запоминания.
Не пытайтесь держать все в голове. Ключевой навык это быстро находить и фильтровать информацию. Правильный запрос в поисковике или чат-боте часто ценнее часа чтения документации.
Практика единственный путь к мастерству.
Теория дает основу, но настоящие навыки приходят только с практикой. Не бойтесь экспериментировать и делать ошибки, именно так формируется настоящий опыт.
Баланс не роскошь, а необходимость.
Выгорание не признак профессионализма. Регулярный отдых и перезагрузка это инвестиция в вашу продуктивность и креативность. Качество работы важнее количества часов.
Широкий взгляд важнее узкой специализация.
Программирование это не только написание кода. Понимание бизнес-контекста, коммуникация с командой, умение работать с требованиями часто важнее технических деталей.
Незнание чего-либо - это нормально.
В IT невозможно знать все, технологии развиваются слишком быстро. Нормальное восприятие состояния «я этого не знаю, но научусь» - ключ к постоянному росту.
Делайте только то, что вам нравится.
Заставлять себя работать это путь в никуда. Мозг саботирует неинтересные задачи. Найдите то, что приносит удовольствие, делайте это хорошо, получайте признание = любите работу еще больше.
Карьера в IT - это марафон, а не спринт, где важно сохранять гибкость мышления и готовность к изменениям. Самые успешные специалисты не те, кто знает все, а те, кто умеет адаптироваться, сотрудничать и постоянно учиться. Найдите то, что приносит вам искреннее удовольствие, и не бойтесь менять направление, именно это приводит к настоящему профессиональному удовлетворению и росту.
Мобильный трудоголик
Please open Telegram to view this post
VIEW IN TELEGRAM
👍30❤17 9🔥5🙏4👏2👀1
Всем привет! Сегодня поговорим о Derived Data - одной из самых загадочных, но важных папок в жизни iOS-разработчика. Хотя мы редко взаимодействуем с ней напрямую, именно здесь Xcode хранит кэш для ускорения сборки проектов.
Обсудим основные ошибки, которые совершают разработчики при работе с Derived Data:
Непонимание предназначения папки.
Derived Data это не просто папка для мусора. Здесь хранится:
🔹 Кэш компиляции модулей.
🔹 Информация о Swift packages.
🔹 Символы для отладки.
🔹 Собранные бинарные файлы.
Папки с суффиксом
.noindex (ModuleCache, SDKStatCaches, SymbolCache) лучше не трогать без необходимости. Хотя иногда их очистка помогает решить проблемы с симулятором, в 90% случаев достаточно работать только с папкой конкретного проекта.Ручной поиск папки.
Не ищите Derived Data вручную через Finder! Есть простой способ:
Xcode -> Settings -> Locations -> Derived Data
Маленькая синяя стрелка - это кнопка, которая мгновенно откроет папку в Finder.
Удаление папки Derived Data.
Классическая ошибка, когда Xcode «заглючил», многие удаляют всю папку Derived Data. Правильный подход:
🔹 Сбросить настройки симулятора.
🔹 Сделать Clean Build в Xcode.
🔹 Удалить ТОЛЬКО папку проблемного проекта.
Удаляя всю Derived Data, вы заставляете Xcode пересобирать ВСЕ проекты с нуля, что может занять много времени.
Игнорирование метрик сборки.
Это особенно критично для команд. Представьте:
🔹 Сборка замедлилась на 30 секунд.
🔹 В команде 20 разработчиков.
🔹 Каждый делает 10 сборок в день.
Итого: команда теряет полтора часа в день!
Решение: использование инструменты вроде RocketSim Team Insights для мониторинга:
🔹 Время типичной и максимальной сборки.
🔹 Разницу между чистыми и инкрементальными сборками.
🔹 Влияние версий Xcode и macOS на производительность.
Эти данные помогают обосновать покупку новых MacBook для команды, иногда это дает до 30% прироста скорости!
Отсутствие анализа сборок.
В подпапках проекта в Derived Data лежит собранное приложение. Если заглянуть внутрь (Show Package Contents), то можно обнаружить следующие папки и ресурсы:
🔹 Resources: могут содержать неиспользуемые ассеты.
🔹 Frameworks: иногда там остаются ненужные зависимости.
🔹 Bundles: сторонние библиотеки могут добавлять лишние ресурсы.
Это напрямую влияет на размер приложения! Однажды нам удалось уменьшить размер приложения на 70 МБ, благодаря удалению неиспользуемых ресурсов и лишних библиотек.
Неиспользование кэша для CI/CD.
Многие команды напрасно игнорируют Derived Data в процессах непрерывной интеграции. При каждой сборке на CI-сервере Xcode заново компилирует все зависимости. Правильный подход: кэшировать папку Derived Data между сборками, особенно:
🔹 Кэш компиляции Swift Packages.
🔹 Скомпилированные модули зависимостей.
🔹 Кэш Asset Catalog.
Игнорирование проблем с инкрементальной сборкой.
Если вы замечаете, что Xcode постоянно пересобирает файлы, которые не менялись - проблема часто кроется в Derived Data. Типичные причины:
🔹 Поврежденный кэш модулей.
🔹 Неправильные временные метки файлов.
🔹 Конфликт версий Swift компилятора.
Решение: использовать флаг
-driver-show-incremental в настройках сборки, чтобы увидеть, что именно заставляет Xcode пересобирать файлы. Это помогает выявить проблемы с зависимостями и оптимизировать структуру проекта.Derived Data не враг, а мощный инструмент для оптимизации процесса разработки. Правильная работа с ним экономит часы и нервы.
Мобильный трудоголик
Please open Telegram to view this post
VIEW IN TELEGRAM
Всем привет! Сегодня хочу поговорить о самой изматывающей части работы разработчика: поиске багов, которые возникают в самых неожиданных условиях. Когда проблема есть только у одного пользователя, воспроизводится раз в полгода и вообще выглядит как мистика.
Загадка с Face ID и темной темой:
Недавно мы получили странный баг-репорт: «Приложение зависает при попытке войти через Face ID». Особенность была в том, что проблема возникала только у пользователей с включенной темной темой и только на iPhone 14 Pro.
Логи показывали стандартный поток аутентификации, но при вызове Face ID приложение просто замирало. Оказалось, что кастомная анимация загрузки конфликтовала с системным модальным окном Face ID в темной теме. Система пыталась применить темную тему к нашему кастомному компоненту, что приводило к бесконечному циклу обновления UI.
Проблема с арабским языком и скроллом:
Другой интересный случай: пользователь жаловался на дергающийся скролл в списке. Особенность: устройство было настроено на арабский язык с направлением текста справа налево.
Мы тестировали приложение с английским и русским языками, но забыли про RTL-локации. Оказалось, что кастомный индикатор скролла неправильно рассчитывал позицию при RTL-раскладке, из-за чего происходило постоянное перерисовывание контента.
Режим энергосбережения как тормоз:
Классика: жалобы на тормозящее приложение, когда у телефона меньше 20% заряда и включен режим энергосбережения. Система ограничивает производительность, а пользователь винит наше приложение.
Slide Over и потерянный контент:
Еще одна история: пользователь сообщал, что в режиме Slide Over на iPad пропадает часть контента. При обычном использовании все работало идеально.
Проблема была в том, что мы не учитывали изменение safe area insets в компактном режиме. Контент просто уезжал за границы видимой области, но только при определенной ширине Slide Over.
Загадка в подвале:
Несколько лет назад к нам пришла жалоба: «Приложение не работает вообще». Никакие данные не грузятся, картинки не отображаются. По логам полная чистота. У пользователя нет аномалий, мы перепробовали все сценарии.
И тогда мы задали простой вопрос: «А где вы находились, когда это произошло?».
Ответ оказался неожиданным: «в подвале». Человек решил проверить работу приложения в под землей, где не ловил ни Wi-Fi, ни мобильный интернет. Всё встало на свои места!
🔸 Контекст важнее технических данных: иногда нужно спросить не «что делали?», а «где и при каких условиях?».
🔸 Логи не всесильны: они показывают только то, что мы предусмотрели.
🔸 Пользователи живут в другом мире: их сценарии использования часто отличаются от наших, тестовых.
🔸 Тестируйте неочевидные сценарии: темная тема, разные языки, специальные возможности.
🔸 Учитывайте особенности платформы: Slide Over, Split View, Picture-in-Picture.
Разрабатывайте не только для идеальных условий. Пользователи будут использовать ваше приложение в самых неожиданных местах и ситуациях. И иногда самое сложное не пофиксить баг, а найти его причину.
Мобильный трудоголик
Please open Telegram to view this post
VIEW IN TELEGRAM
👍21❤13🔥13😁4👀3 2🙏1
Здравствуйте, друзья! Сегодня мы разберем одну из основ работы с коллекциями в Swift: тип данных Set и протокол Hashable. Это критически важно для понимания, как Swift управляет уникальностью объектов.
Set это неупорядоченная коллекция, которая хранит только уникальные элементы. Но как Swift определяет, что объект уникальный? Всё основано на двух столпах: хэш-значении и операции сравнения.
Что происходит под капотом при добавления в Set.
Когда мы пытаемся добавить новый элемент, Swift проделывает два шага:
🔹 Вычисляет hashValue объекта. Это своего рода цифровой «отпечаток».
🔹 Сравнивает этот отпечаток с существующими с помощью оператора == (равенства).
Если хэш уникальный, объект считается новым и добавляется в коллекцию. Если же хэш совпал с одним из уже имеющихся, Swift делает дополнительную проверку через ==, чтобы убедиться, что это не просто коллизия, а действительно один и тот же элемент.
Пример.
Допустим, у нас есть структура Room (Комната) с координатами на карте:
struct Room {
let position: (x: Int, y: Int)
}
Чтобы добавить такие комнаты в Set, мы должны подписать структуру под протокол Hashable.
extension Room: Hashable {
// Проверка на равенство
static func == (lhs: Room, rhs: Room) -> Bool {
// Две комнаты равны, если их координаты идентичны
return lhs.position.x == rhs.position.x && lhs.position.y == rhs.position.y
}
// Генерация хэша
func hash(into hasher: inout Hasher) {
hasher.combine(position.x)
hasher.combine(position.y)
}
}
Что такое хеш-коллизия и почему она страшна?
Коллизия хеша это ситуация, когда два разных объекта имеют одинаковый хеш.
Чем плохи коллизии?
🔹 Производительность: каждая коллизия заставляет Swift делать дополнительную проверку через ==, что замедляет работу с коллекцией.
🔹 Логические ошибки: плохой хеш-алгоритм может создать множество коллизий, превращая Set из быстрой структуры данных в медленную.
Почему это так важно?
🔹 Метод == позволяет задать логику проверки равенства пользовательских типов.
🔹 Метод hash(into:) создает тот самый «отпечаток». Мы «смешиваем» в хэшере оба значимых поля: x и y. Почему не только x? Потому что это привело бы к коллизиям: комнаты с координатами (1, 1) и (1, 2) имели бы одинаковый хэш, и одна из них не попала бы в Set, хотя они разные!
Всегда, когда вы реализуете Hashable, ваша задача: гарантировать, что если два объекта равны (== возвращает true), то и их хэш-значения должны быть идентичными. Обратное утверждение (если хэши равны, то и объекты равны) не всегда верно из-за возможных коллизий, но хороший хэш-алгоритм сводит их к минимуму.
Используйте combine для всех значимых свойств, которые участвуют в проверке на равенство. Это залог корректной и эффективной работы ваших коллекций.
Мобильный трудоголик
Please open Telegram to view this post
VIEW IN TELEGRAM
👍22❤15 10🙏4🔥1👏1 1
Всем привет! Сегодня поговорим о тернарном операторе в Swift. Это одна из тех фич, которые делают код лаконичнее, но требуют аккуратного использования.
Тернарный оператор - это компактная замена конструкции if-else когда нужно просто выбрать одно из двух значений. Синтаксис такой:
<условие> ? <значение если true> : <значение если false>
Тернарный оператор идеально подходит для простых присваиваний:
// Без использования тернарного оператора
let accessLevelText: String
if user.isAdmin {
accessLevelText = "admin"
} else {
accessLevelText = "user"
}
// С использованием тернарного оператора
let accessLevelText = user.isAdmin ? "admin" : "user"
Тернарные операторы в SwiftUI.
В SwiftUI это вообще мастхэв для условного применения модификаторов:
Text("Привет!")
.foregroundColor(isError ? .red : .primary)
Вложенные тернарные операторы.
Не стоит использовать вложенные тернарные операторы:
let priceColor = isOnSale ? (isAlmostSoldOut ? .red : .orange) : .black
Такой код превращается в головоломку. Лучше использовать обычный if-else или вынести логику в вычисляемое свойство.
Альтернатива: If-expression.
В Swift 5.9 появились if выражения, которые часто читаются лучше чем тернарные операторы:
// Тернарный оператор
let status = isActive ? "online" : "offline"
// If-expression (более читаемо для сложных случаев)
let status = if isActive {
"online"
} else {
"offline"
}
Тернарный оператор - это отличный инструмент, который экономит место и время. Но как с любым мощным инструментом, главное не переусердствовать и помнить о читаемости кода.
Мобильный трудоголик
Please open Telegram to view this post
VIEW IN TELEGRAM
👍25 13❤10🔥6🙏1🤝1 1
Здравствуйте, друзья! Сегодня мы поговорим на одну из самых болезненных, но важных тем в карьере любого разработчика: о истинных причинах увольнений.
В 90% случаев увольнение это не внезапное решение, а накопительный эффект. Последний проступок просто становится формальным поводом для того, что назревало месяцами.
Причины вне вашего контроля (самые частые).
Это то, на что вы вообще не можете повлиять:
🔹 Внезапное сокращение финансирования проекта.
🔹 Реорганизация компании/отдела.
🔹 Приход нового руководства со «своей командой».
🔹 Слияния и поглощения.
🔹 Стратегическая смена направления компании.
Корпоративный мир жесток, вы как пешка на шахматной доске. Решения принимаются на уровнях, до которых вы просто не дотягиваетесь.
Ошибка найма.
Если увольняют во время испытательного срока, то скорее всего, это ошибка рекрутера или технического интервьюера. Они не смогли правильно оценить ваши навыки или ошибочно решили что вы идеально вольетесь в команду.
Неоправданные ожидания.
Бывает, когда нанимают на позицию с перспективой роста (например, в тимлида), но за полгода-год роста не происходит. Или когда обещали быстро освоить новую технологию, но не получилось.
Иногда в данной проблеме виноваты обе стороны: работодатель не озвучивает ожидания четко, а сотрудник не умеет трезво оценивать свои силы.
Нарушение доверия.
Что не любят работодатели больше всего:
🔹 Откровенная ложь на собеседовании (указал 3 года опыта вместо 6 месяцев).
🔹 Нарушение NDA - даже безобидный репост сообщения из рабочего чата в чат с друзьям или отправка куска рабочего кода в Stack Overflow.
🔹 Регулярные больничные или дни без содержания, когда горят сроки.
🔹 Работа на несколько компаний одновременно без предупреждения.
В IT доверие - это ваша главная валюта. Его легко потерять и почти невозможно восстановить.
Попытка заменить универсального специалиста.
Такое происходит когда уволился уникальный специалист, который все знал, умел и совмещал несколько направлений и зон ответственности. На его место берут нового человека и ожидают, что он сразу закроет все пробелы. Когда это не происходит, его увольняют.
Это вина со стороны работодателя, нормальные компании распределяют знания и ответственность, а не ищут нового «гения».
Проблемы с софт-скиллами.
За токсичное поведение увольняют быстрее, чем за пробелы в технических знаниях. Что сюда относится:
🔹 Пассивно-агрессивное общение.
🔹 Распространение слухов.
🔹 Неуважение к коллегам.
🔹 Постоянные конфликты.
Часто вместо настоящей причины придумывают проблемы с хард-скиллами, потому что говорить правду о поведении неудобно.
Профессиональная некомпетентность.
Опытному разработчику непозволительно постоянно допускать ошибки, которые типичны для новичка:
🔹 Постоянно ломать билд и блокировать команду.
🔹 Регулярные ошибки с базовыми инструментами проекта и проблемы с их изучением.
🔹 Писать код, который постоянно требует переписывания.
За чистую техническую некомпетентность увольняют реже всего, обычно таким сотрудникам пытаются помочь менторингом и обучением.
Рекомендации при увольнении.
Не принимайте увольнение близко к сердцу, зачастую это не ваша вина. Вот что стоит делать, если это все таки произошло:
🔹 Не паникуйте: это бизнес, а не личное оскорбление.
🔹 Возьмите паузу: 3-5 дней на восстановление ментального состояния.
🔹 Учитесь на ошибках: проанализируйте, что можно было сделать иначе и что следует изучить.
🔹 Обновите резюме: чтобы оно содержало самую актуальную информацию.
🔹 Свяжитесь со знакомыми: напишите всем, кого знаете из вашей сферы, ведь большинство хороших вакансий не размещается в публично.
🔹 Откликайтесь на вакансии: поиск новой работы займет время, но компетентные, адекватные, обучаемые специалисты всегда в цене.
В IT-рынке всегда есть спрос на хороших специалистов. Главное извлекать уроки из каждой ситуации и не повторять ошибок. Даже если одна дверь закрылась, то впереди десятки других, часто даже лучше.
Мобильный трудоголик
Please open Telegram to view this post
VIEW IN TELEGRAM
👍22👀16🗿11🙏4❤1🤔1
Здравствуйте, друзья! Сегодня разберем одну из самых интересных тем в Swift: как работают протоколы на низком уровне. Если вы думаете, что протоколы это просто синтаксический сахар, приготовьтесь удивляться!
Что такое Existential Container?
Когда мы объявляем переменную с типом протокола:
var item: MyProtocol = MyStruct()
Компилятор создает специальную сущность: Existential Container. Это «коробка», которая хранит:
🔹 Само значение.
🔹 Метаданные о типе.
🔹 Таблицу методов протокола.
Как устроена память?
На 64-битных системах Existential Container занимает 40 байт (5 машинных слов):
🔹 24 байта - данные значения.
🔹 8 байт - указатель на метаданные типа (VWT).
🔹 8 байт - указатель на таблицу методов (PWT).
Inline vs Outline хранение.
Swift умный! Если ваша структура помещается в 24 байта, то данные хранятся прямо в буфере Existential Container'а (inline):
struct MyStruct: MyProtocol {
let a: Int
let b: Int
let c: Int
}Если данные больше 24 байт, то создается объект в куче и в буфер контейнера сохраняется только указатель на эту память (outline).
Value Witness Table (VWT).
Это «инструкция по эксплуатации» для типа. VWT содержит функции:
🔹 initialize - создание значения.
🔹 copy - копирование.
🔹 destroy - очистка памяти.
Для тривиальных типов (Int, Double) многие операции пустые заглушки!
Protocol Witness Table (PWT).
Вот где магия! PWT связывает методы протокола с конкретными реализациями:
protocol Drawable {
func draw()
}
struct Circle: Drawable {
func draw() {
}
}Компилятор создает таблицу, где draw() указывает на реализацию в Circle.
Где находятся VWT и PWT?
Value Witness Table (VWT) и Protocol Witness Table (PWT) хранятся в сегменте данных программы - это статическая память, которая существует на протяжении всего времени жизни приложения.
🔹 VWT создается для каждого типа на этапе компиляции.
🔹 PWT создается для каждой пары «тип-протокол» на этапе компиляции.
🔹 Existential Container содержит только указатели на эти таблицы.
Особенности Existential Container:
🔹 Existential Container более ресурсоемкий чем Generic.
🔹 Большие структуры хранятся в куче.
🔹 Возможно менять типы в runtime.
Existential Container - это мощный механизм, но не бесплатный. Понимание его работы помогает писать более эффективный код.
Мобильный трудоголик
Please open Telegram to view this post
VIEW IN TELEGRAM
Если вы уже обновились до macOS 26 Tahoe и новый дизайн Liquid Glass вызывает у вас не восторг, а раздражение, то у меня для вас хорошие новости. Оказывается, Apple оставила лазейку для возврата к привычному интерфейсу!
Новый стиль Liquid Glass (внутреннее название Solarium) это:
🔹 Увеличенная прозрачность элементов.
🔹 Новые анимации окон.
🔹 Измененные иконки и шрифты.
🔹 Обновленная панель Dock.
Но не всем нравятся радикальные изменения. К счастью, есть способ вернуть всё как было.
Как отключить Liquid Glass:
🔹 Открываем «Терминал» (через Spotlight или в папке Утилиты).
🔹 Вводим команду:
defaults write -g com.apple.SwiftUI.DisableSolarium -bool TRUE
🔹 Нажимаем «Enter».
🔹 Перезагружаем Mac.
Что изменится:
🔹 Системные элементы вернутся к стилю macOS 15.
🔹 Стандартные приложения (Safari, Mail, Календарь) тоже обновятся.
🔹 Исчезнет излишняя прозрачность.
🔹 Вернутся привычные тени и границы окон.
Важные нюансы:
🔹 Dock и пункт управления могут остаться полупрозрачными.
🔹 Новые функции macOS 26 сохранятся, изменится только внешний вид.
🔹 Сторонние приложения могут потребовать отдельного обновления.
Если передумаете:
Вернуть Liquid Glass можно той же командой, но с FALSE:
defaults write -g com.apple.SwiftUI.DisableSolarium -bool FALSE
Apple дала нам выбор, это редкая ситуация для компании, известной своим принципом «мы знаем лучше». Воспользуйтесь этой возможностью, если новый дизайн вам не подходит.
А вы уже обновились до macOS 26?
Мобильный трудоголик
Please open Telegram to view this post
VIEW IN TELEGRAM
👍23❤13🔥7 2😁1🙏1
Всем привет! Сегодня поговорим о Swift макросах: одной из самых мощных фич в Swift. Эта функциональность появилась относительно недавно, но уже успела показать себя как инструмент, который может кардинально изменить подход к написанию кода.
Зачем нужны макросы?
Представьте: вы постоянно пишете однотипный текст с одинаковым стилем в SwiftUI. Код повторяется, а любое изменение темы требует правки в десятках мест. Макросы решают именно эту проблему!
Пример:
// Вместо этого громоздкого блока кода, где нужно прописывать
// все модификаторы вручную для каждого заголовка:
Text("Привет, мир!")
.font(.title)
.fontWeight(.bold)
.foregroundColor(.primary)
.multilineTextAlignment(.center)
.padding(.bottom, 8)
// Мы можем написать просто одну строку с макросом,
// который автоматически развернется во всю необходимую верстку:
@TitleText("Привет, мир!")
Как это работает под капотом.
Создаем объявление макроса, которое говорит компилятору,
что когда он встречает
@TitleText, нужно обратиться к внешней реализации:
@freestanding(expression)
public macro TitleText(_ content: String) -> some View = #externalMacro(
module: "MacroToolsMacros",
type: "TitleTextMacro"
)
И саму реализацию макроса, которая генерирует конечный код:
public struct TitleTextMacro: ExpressionMacro {
public static func expansion(
of node: MacroExpansionExprSyntax,
in context: some MacroExpansionContext
) throws -> ExprSyntax {
// Проверяем, что макросу передали строковый аргумент
guard let firstArg = node.argumentList.first?.expression else {
throw CustomMacroError("Нужен текст для заголовка")
}
// Возвращаем готовый код, который заменит вызов макроса
// Компилятор буквально подставит этот текст вместо @TitleText("Привет, мир!")
return """
Text(\(firstArg))
.font(.title)
.fontWeight(.bold)
.foregroundColor(.primary)
.multilineTextAlignment(.center)
.padding(.bottom, 8)
"""
}
}
Что происходит при компиляции:
🔹 Компилятор встречает
@TitleText("Привет, мир!")🔹 Вызывает
TitleTextMacro.expansion()🔹 Получает сгенерированный код с полноценным текстом
🔹 Подставляет этот код вместо вызова макроса
Более сложный пример:
// Превращается в полноценный Text с форматированием
@GreetingText("Мобильный трудоголик")
Здесь макрос не просто подставляет готовый шаблон, а анализирует переданный текст и генерирует разное форматирование в зависимости от содержимого. Например:
🔹 Если передано имя пользователя: добавляет иконку человека.
🔹 Если передана профессия: применяет специальное форматирование.
🔹 Анализирует длину текста и выбирает оптимальный размер шрифта.
🔹 Может добавлять дополнительные элементы (эмодзи, разделители).
Такой макрос содержит сложную логику разбора и преобразования текста, а не просто подставляет статический шаблон.
Плюсы использования макросов:
🔹 Ускорение разработки: меньше шаблонного кода.
🔹 Единообразие UI: все кнопки выглядят одинаково.
🔹 Компиляция быстрее: макросы раскрываются на этапе компиляции.
Минусы:
🔹 Дебаггинг превращается в ад: ошибки показываются в сгенерированном коде.
🔹 Инструменты рефакторинга не работают: Xcode не видит внутрь макросов.
🔹 Сложность написания: нужно разбираться в SwiftSyntax.
Макросы - это как суперспособность. Мощно, но опасно. Используйте их там, где действительно нужно убрать повторяющийся код и стандартизировать UI.
Мобильный трудоголик
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥24👍15❤9 2🙏1
Forwarded from Кот Денисова
Всем привет! Недавно я писал пост с иронией про новый скилл разработчиков: чистку кода от вайбкодинга. Оказалось все куда серьезнее и это не ирония, а реальность рынка 2025 года.
Что происходит?
Компании начали нанимать дорогих разработчиков для одной задачи - чистить код, который сгенерировали нейросети. Появились целые продукты, бизнес которых построен на приведении AI-кода в человеческий вид.
Три главные проблемы ИИ-генерации кода:
Неэффективность и изобретение велосипедов.
Нейросети прямолинейны как трамвай. Если в проекте уже есть функция умножения двух чисел, ИИ напишет новую. Если есть готовый хелпер для работы с датами, создаст свой.
Результат: дублирование кода, раздутые билды и архитектурный хаос.
Проблемы безопасности.
Нейросети игнорируют базовые правила безопасности. В коде остаются:
🔹 Проблемы с аутентификацией.
🔹 Утечки чувствительных данных.
🔹 Уязвимости инъекций.
Низкая читаемость кода.
ИИ любит:
🔹 Вкладывать функции в функции до бесконечности.
🔹 Создавать мега-файлы с десятками методов.
🔹 Генерировать многословные комментарии вместо понятного кода.
Получается не архитектура, а дерево зависимостей, где все вызывает все.
Мой опыт:
В личных проектах я активно использую ИИ для рутины, но перед релизом такой код требует серьезного рефакторинга. Особенно страдают:
🔹 Code style: совсем не соответствует внутренним стандартам проекта.
🔹 Переиспользование кода: дублирует существующую логику.
🔹 Архитектура: ИИ не понимает текущую архитектуру приложения и концепцию разделения ответственности.
Нейросети не заменяют разработчиков, они меняют их роль. Теперь мы не только пишем код, но и рефакторим и приводим в порядок то, что создает ИИ.
А как вы используете нейросети в работе? Сталкивались с похожими проблемами?
Кот Денисова
Please open Telegram to view this post
VIEW IN TELEGRAM
👍24❤15🔥7 4👏1🙏1👀1
Привет, друзья! Apple тихо представила интересный фреймворк: AppMigrationKit. Это не просто еще одна библиотека, а реальный инструмент для решения старой боли: миграции данных между Android и iOS.
Что такое AppMigrationKit.
Данный фреймворк позволит безопасно переносить данные вашего приложения между iOS и Android. Вместо того чтобы пользователи вручную переносили свои настройки, прогресс или сохранения, всё происходит автоматически во время настройки нового устройства.
Как это работает.
Вы создаете extension в своем приложении, который реализует протокол AppMigrationExtension. Система сама вызовет ваш extension когда пользователь переходит с Android на iPhone (или обратно).
Что важно понимать:
🔹 Работает между Apple и не-Apple платформами (например, Android).
🔹 Не для iOS-iOS миграции (тут и так все просто).
🔹 Не работает на visionOS и macOS.
🔹 Cloud данные нужно синхронизировать отдельно после миграции.
Почему это важно для разработчиков.
Пользователи часто не хотят менять iPhone именно из-за страха потерять данные в приложениях. Теперь мы можем дать им плавный переход. Особенно важно для:
🔹 Игр с прогрессом.
🔹 Приложений для тренировок.
🔹 Трекеров привычек.
🔹 Любых приложений с пользовательскими данными.
Это большой шаг к открытости экосистемы Apple. Раньше такие миграции требовали костылей и сторонних сервисов. Теперь есть нативный способ. Жду, когда крупные приложения типа Duolingo начнут это использовать!
Мобильный трудоголик
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥21👍16❤8👀4 2🤯1🙏1
Друзья, сегодня поговорим о массивах в Swift. Казалось бы, самой базовой структуре данных, но с интересными особенностями под капотом.
Value type но со своими хитростями.
Массивы - это структуры (value types), но данные хранятся в куче. При присваивании в переменную создается новая структура, но буфер с данными остается общим до момента модификации (Copy-on-Write):
var array1 = [1, 2, 3]
var array2 = array1 // Пока нет копирования данных
array1.append(4) // Только теперь данные копируются
Работа с ссылочными типами.
С классами нужно быть аккуратнее:
class User {
var name: String
init(name: String) {
self.name = name
}
}
let users1 = [User(name: "Artem"), User(name: "Dima")]
let users2 = users1
users1[0].name = "Egor" // Изменение затронет оба массива
print(users2[0].name) // Egor
Для глубокого копирования нужно реализовать протокол NSCopying. Без него при копировании массива с классами создаются только новые ссылки на те же объекты в памяти. NSCopying позволяет создать настоящие копии каждого объекта.
Производительность операций с массивами.
На практике важно понимать сложность основных операций с массивами, чтобы выбирать оптимальные подходы:
🔹 append() - в среднем O(1), но при расширении буфера происходит копирование всех элементов.
🔹 removeLast() - O(1), так как не требует сдвига элементов.
🔹 remove(at:) - O(n) из-за необходимости сдвигать все последующие элементы.
🔹 insert(_:at:) - O(n) по той же причине, что и remove(at:).
Эффективная работа с частями массива (ArraySlice).
ArraySlice работает как указатель на определенный диапазон элементов в оригинальном массиве. Это особенно полезно при работе с большими массивами:
let largeArray = Array(0..<1000000) // Большой массив
let slice = largeArray[1000..<2000] // Не копирует миллион элементов!
// Используем срез как обычный массив
for element in slice {
print(element) // Работает с элементами 1000-1999
}
Но важно помнить: ArraySlice сохраняет сильную ссылку на весь исходный массив. Это может привести к неожиданному потреблению памяти. Вот решение:
let largeArray = Array(0..<1000000) // Большой массив
let slice = largeArray[1000..<2000] // Не копирует миллион элементов!
// Создаем новый массив если нужна независимая копия
let independentCopy = Array(slice) // Теперь largeArray можно освободить
Массивы под капотом.
Swift использует разные буферы в зависимости от контекста:
🔹 _ContiguousArrayBuffer - для Swift-типов.
🔹 _ArrayBuffer - при работе с Objective-C.
Массивы в Swift - это не просто списки значений. Понимание их устройства помогает писать более эффективный код и избегать неочевидных проблем.
Мобильный трудоголик
Please open Telegram to view this post
VIEW IN TELEGRAM
👍24❤17🔥10🙏3 2🤝1 1
В Swift у разработчиков есть выбор: использовать традиционные циклы for или функции высшего порядка. Давайте разберемся, где каждый подход раскрывается лучше всего.
Сильные стороны функций высшего порядка.
Для простых преобразований данных map выглядит чище:
// Более читаемо
let numbers = [1, 2, 3, 4, 5]
let squaredNumbers = numbers.map { $0 * $0 }
// squaredNumbers = [1, 4, 9, 16, 25]
// Чем
let numbers = [1, 2, 3, 4, 5]
let squaredNumbers = [Int]()
for number in numbers {
squaredNumbers.append(number * number)
}
// squaredNumbers = [1, 4, 9, 16, 25]
filter идеален для отбора элементов:
let users = [
User(name: "Анна", isActive: true, age: 25),
User(name: "Петр", isActive: false, age: 30),
User(name: "Мария", isActive: true, age: 22)
]
let activeUsers = users.filter { $0.isActive }
// activeUsers = [User(name: "Анна", isActive: true, age: 25), User(name: "Мария", isActive: true, age: 22)]
reduce отлично подходит для агрегации:
let prices = [100, 200, 300]
let totalPrice = prices.reduce(0) { currentSum, price in
return currentSum + price
}
// totalPrice = 600
Когда циклы for предпочтительнее.
Логика с условиями:
var passedStudents: [String] = []
let studentGrades = [45, 82, 91, 33, 67, 74, 58, 29, 95]
for grade in studentGrades {
guard grade >= 60 else { continue } // Исключаем оценки ниже 60
guard grade <= 90 else { continue } // Исключаем оценки выше 90
passedStudents.append("Студент с оценкой \(grade)")
}
/*
passedStudents = [
"Студент с оценкой 82",
"Студент с оценкой 67",
"Студент с оценкой 74"
]
*/
Ранний выход из цикла:
// Поиск первого отрицательного числа
let dailyTemperatures = [15, 18, -2, 20, 25, -5]
var firstNegative: Int?
for temperature in dailyTemperatures {
if temperature < 0 {
firstNegative = temperature
break // Выходим при первом найденном
}
}
// firstNegative = -2
Изменение внешних переменных:
let itemsToProcess = [1, 15, 41, 3, 22, 61]
var processedItems = 0
var failedItems = 0
for item in itemsToProcess {
if processItem(item) {
processedItems += 1
} else {
failedItems += 1
}
}
Производительность.
Для массивов до 1000 элементов разница в производительности между функциями высшего порядка и циклом for практически незаметна. Современные оптимизации компилятора Swift отлично справляются с функциональными методами. Однако при обработке десятков тысяч элементов циклы for могут быть на 10-30% быстрее.
Функции высшего порядка часто создают промежуточные массивы. Цепочка map, filter, map может создать 3 временных массива, тогда как цикл for работает с одним. Для больших данных это влияет на память.
Swift предлагает нам два мощных подхода к обработке данных, и мудрый разработчик знает, когда применять каждый из них. Функции высшего порядка прекрасно справляются с простыми и средними по сложности преобразованиями данных. Они делают код более декларативным, читаемым и безопасным за счет неизменяемости.
Однако традиционные циклы for остаются незаменимыми, когда речь идет о сложной бизнес логике с множеством условий внутри цикла, необходимости раннего выхода из цикла или изменении внешних переменных.
Мобильный трудоголик
Please open Telegram to view this post
VIEW IN TELEGRAM
Forwarded from Кот Денисова
Здравствуйте, друзья! Сегодня поговорим на одну неудобную, но очень важную тему. Ту самую, о которой многие молчат, но которая живет в голове у большинства из нас.
Вам знакомо чувство, когда смотришь на код коллеги и думаешь: «Вау, он такой умный, а я вообще ничего не понимаю»? Или когда получаешь оффер и первая мысль: «Ой, а они точно не ошиблись?»
Поздравляю, вы не одиноки. Это тот самый «синдром самозванца», и в мобильной разработке он распространён как нигде. Почему? Потому что экосистема iOS меняется со скоростью света. Только разобрался с UIKit, появился SwiftUI. Только освоил GCD, все переходят на async/await. Вчера Objective-C, сегодня Swift, завтра - кто знает, что еще.
Типы «синдрома самозванца».
🔹 Перфекционист: будет переписывать код десять раз, потому что «еще не идеально». Знакомо? Многие иногда ловят себя на том, что тратят час на нейминг переменной.
🔹 «Я же должен всё знать»: этот парень уверен, что Senior iOS разработчик должен помнить наизусть всю документацию Apple. А когда сталкивается с задачей, которую не может решить за пять минут, начинает сомневаться в своей профпригодности.
🔹 Одиночка: три дня бьётся над проблемой с Auto Layout, но не спросит помощи, потому что «буду выглядеть глупо». Признаюсь, я и сам через это проходил в начале карьеры.
🔹 Супермен: берет все задачи подряд, работает по ночам, доказывает что-то миру и выгорает через полгода.
🔹 Вечный студент: «мне нужно пройти еще один курс по iOS-разработке, прежде чем я буду готов к собесу». Узнаете?
Что может помочь улучшить ситуацию:
🔹 Фокус на решении, а не на знании: вместо «я должен выучить весь Swift» думайте «мне нужно решить вот эту конкретную задачу». Это сильно снижает уровень давления.
🔹 Блокнот маленьких побед: звучит банально, но работает. Раз в неделю записывайте 2-3 вещи, которые получились хорошо: пофиксили сложный баг, помогли коллеге разобраться, написал чистый код. Со временем начнете видеть реальную картину вместо выдуманной.
🔹 Экспериментальный подход: смотрите на сложные задачи не как на экзамен, а как на исследование. Не получилось с первого раза? Ну и ладно, получили опыт, собрали данные, пробуйте иначе.
🔹 Помните, что сомнения - это нормально: если вы в чем-то не уверены значит вы растете и выходите из зоны комфорта. Отсутствие сомнений это скорее повод для беспокойства.
Синдром самозванца не исчезает с опытом. Он просто трансформируется. Даже спустя годы в индустрии иногда ловлю себя на мысли: «А не переоценивают ли меня?» Но теперь я понимаю, что это не слабость, а обратная сторона развития.
Please open Telegram to view this post
VIEW IN TELEGRAM
❤20👍17🔥7👀6😁1🙏1
Всем привет! Сегодня поговорим о, казалось бы, базовой теме: выборе между let и var в структурах Swift. Но, как оказалось, даже здесь есть много тонкостей, которые влияют на поведение нашего кода.
Все мы знаем, что let создает константу, а var переменную. Но в контексте структур есть важные нюансы:
Проблемы с let и опциональными типами.
Когда мы используем let для опциональных свойств, мы обязаны явно передавать значения в инициализаторе, даже если хотим передать nil:
struct User {
let id: UUID
let name: String
let email: String?
}
// Ошибка компиляции: Missing argument for parameter 'email' in call
let user1 = User(
id: UUID(),
name: "Андрей"
)
// Обязаны явно указать nil для свойства email.
let user2 = User(
id: UUID(),
name: "Вася",
email: nil
)
Теперь посмотрим на пример с var:
struct User {
let id: UUID
let name: String
var email: String?
}
// Оба варианта работают:
let user1 = User(
id: UUID(),
name: "Петр"
)
let user2 = User(
id: UUID(),
name: "Иван",
email: nil
)
Использование let для опциональных свойств создает неожиданные сложности при создании экземпляров структур. Компилятор требует явно передавать значения для всех let-свойств в автоматическом инициализаторе, даже если мы хотим установить nil. Это приводит к громоздкому коду, где приходится постоянно указывать nil для необязательных полей. С var такой проблемы нет, опциональные свойства автоматически получают значение nil, если их не указать явно.
Дефолтные значения и let.
Комбинация let и дефолтных значений может привести к неочевидному поведению, особенно при работе с Codable.
// id не будет задаваться в инициализаторе, он всегда будет новым (даже при декодировании из JSON):
struct User {
let id = UUID()
let name: String
}
let user = User(name: "Максим")
Когда свойство объявлено как let с дефолтным значением, оно всегда будет использовать это значение, даже при декодировании из внешних данных. Это означает, что JSON с другим значением для этого поля будет проигнорирован, что часто является ошибкой.
Кастомные инициализаторы в помощь.
Если нужна читаемост и гибкость, то напишем свой инициализатор:
struct User {
let id: UUID
let name: String
let email: String?
init(
id: UUID = UUID(),
name: String,
email: String? = nil
) {
self.id = id
self.name = name
self.email = email
}
}
Создание собственного инициализатора позволяет обойти ограничения автоматического. Мы можем определить параметры с дефолтными значениями, сохраняя при этом иммутабельность через let. Такой подход дает контроль над процессом инициализации, но требует написания дополнительного кода и его поддержки.
Property Wrapper для иммутабельности.
Можно создать кастомное решение для read-only свойств с дефолтными значениями:
@propertyWrapper
struct Readonly<Value> {
let wrappedValue: Value
}
struct User {
@Readonly var id = UUID()
var name: String
@Readonly var email: String?
}
Обертка свойства
@Readonly имитирует поведение let, но с поддержкой дефолтных значений. Однако этот подход добавляет сложности и может сделать код менее читаемым.Начинайте с let для всех свойств, это обеспечивает безопасность и предсказуемость. Переходите на var только когда появляется реальная необходимость изменять данные.
Для опциональных свойств, где let создает неудобства, можно использовать кастомные инициализаторы с параметрами по умолчанию - это сохраняет иммутабельность без потери удобства. Идентификаторы и другие гарантированно неизменяемые данные всегда должны объявляться через let.
Мобильный трудоголик
Please open Telegram to view this post
VIEW IN TELEGRAM
1 19❤13👍13🙏4🔥2👏1👀1