Мобильный трудоголик
1.38K subscribers
61 photos
9 videos
267 links
👨‍💻 Пишу простым языком об iOS разработке на Swift и мобильной разработке в целом.
🔹 Вошел в IT задолго до того как это стало мейнстримом.
---
‍Обо мне: https://t.me/hardworkerIT/3
Чат: @hardworkerChatIT
Канал про разработку и жизнь в ИТ: @itDenisov
Download Telegram
👨‍💻 iOS 26: почему нативная разработка становится must-have.

С выходом iOS 26 разрыв между нативными и кроссплатформенными решениями стал очевиден как никогда. Новый дизайн Liquid Glass, Apple Intelligence и интерактивные виджеты работают на полную мощность только в нативных приложениях. Flutter и другие кросс-платформенные фреймворки сталкиваются с растущими ограничениями: от задержек в поддержке новых функций до сложностей с интеграцией системных возможностей.


⚠️ Ключевые преимущества натива в iOS 26:

1️⃣ Полная интеграция с системой:

// Liquid Glass работает из коробки
struct ContentView: View {
var body: some View {
Text("Hello iOS 26!")
.glassBackgroundEffect() // Нативный эффект
}
}



2️⃣ Мгновенный доступ к новым API:

🔹 Apple Intelligence.
🔹 Интерактивные виджеты.
🔹 Системные жесты и анимации.
🔹 On-device AI через Core ML.


3️⃣ Высокая производительность:

Нативные приложения работают на 20-30% быстрее Flutter-аналогов благодаря прямому доступу к системным ресурсам.


Ограничения кроссплатформенных решений:

🔸 Задержки обновлений: поддержка новых функций iOS появляется с опозданием на 6-12 месяцев.
🔸 Костыли и обертки: для интеграции с системными API требуются мосты и плагины.
🔸 Визуальные компромиссы: Liquid Glass и кастомные анимации сложно реализовать.
🔸 Рост техдолга: поддержка двух платформ усложняет архитектуру.


Альтернатива — Kotlin Multiplatform, но с нюансами.

Kotlin Multiplatform (KMP) может быть компромиссом для проектов, где критична экономия без полной потери качества:

🔹 Общая бизнес-логика на Kotlin.
🔹 Нативные UI на SwiftUI и Jetpack Compose.
🔹 Снижение затрат на 20-40% без ущерба UX.

НО: даже KMP не даёт 100% преимуществ чистой нативной разработки:

🔸 Задержки с поддержкой новых API iOS: Swift-экосистема обновляется быстрее, чем KMP-компоненты.
🔸 Сложность отладки: бизнес-логика в KMP + нативный UI = два слоя для диагностики проблем.
🔸 Зависимость от JetBrains и Google: в отличие от нативного Swift, который полностью контролируется Apple.
🔸 Ограниченный доступ к iOS-специфичным функциям: особенно в первые месяцы после релиза новой iOS.


💡 Вывод:

iOS 26 сделала нативную разработку не просто выбором, а необходимостью для продуктов, которые хотят оставаться конкурентоспособными. Хотя Flutter остается вариантом для MVP, для долгосрочных проектов с высокими требованиями к UX и производительности нативный стек — самое верное решение.


➡️ Подписаться на канал
Мобильный трудоголик
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥26👍143🗿3🤝11
🔨 iOS 26: как отключить Liquid Glass в iOS-приложениях.

С выходом iOS 26 многие разработчики столкнулись с необходимостью адаптации под новый дизайн-систему Liquid Glass. Но что делать, если приложение ещё не готово к переходу? Рассказываю простое решение.


⚙️ Отключение Liquid Glass:

Добавьте в ваш Info.plist параметр UIDesignRequiresCompatibility со значением YES.

Это заставит приложение использовать старую дизайн-систему вместо Liquid Glass, даже на iOS 26.


⚠️ Важные ограничения:

🔸 Это временное решение, Apple планирует убрать эту опцию в iOS 27.
🔸 Не влияет на новые API, только на визуальное оформление.
🔸 Тестирование обязательно, некоторые элементы могут выглядеть иначе.


💡 Вывод:

Временное отключение Liquid Glass — это спасательный круг для приложений, которые не успели адаптироваться к iOS 26, но не стоит воспринимать это как постоянное решение. Начинайте работу над поддержкой новой дизайн-системы, которая уже стала основной.


➡️ Подписаться на канал
Мобильный трудоголик
Please open Telegram to view this post
VIEW IN TELEGRAM
🙏25👍189🔥2🤯21🤝1
🔢 Generics и Protocols: пишем код на Swift профессионально.

Если вы пишете на Swift, но не используете Generics и Protocols на полную, то вы теряете главные преимущества языка. Эти инструменты превращают код из простого набора инструкций в гибкую и безопасную систему.


1️⃣ Generics: пишем код один раз, используем везде.

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

Простой пример:


func swapValues<T>(_ a: inout T, _ b: inout T) {
let temp = a
a = b
b = temp
}

var x = 10
var y = 20
swapValues(&x, &y) // Работает с Int

var s1 = "Hello"
var s2 = "World"
swapValues(&s1, &s2) // Работает с String


Ограничения generic-ов:


// Проблема: нельзя складывать любой тип данных
func sum<T>(_ a: T, _ b: T) -> T {
return a + b // Ошибка: Binary operator '+' cannot be applied
}

// Решение: сообщаем компилятору что T должен быть числом
func sum<T: Numeric>(_ a: T, _ b: T) -> T {
return a + b // Работает с любыми числами
}


Пример с протоколом:


protocol Identifiable {
var id: Int { get }
}

struct User: Identifiable {
let id: Int
let name: String
}

class DataService<T: Identifiable> {
private var storage: [Int: T] = [:]

func save(_ item: T) {
storage[item.id] = item
}
}


Использование where для сложных ограничений:


func process<T>(_ value: T) where T: Identifiable, T: Equatable {
// value имеет и id, и может сравниваться
}



2️⃣ Protocols: контракты для ваших типов.

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

Простой пример:


protocol Drawable {
func draw()
}

struct Circle: Drawable {
func draw() {
print("Рисуем круг")
}
}

struct Square: Drawable {
func draw() {
print("Рисуем квадрат")
}
}

let shapes: [Drawable] = [Circle(), Square()]
shapes.forEach { $0.draw() }


Расширения протоколов:


extension Drawable {
// Теперь можно использовать реализацию по умолчанию
func draw() {
print("Рисуем что-то")
}
}



3️⃣ Associated Types: протоколы как дженерики.

Иногда нужно, чтобы протокол работал с разными типами данных, но мы заранее не знаем, с какими именно. Для этого используют associatedtype — это как заглушка для типа, которую заполнят при реализации.

Простой пример:


protocol Storage {
associatedtype Item
var items: [Item] { get set }
func add(_ item: Item)
}

class StringStorage: Storage {
typealias Item = String // Задаем тип
var items: [String] = []

func add(_ item: String) {
items.append(item)
}
}

class IntStorage: Storage {
var items: [Int] = [] // Компилятор сам определит тип

func add(_ item: Int) {
items.append(item)
}
}


Пример с ограничениями:


protocol ComparableStorage {
associatedtype Item: Comparable // Только сравниваемые типы
func isFirstItemGreater() -> Bool
}

class NumberStorage: ComparableStorage {
var items: [Int] = [5, 2, 8]

func isFirstItemGreater() -> Bool {
return items[0] > items[1] // Можем сравнивать
}
}


Что дает associatedtype:

🔹 Гибкость: один протокол для разных типов.
🔹 Безопасность: компилятор следит за типами.
🔹 Четкость: ясно, какие данные ожидаются.

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


⚠️ Как начать использовать Generics и Protocols эффективно:

🔸 Найдите повторяющийся код: где вы копируете логику для разных типов.
🔸 Определите общее поведение: что общего у ваших объектов.
🔸 Выносите в протоколы: создайте контракты для похожих объектов.
🔸 Добавляйте generics: делайте код универсальным.


💡 Вывод:

Generics и Protocols это не просто синтаксис, а способ мышления. Они превращают Swift из просто языка в инструмент для создания надежных и масштабируемых систем.


➡️ Подписаться на канал
Мобильный трудоголик
Please open Telegram to view this post
VIEW IN TELEGRAM
2417👍42🔥1🙏1
👨‍💻 Что на самом деле мотивирует разработчика: три фактора, которые важнее зарплаты.

Всем привет! Сегодня хочу порассуждать на тему, которая всплывает у каждого разработчика, когда приходит время менять работу. Что на самом деле определяет, будет ли нам комфортно и интересно на новом месте? Часто все сводится к обсуждению зарплаты, но я уверен, что есть факторы куда важнее. Давайте разберемся.


🔹 Люди, с которыми ты проводишь 8 часов в день.

Можно работать над самым прорывным продуктом в мире, но если команда разобщена, а атмосфера токсична, желание творить быстро испарится. Сильная команда это не просто набор талантливых инженеров. Это в первую очередь культура взаимопомощи, доверия и открытости. В такой среде не страшно задавать «глупые» вопросы, предлагать смелые идеи и признаваться в ошибках. Именно это позволяет быстро расти. Я бы предпочел присоединиться к сильной команде со средним продуктом, чем к хаотичной команде с гениальной идеей. Первая научит тебя большему и сбережет ментальное здоровье.


🔹 Руководитель как твой личный навигатор.

Руководитель это человек, который определяет твой повседневный опыт работы. Хороший менеджер не просто ставит задачи. Он видит твой потенциал, помогает строить карьерный путь, дает конструктивную обратную связь и, что немаловажно, прикрывает твой тыл от корпоративных бурь. Он превращает препятствия в задачи, которые можно решить. Для меня совпадение по ценностям и видению с будущим руководителем часто перевешивает дополнительные 20% к окладу. Потому что с плохим менеджером эти деньги будут стоить тебе слишком дорого, в виде стресса и выгорания.


🔹 Продукт, который зажигает, а не просто платит.

Даже в лучшей команде с идеальным менеджером может не хватить мотивации, если продукт сам по себе не вызывает интереса. Речь не только о «спасе мира». Важно, чтобы продукт был качественным, с продуманной архитектурой, а не вечным MVP, который постоянно горит. Когда ты видишь, что твоя работа имеет смысл, что ею пользуются и ценят, это дает совершенно иной уровень энергии. Работа над скучным, нестабильным или бессмысленным продуктом быстро приводит к цинизму и апатии, какую бы зарплату тебе ни платили.


💡 Вывод:

Высокий оффер это отлично, но это краткосрочный стимул. Эйфория от новой зарплаты проходит через несколько месяцев, и на первое место выходят именно те факторы, о которых я написал: атмосфера в команде, адекватность руководства и интерес к продукту. Осознанный выбор, где ты оцениваешь все эти «неочевидные» вещи это инвестиция в свое профессиональное и эмоциональное благополучие на годы вперед. Не позволяйте цифре в оффере затмить те сигналы, которые действительно влияют на качество вашей жизни.

А что для вас было главным фактором на последнем месте работы? Делитесь в комментариях!


➡️ Подписаться на канал
Мобильный трудоголик
Please open Telegram to view this post
VIEW IN TELEGRAM
22👍15👀4🤯11
🔢 Swift Concurrency: правильное использование [weak self] в задачах.

Всем привет! Сегодня разберемся с темой, которая вызывает много вопросов при переходе на Swift Concurrency: как правильно использовать [weak self] в Task.

Если вы работали с completion handlers, то у вас уже выработался рефлекс, почти в каждое замыкание добавлять [weak self] и делать классический guard let self. Кажется логичным перенести этот подход и в Task:


Task { [weak self] in
guard let self else { return }

let data = await loadData()
...
}


Но вот в чем проблема: этот код не предотвращает утечку памяти так, как вы ожидаете.


⚠️ Почему это происходит:

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

Когда вы делаете guard let self, вы создаете сильную ссылку на объект. Теперь Task будет удерживать self до своего завершения, до окончания всех await вызовов. Это эквивалентно тому, если бы вы в старом коде с completion handlers написали просто self.loadData() без всякого [weak self].


🤔 Когда это действительно становится проблемой?

Представьте долгоиграющую задачу, например, загрузку данных постранично:


Task { [weak self] in
guard let self else { return } // Проблема здесь

var hasMorePages = true
while hasMorePages {
let page = await fetchNextPage()
hasMorePages = !page.isLastPage
}
}


Если пользователь уйдет с экрана, self не сможет освободиться, потому что Task продолжит свою работу и удерживает его до окончания цикла.


Правильное решение:

Вместо немедленного разворачивания self, делайте это точечно, непосредственно перед использованием:


Task { [weak self] in
var hasMorePages = true
while hasMorePages {
// Проверяем self на каждой итерации
guard let self else {
hasMorePages = false
break
}

let page = await self.fetchNextPage()
hasMorePages = !page.isLastPage
}
}


Теперь сильная ссылка на self создается только на время одной итерации цикла. Если self освободится, цикл немедленно завершается.


⚡️ Рекомендации:

🔸 Не всегда нужно [weak self]: для коротких Task это часто избыточно.
🔸 Избегайте guard let self в начале Task: это не дает преимуществ, но может продлить жизнь объекта.
🔸 Разворачивайте self непосредственно перед использованием: минимизируйте время удержания сильной ссылки.
🔸 Рассмотрите захват конкретных свойств: если вам нужен не весь self, а только некоторые его свойства, захватите их отдельно.


💡 Вывод:

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


➡️ Подписаться на канал
Мобильный трудоголик
Please open Telegram to view this post
VIEW IN TELEGRAM
👍24154🔥1🙏1👀1
🔢 Что изменилось в работе со строками в Swift 6.2

Всем привет! Swift 6.2 принес нам небольшие, но очень приятные улучшения в работе со строками, которое многие давно ждали.


Какая была проблема?

До Swift 6.2 при интерполяции опциональных значений мы постоянно сталкивались с надоедливым ворнингом:

let age: Int? = nil

print("Your age: \(age)")
// String interpolation produces a debug description for an optional value


Компилятор предлагал два неидеальных решения:

🔸 String(describing:) - выводил nil, что некрасиво для пользователя.
🔸 Оператор ?? - требовал значение того же типа, что не всегда удобно.

let age: Int? = nil

// Некрасиво
Text("Your age: \(String(describing: age))") // Your age: nil

// Не всегда уместно
Text("Your age: \(age ?? 0)") // Your age: 0, но 0 может быть некорректным!



Что изменилось в Swift 6.2:

Теперь можно использовать параметр default прямо в интерполяции:

let age: Int? = nil

print("Your age: \(age, default: "not specified")") // Your age: not specified



Особенно удобно в SwiftUI:

struct ProfileView: View {
let username: String?
let level: Int?

var body: some View {
VStack {
Text("Username: \(username, default: "Guest")")
Text("Level: \(level, default: "Unknown")")
}
}
}



Что еще важно знать:

🔸 Тип значения не важен: default всегда принимает строку.
🔸 Работает с любыми опционалами: Int?, String?, Bool? и т.д.
🔸 Проверки на этапе компиляции: нет влияния на производительность.


Есть ограничение: к сожалению не работает с LocalizedStringKey.


💡 Мое мнение:

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


➡️ Подписаться на канал
Мобильный трудоголик
Please open Telegram to view this post
VIEW IN TELEGRAM
125👍18🔥4🙏11
👨‍💻 Неожиданный тренд 2025 года: некоторые компании заменяют сеньоров на джунов.

Всем привет! Сегодня хочу обсудить тренд, который может показаться парадоксальным: в 2025 году некоторые компании сознательно заменяют опытных разработчиков начинающими. Да, вы не ослышались, джуны становятся более востребованными, чем сеньоры в определенных сценариях.


Парадокс рынка труда.

Если посмотреть на статистику безработицы в США и Европе, становится ясно: молодым специалистам действительно сложно найти работу. Но причина не только в том, что ИИ забирает начинающие позиции. Оказалось, что искусственный интеллект в первую очередь бьет по… опытным разработчикам.


Почему опыт стал проблемой?

Сеньоры и мидлы это сформировавшиеся специалисты с устоявшимися подходами к работе. У них есть:

🔸 Четкое мнение об архитектуре проектов.
🔸 Привычный набор инструментов и технологий.
🔸 Собственные стандарты кодирования.

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


Гибкость vs опыт: что ценнее?

Некоторые компании пришли к выводу: лучше взять джуна, который:

🔸 Не боится экспериментировать с ИИ.
🔸 Готов быстро осваивать новые инструменты.
🔸 Не имеет предубеждений против «новомодных» технологий.
🔸 Обходится компании дешевле.

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


Рекомендации.

Если вы опытный разработчик:

🔹 Не игнорируйте ИИ-инструменты, осваивайте их постепенно.
🔹 Будьте готовы менять рабочие процессы, гибкость стала ключевым навыком.
🔹 Учитесь работать в тандеме с ИИ, а не против него.

Если вы джун:

🔹 Ваше умение быстро обучаться это главное преимущество.
🔹 Осваивайте ИИ-инструменты с самого начала карьеры.
🔹 Не бойтесь предлагать новые подходы, компании это ценят.


💡 Личное наблюдение:

Я вижу, как многие опытные коллеги совершают одну ошибку: считают ИИ временной модой. Но это не так, это фундаментальное изменение индустрии. Те, кто успеет адаптироваться, останутся востребованными. Остальные рискуют оказаться за бортом, несмотря на весь свой опыт.


➡️ Подписаться на канал
Мобильный трудоголик
Please open Telegram to view this post
VIEW IN TELEGRAM
👍21🤯14👀11🤔41🙏1
🔢 Swift Concurrency: как Executors и Actors управляют потоками.

Разберем как на самом деле работают потоки в Swift Concurrency. Если вы думаете, что async/await — это просто синтаксический сахар, приготовьтесь удивляться. Под капотом скрывается мощная система управления задачами через Executors и Actors.


Из чего состоит Swift Concurrency:

🔸 Task: единица асинхронной работы (как поток для синхронных функций).
🔸 Job: часть задачи между точками await (suspension points).
🔸 Executor: планировщик, который распределяет Job по потокам.
🔸 Cooperative Thread Pool: пул потоков (число = числу ядер устройства).


Типы Executors:

🔸 Global Concurrent Executor: дефолтный, распределяет задачи по потокам из пула.
🔸 Serial Executors: для акторов, выполняет задачи последовательно.
🔸 Main Actor Executor: специальный для главного потока.


Пример кастомного Executor:


// Создаем свой планировщик
final class CustomExecutor: TaskExecutor {
func enqueue(_ job: consuming ExecutorJob) {
print("Запускаем задачу на потоке: \(Thread.current)")
job.runSynchronously(on: asUnownedTaskExecutor())
}

func asUnownedTaskExecutor() -> UnownedTaskExecutor {
UnownedTaskExecutor(ordinary: self)
}
}

// Использование
Task(executorPreference: CustomExecutor()) {
print("Выполняем работу")
try await Task.sleep(for: .seconds(1))
print("Завершаем работу")
}



Actors: безопасность данных.

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


actor SafeCounter {
private var count = 0

func increment() {
count += 1
print("Текущее значение: \(count)")
}
}

// Использование
let counter = SafeCounter()

Task {
await counter.increment() // Безопасный доступ
}



Как работает последовательность.

Даже при параллельных вызовах актор гарантирует порядок:


await withTaskGroup { group in
for i in 0..<5 {
group.addTask { await counter.increment() }
}
}
// Вывод всегда будет: 1, 2, 3, 4, 5



Global Actors.

@MainActor вы уже знаете, но можно создавать и свои глобальные акторы:


@globalActor
actor NetworkActor {
static let shared = NetworkActor() // Единый экземпляр на все приложение
}

@NetworkActor
func fetchData() async -> Data {
// Все вызовы этого метода будут последовательными
// даже из разных частей приложения!
}

@NetworkActor
class ApiService {
// Все методы класса будут автоматически изолированы
func getUser() async -> User {
}
}


Зачем это нужно, спросите вы. Чтобы разные функции и классы использовали один общий актор. Например:

🔸 Все сетевые запросы через один NetworkActor
🔸 Все операции с базой данных через DatabaseActor
🔸 Все аналитические события через AnalyticsActor


Важные особенности:

🔸 Потоки не блокируются: await освобождает поток для других задач.
🔸 Автоматическое планирование: система сама выбирает оптимальный поток.
🔸 Безопасность по умолчанию: компилятор следит за изоляцией данных.


Когда использовать:

🔹 Global Concurrent: для независимых задач.
🔹 Serial Executor: когда важен порядок выполнения.
🔹 Main Actor: для работы с UI.
🔹 Custom Executors: для специальных требований к планированию.


💡 Вывод:

Swift Concurrency это не просто async/await, а целая экосистема для безопасной многопоточности. Понимание работы Executors и Actors поможет писать более эффективный и надежный код.


➡️ Подписаться на канал
Мобильный трудоголик
Please open Telegram to view this post
VIEW IN TELEGRAM
1👍24126🔥4🙏31
📱 Революция в Apple: почему iOS переписывают на Rust.

Всем привет! Пока мы спорим о SwiftUI и async/await, в компании Apple происходит тихая революция. Оказывается, компания постепенно переписывает ключевые компоненты iOS и делает это не на Swift. Все указывает на Rust - язык, который становится новым фундаментом операционной системы.


Что происходит на самом деле?

Если посмотреть на вакансии Apple за последние годы, становится ясно: компания активно ищет инженеров с опытом работы с Rust. Речь идет о системных компонентах:

🔹 Управление памятью.
🔹 Безопасность и криптография.
🔹 Низкоуровневые фреймворки.
🔹 Драйверы и ядро системы.


Почему Swift не подходит для этих задач?

Swift - прекрасный язык для разработки приложений, но у него есть ограничения:

🔹 Не идеален для системного программирования.
🔹 Работа с памятью через C/Obj-C оставляет уязвимости.
🔹 Модель многопоточности уступает Rust в безопасности.


Преимущества Rust для Apple:

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


Что это значит для нас, разработчиков?

В краткосрочной перспективе ничего не изменится:

🔹 SwiftUI и UIKit остаются основными фреймворками.
🔹 Swift продолжит развиваться.
🔹 API для приложений не поменяются.

Но в долгосрочной перспективе:

🔹 Стабильность: меньше падений из-за низкоуровневых ошибок.
🔹 Безопасность: уменьшится количество уязвимостей нулевого дня.
🔹 Производительность: системные компоненты станут эффективнее.


💡 Мое мнение:

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


➡️ Подписаться на канал
Мобильный трудоголик
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥2516👍4🤔4👏21
📱 Правда о вайбкодинге: почему быстрые решения убивают ваш профессиональный рост.

Всем привет! Сегодня хочу поговорить об опасной тенденции, которая захватила мир разработки - vibe coding. Это когда ты быстро переключаешься между вкладками, копируешь код из ChatGPT, видишь, что все компилируется, и чувствуешь себя продуктивным. Но на самом деле ты не понимаешь, что происходит.


Что не так с вайбкодингом?

Представьте: вы едете домой и разговариваете по телефону. Через час вы не помните ни дороги, ни поворотов, ни светофоров. Точно так же работает вайбкодинг, все происходит как в тумане.


Типичная ситуация:

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


Почему это опасно для карьеры:

🔸 Вы не становитесь экспертом: знание предметной области часто важнее технических навыков.
🔸 Технический долг растет как снежный ком: вы не понимаете, какие проблемы создаете.
🔸 Вас легко заменить: любой может скопировать код из AI, но не каждый может его осмысленно улучшить.


Что важнее: скорость или понимание?

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

Настоящий прогресс приходит через:

🔸 Борьбу с сложными ошибками.
🔸 Понимание КАК и ПОЧЕМУ что-то работает.
🔸 Глубокое погружение в проблему.


Правила осознанного использования ИИ:

Джуны, которые осмысленно работают с ИИ, растут быстрее, чем мидлы, просто копирующие код. Разница в подходе: первые используют ИИ как инструмент, вторые как костыль.

🔸 Используйте ChatGPT для рутины и изучения новых подходов.
🔸 Всегда анализируйте сгенерированный код перед использованием.
🔸 Задавайте вопросы «почему это работает?» и «как можно улучшить?».
🔸 Сохраняйте баланс между скоростью и качеством.


💡 Вывод:

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


А как вы используете ИИ в работе?


➡️ Подписаться на канал
Мобильный трудоголик
Please open Telegram to view this post
VIEW IN TELEGRAM
👍2419👀7🔥4🤔2🤯2
🔢 Swift Concurrency: что на самом деле делает компилятор?

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
2117👍9🔥3🙏32
🏋️ Android теряет свободу: новые правила верификации разработчиков по паспорту.

Хотя мое сердце принадлежит 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👀32
🔢 Enums vs Structs в Swift: когда что использовать.

При разработке на 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
2214👍12🔥1🙏1🫡1
📱 Как я создал полноценную доску объявлений в Telegram.

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

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


Что особенного в freeBuySellBot:

🔹 Полноценная доска объявлений прямо в Telegram: не нужно скачивать отдельное приложение.
🔹 Простая регистрация: никаких паролей и подтверждений почты.
🔹 Загрузка фото и редактирование объявлений в пару кликов.
🔹 Бесплатное поднятие объявлений раз в неделю: никаких скрытых платежей.
🔹 Удобный поиск по ключевым словам с пагинацией.
🔹 Избранное и история просмотров: не потеряете интересные предложения.
🔹 Встроенная связь с продавцом: вся коммуникация в одном месте.


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

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


Разработка:

🔹 Бот написан на JavaScript и Node.js.
🔹 Использовал фреймворк Telegraf для работы с Telegram Bot API.
🔹 База данных: SQLite для хранения пользовательских данных.


📊 На текущий момент в боте зарегистрировано более 10 тысяч человек.

🔗 Попробуйте freeBuySellBot прямо сейчас, не выходя из Telegram!


➡️ Подписаться на канал
Мобильный трудоголик
Please open Telegram to view this post
VIEW IN TELEGRAM
👍146🗿44😁3🔥2👏1🫡1
🔨 Оптимизация памяти: как снизить потребление памяти в iOS-приложениях.

Всем привет! Сегодня разберем одну из самых важных тем в 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👍1613🔥3🤝321
👨‍💻 Советы, которые помогли бы мне в начале карьеры в IT.

За годы работы в IT я собрал советы, которые значительно ускорили бы мне рост на старте карьеры.


Никогда не поздно сменить направление.

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

Я начинал с web-разработки на PHP, но со временем понял, что мобильная разработка для меня гораздо интереснее.


Инструменты меняются, принципы остаются.

Не зацикливайтесь на конкретных технологиях. Языки и фреймворки приходят и уходят, а фундаментальные принципы, такие как архитектура, алгоритмы, проектирование остаются актуальными годами. Умение адаптироваться ценнее знания конкретного стека.


Командная работа - вот настоящая суперсила.

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


Умение искать информацию важнее запоминания.

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


Практика единственный путь к мастерству.

Теория дает основу, но настоящие навыки приходят только с практикой. Не бойтесь экспериментировать и делать ошибки, именно так формируется настоящий опыт.


Баланс не роскошь, а необходимость.

Выгорание не признак профессионализма. Регулярный отдых и перезагрузка это инвестиция в вашу продуктивность и креативность. Качество работы важнее количества часов.


Широкий взгляд важнее узкой специализация.

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


Незнание чего-либо - это нормально.

В IT невозможно знать все, технологии развиваются слишком быстро. Нормальное восприятие состояния «я этого не знаю, но научусь» - ключ к постоянному росту.


Делайте только то, что вам нравится.

Заставлять себя работать это путь в никуда. Мозг саботирует неинтересные задачи. Найдите то, что приносит удовольствие, делайте это хорошо, получайте признание = любите работу еще больше.


💡 Вывод:

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


➡️ Подписаться на канал
Мобильный трудоголик
Please open Telegram to view this post
VIEW IN TELEGRAM
👍30179🔥5🙏4👏2👀1
🔨 Ошибки iOS-разработчиков при работе с Derived Data.

Всем привет! Сегодня поговорим о 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
2216🔥7👍4😁2🙏22
👨‍💻 Когда логи бессильны: истории о самых неочевидных багах в мобильной разработке.

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


Загадка с 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
👍2113🔥13😁4👀32🙏1
🔢 Set + Hashable: как Swift проверяет уникальность объектов.

Здравствуйте, друзья! Сегодня мы разберем одну из основ работы с коллекциями в 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
👍221510🙏4🔥1👏11
🔢 Swift: как сделать код лаконичнее с тернарным оператором.

Всем привет! Сегодня поговорим о тернарном операторе в 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
👍251310🔥6🙏1🤝11
👨‍💻 За что увольняют сотрудников в IT.

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

В 90% случаев увольнение это не внезапное решение, а накопительный эффект. Последний проступок просто становится формальным поводом для того, что назревало месяцами.


Причины вне вашего контроля (самые частые).

Это то, на что вы вообще не можете повлиять:

🔹 Внезапное сокращение финансирования проекта.
🔹 Реорганизация компании/отдела.
🔹 Приход нового руководства со «своей командой».
🔹 Слияния и поглощения.
🔹 Стратегическая смена направления компании.

Корпоративный мир жесток, вы как пешка на шахматной доске. Решения принимаются на уровнях, до которых вы просто не дотягиваетесь.


Ошибка найма.

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


Неоправданные ожидания.

Бывает, когда нанимают на позицию с перспективой роста (например, в тимлида), но за полгода-год роста не происходит. Или когда обещали быстро освоить новую технологию, но не получилось.

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


Нарушение доверия.

Что не любят работодатели больше всего:

🔹 Откровенная ложь на собеседовании (указал 3 года опыта вместо 6 месяцев).
🔹 Нарушение NDA - даже безобидный репост сообщения из рабочего чата в чат с друзьям или отправка куска рабочего кода в Stack Overflow.
🔹 Регулярные больничные или дни без содержания, когда горят сроки.
🔹 Работа на несколько компаний одновременно без предупреждения.

В IT доверие - это ваша главная валюта. Его легко потерять и почти невозможно восстановить.


Попытка заменить универсального специалиста.

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

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


Проблемы с софт-скиллами.

За токсичное поведение увольняют быстрее, чем за пробелы в технических знаниях. Что сюда относится:

🔹 Пассивно-агрессивное общение.
🔹 Распространение слухов.
🔹 Неуважение к коллегам.
🔹 Постоянные конфликты.

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


Профессиональная некомпетентность.

Опытному разработчику непозволительно постоянно допускать ошибки, которые типичны для новичка:

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

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


Рекомендации при увольнении.

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

🔹 Не паникуйте: это бизнес, а не личное оскорбление.
🔹 Возьмите паузу: 3-5 дней на восстановление ментального состояния.
🔹 Учитесь на ошибках: проанализируйте, что можно было сделать иначе и что следует изучить.
🔹 Обновите резюме: чтобы оно содержало самую актуальную информацию.
🔹 Свяжитесь со знакомыми: напишите всем, кого знаете из вашей сферы, ведь большинство хороших вакансий не размещается в публично.
🔹 Откликайтесь на вакансии: поиск новой работы займет время, но компетентные, адекватные, обучаемые специалисты всегда в цене.


💡 Вывод:

В IT-рынке всегда есть спрос на хороших специалистов. Главное извлекать уроки из каждой ситуации и не повторять ошибок. Даже если одна дверь закрылась, то впереди десятки других, часто даже лучше.


➡️ Подписаться на канал
Мобильный трудоголик
Please open Telegram to view this post
VIEW IN TELEGRAM
👍22👀16🗿11🙏41🤔1