#iOSКомпас 🧭1️⃣3️⃣7️⃣ Завдання 137Які основні lazy контейнери у Swift ви знаєете?Доброго здровля мої любі друзі. З вами ваш незмінний ios розробник Сергій з @badlinkschannel.1️⃣ lazy var - ліниві властивості (Swift)Властивість не створюється під час ініціалізації об’єкта, а лише при першому зверненні.class Example { lazy var expensiveObject: Data = { print("Об’єкт створено") return Data() }()}let example = Example()print("Ще не створено")_ = example.expensiveObject
2️⃣ LazySequence та LazyCollection (Swift)Дозволяють уникати зайвих обчислень.let numbers = (1...1000).lazy.map { $0 * 2 }print(numbers.first!)
3️⃣ SwiftUI ліниві контейнериСтворюють дочірні в’ю лише тоді, коли вони з’являються в зоні видимості.🔣 LazyVStack та LazyHStack:ScrollView { LazyVStack { ForEach(0..<1000) { index in Text("Рядок \(index)") } }}
🔣 LazyVGrid та LazyHGrid:let columns = [GridItem(.flexible()), GridItem(.flexible())]ScrollView { LazyVGrid(columns: columns) { ForEach(0..<100) { index in Text("Елемент \(index)") } }}
4️⃣ UIKit - перевикористовувані таблиці та колекціїЕлементи створюються лише тоді, коли вони потрібні для відображення.🔣 UITableView:func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { let cell = tableView.dequeueReusableCell( withIdentifier: "Cell", for: indexPath ) cell.textLabel?.text = "Рядок \(indexPath.row)" return cell}
🔣 UICollectionView:func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell { let cell = collectionView.dequeueReusableCell( withReuseIdentifier: "Cell", for: indexPath ) return cell}
5️⃣ UIScrollView плюс reusable viewsПоширений патерн, коли в’ю створюються динамічно під час прокрутки.for i in 0..<100 { let label = UILabel() label.text = "Елемент \(i)" stackView.addArrangedSubview(label)}
6️⃣ UITableViewDiffableDataSource та UICollectionViewDiffableDataSourceОновлення даних застосовуються ефективно та без зайвих перезавантажень.var snapshot = NSDiffableDataSourceSnapshot<Section, Item>()snapshot.appendSections([.main])snapshot.appendItems(items)dataSource.apply(snapshot, animatingDifferences: true)
7️⃣ Swift Concurrency та AsyncLazy патерниЛіниве асинхронне завантаження даних.lazy var imageTask = Task { try await loadImage()}
#️⃣ У підсумку, ліниві механізми в iOS охоплюють як мову Swift, так і SwiftUI та UIKit, і є ключовим інструментом для оптимізації продуктивності та пам’яті.@Zatishna_Galera
#ЗатишнийDigest👍 П'ятничний digest новин зі світу IT:1️⃣ GOOGLE ПЕРЕВОДИТЬ GMAIL В ЕРУ GEMINIНа цю тему буде окремий пост, але по суті Google імплементує серйозні зміни в поштовий клієнт, прив’язуючи скрізь свій Gemini. Став запитання по листах, готуй чернетки, змінюй стиль і обсяг і так далі. Круто, що тут сказати, і логічно.2️⃣ HIGGSFIELD AI ВИПУСТИЛА ІНСТРУМЕНТ RELIGHTПрикольний інструмент, який дозволяє повністю змінювати і керувати освітленням на вже знятому або готовому медіа. Наприклад, вам подобається кадр, який ви зробили, але світло вийшло так собі - не проблема, тепер це можна змінити. На базі ШІ, звісно ж.3️⃣ xAI ПІДНЯЛИ $20 МЛРДНовий раунд інвестування закрила компанія Маска. Серед інвесторів Fidelity, Qatar Investment Authority, MGX та Nvidia й Cisco. При цьому Grok досяг 600 млн користувачів, але це зрозуміло, це загальна аудиторія X.4️⃣ RUNWAYML ПОКАЗАЛИ ГЕНЕРАЦІЮ ВІДЕО НА NVIDIA RUBINRunway Gen 4.5 запущена на новій картці Nvidia. Все як завжди - краще, швидше і так далі.5️⃣ NETFLIX КУПУЄ READY PLAYER MEСервіс призначений для створення аватарів для ігор. Покупка розумна, з урахуванням того, що Netflix пробує в ігри на ТВ.6️⃣ OPENAI ЗАПУСКАЄ CHATGPT HEALTHЦе нова функція для роботи з даними про здоров’я. Дозволяє аналізувати ваші дані про здоров’я, підключати дані з медичних записів та wellness-додатків (наприклад Apple Health, MyFitnessPal та інші). Передбачені персоналізовані поради, допомога з розумінням аналізів та рекомендації.7️⃣ LOGITECH ПРИСТРОЇ ПЕРЕСТАЛИ ПРАЦЮВАТИВесь логітек на macOS зламався, бо хтось забув оновити сертифікати. Дивно, звісно, таке чути від компанії, яка робить так багато девайсів та софта для них.8️⃣ ЕКОСИСТЕМА APPLE ДАЄ НОВУ ТРІЩИНУЦього разу антимонопольний регулятор Бразилії, разом із уже запровадженими в ЄС та Південній Кореї змінами, схвалив рішення про те, що Apple зобов’язується дозволити альтернативні магазини додатків і зовнішні способи оплати, минаючи App Store.9️⃣ RIVIAN ПОКАЗАЛИ UNIVERSAL HANDS-FREERivian R1 отримають нову функцію водіння, завдяки якій водії зможуть прибирати руки з керма і насолоджуватись видами за вікном. Поки анонсовано для ринку США і Канади і лише на певних ділянках доріг.@Zatishna_Galera
13 ПОРАД, ЯК РЕАЛЬНО ПРАЦЮВАТИ З AI ДЛЯ КОДУ (З ТРЕДА BCHERNY, КОТРИЙ СТВОРИВ CLAUDE CODE)1️⃣ Паралельність - must5 сесій у терміналі + нотифікації. Поки один агент думає, інший уже щось робить.2️⃣ Ще 5-10 сесій у вебіРозділяй контексти: код окремо, ресерч/тексти окремо. Менше плутанини, більше швидкості.3️⃣ Одна «важка» модель для всьогоКраще повільніше, але стабільно, ніж постійно перемикатись і ловити фейли в tool-use.4️⃣ Один спільний CLAUDE.md для командиУсі правила, обмеження і «чого не робити» зберігаються там. Помилки → правила → менше повторів.5️⃣ Фіксити помилки прямо в code reviewТегай агента в PR і одразу додавай правила в CLAUDE.md. Це накопичуваний ефект.6️⃣ Завжди починати з Plan modeСпочатку нормальний план, потім auto-accept правок. Менше переробок.7️⃣ Slash-команди для рутиниУсе повторюване - в .claude/commands. Це макроси для inner loop, економія часу і токенів.8️⃣ Субагенти під роліОкремо - написати код, окремо - спростити, окремо - перевірити. Якість стабільніша.9️⃣ PostToolUse hook для форматуванняФорматування має робити автоматика, а не модель і не ти.1️⃣0️⃣ Не використовуй dangerously-skip-permissionsКраще allowlist через /permissions і спільний settings.json. Безпечніше і контрольованіше.1️⃣1️⃣ Дай агенту доступ до реальних інструментівSlack, логи, CLI, аналітика. Тоді це не чат, а робочий інструмент.1️⃣2️⃣ Для довгих задач - контроль завершенняФонові агенти, stop hooks, sandbox без постійних підтверджень. Інакше все зависає.1️⃣3️⃣ Головне - feedback loopПоки агент сам не перевіряє результат (тести, UI, e2e), якості не буде. Верифікація дає +2-3x до результату.#️⃣ паралельність + стандарти + автоматична перевірка = AI, який реально працює, а не «пише код».@Zatishna_Galera
#iOSКомпас 🧭1️⃣3️⃣6️⃣ Завдання 136Що повинні реалізовувати змінні, що містяться в протоколі?З вами знову той самий автор каналу @badlinkschannel. Не забувайте підписатися. А ми продовжуємо наші яблучні історіі.У Swift змінні (властивості), оголошені в протоколі, повинні вказувати:🔣 Тільки для читання (get)Якщо властивість оголошена як { get }, клас або структура, що реалізує протокол, повинна надати щонайменше геттерprotocol Animal { var name: String { get } // Тільки читання}struct Dog: Animal { let name = "Барсик" // Реалізуємо тільки get}let dog = Dog()print(dog.name) // "Барсик"
🔣 Також можна використовувати обчислювану властивість:struct Cat: Animal { var name: String { return "Мурзик" }}
🔣 Для читання і запису (get set)Якщо властивість { get set }, клас або структура обов’язково повинні надати і get, і set.protocol Vehicle { var speed: Int { get set } // Читання і запис}class Car: Vehicle { var speed: Int = 100 // Реалізуємо і get, і set}let car = Car()car.speed = 120 // Можна змінити значенняprint(car.speed) // 120
🔣 Обчислювана властивість також підійде, якщо вона має get і set:class Bike: Vehicle { private var internalSpeed = 50 var speed: Int { get { return internalSpeed } set { internalSpeed = newValue } }}
🔣 Статичні властивості (static)Якщо властивість повинна бути спільною для всіх екземплярів (не індивідуальною), то вона оголошується static.protocol Config { static var appVersion: String { get }}struct AppSettings: Config { static let appVersion = "1.0.0"}print(AppSettings.appVersion) // "1.0.0"
🔣 Клас може використовувати class var, якщо властивість можна перевизначати в підкласах:class AppInfo: Config { class var appVersion: String { return "2.0.0" }}
@Zatishna_Galera
#ЧарівнийКомпас 🧭2️⃣2️⃣1️⃣ Завдання 221Що таке soft assertion і hard assertion у тестуванні?Якщо пасажири переживали, куди команда пропала на кілька днів, то у звʼязку зі святами і кінцем року часу у команди дуже мало, а роботи дуже багато. Але сьогодні ми знайшли час на те, щоб почати нову пʼятірку запитань з теорії тестування.🤔 Hard assertion і soft assertion - два підходи до перевірок у тестах. Основна відмінність - яка наша дія при отриманні першої помилки в ході тесту: або ми зупиняємо тест, або продовжуємо.🔣 Hard assertion (він же жорстка перевірка) - якщо умова перевірки не виконується, то ми вважаємо тест впашим і припиняємо подальший його прогін.Це корисна практика, коли кожна перевірка є критичною для тесту, а саме продовження тесту не має сенсу.🤓 Для наочності приклад: для виконання тесту і подальших перевірок у його ході нам необхідно залогінитися під існуючим користувачем, але ми цього зробити не можемо. У більшості випадків продовження такого тесту після неможливості залогінитися сенсу мати не буде.🔣 Soft assertion (він же це мʼяка перевірка) - якщо умова перевірки не виконується, то ми фіксуємо цей баг, але продовжуємо прогонку тесту.Ця практика буде корисною, коли ми можемо перевірити одразу кілька речей у ході нашого тесту і вони можуть бути незалежними одна від одної.🤔 Хоч найкращі практики тестування і радять не обʼєднувати такі речі в один тест, але, по суті, це не завжди можливо зробити. Наприклад, у нас може бути один тест на безліч UI-перевірок: текстів, заголовків, різних кнопок і так далі. Ці перевірки обʼєднують в один тест для зручності, щоб не плодити десятки тестів.#️⃣ У підсумку можна сказати, що hard assertion - використовується для критичних кроків у тестах, а soft assert - для комплексних перевірок.@Zatishna_Galera
#iOSКомпас 🧭1️⃣3️⃣4️⃣ Завдання 134Чим небезпечна утечка пам’яті?Доброго здровля мої любі друзі. З вами ваш незмінний ios розробник Сергій з @badlinkschannel. 🤔 Утечка пам’яті (memory leak) - це ситуація, коли пам’ять залишається виділеною, але програма вже не може отримати до неї доступ і не звільняє її. Такі утечки поступово збільшують споживання пам’яті, що може призвести до деградації продуктивності або краху застосунку.class Person { var pet: Pet? deinit { print("Person видалений") }}class Pet { var owner: Person? deinit { print("Pet видалений") }}var person: Person? = Person()var pet: Pet? = Pet()person?.pet = petpet?.owner = personperson = nilpet = nil
🔣 Як уникнути утечок пам’яті?🔣 Використовувати weak або unowned посиланняweak та unowned не утримують об’єкт, тому не створюють циклу посилань.class Person { var pet: Pet? deinit { print("Person видалений") }}class Pet { weak var owner: Person? deinit { print("Pet видалений") }}
🔣 unowned використовують тоді, коли об’єкт гарантовано існуватиме протягом усього часу життя того, хто на нього посилається.class Owner { var car: Car? deinit { print("Owner видалений") }}class Car { unowned var owner: Owner init(owner: Owner) { self.owner = owner } deinit { print("Car видалений") }}
🔣 Розривати цикли в замиканнях ([weak self])Замикання можуть утримувати self, якщо їх зберігає сам клас.class ViewController { var closure: (() -> Void)? func setup() { closure = { [weak self] in print(self as Any) } } deinit { print("ViewController видалений") }}
🔣 Як знайти утечки пам’яті?➖ Використовувати Instruments Leaks➖ Перевіряти виклик deinit➖ Досліджувати Memory Graph у Xcode з Malloc Stack Logging#️⃣ Такий підхід дозволяє виявляти цикли посилань і контролювати життєвий цикл об’єктів у Swift.@Zatishna_Galera
#ЧарівнийКомпас 🧭2️⃣2️⃣0️⃣ Завдання 220Чим traceroute відрізняється від ping і що за вихідними даними можна зрозуміти про проблему в мережі?Ping і traceroute вирішують різні завдання.🔣 Ping перевіряє: чи досяжний хост взагалі, роблячи це за допомогою надсилання ICMP Echo Request і отримання відповіді. При цьому ми отримуємо мінімум інформації: чи є зв’язок чи ні, затримку, втрати.Грубо кажучи, ми просто дізнаємося, чи живий сервер і чи можемо ми до нього достукатися.🔣 Traceroute ж показує: маршрут пакета до сервера, які вузли ми чіпляємо. При цьому використовується TTL (Time To Live), яке поступово збільшується, і ICMP-повідомлення повертаються з тих вузлів, на яких TTL обнуляється. Завдяки цьому ми бачимо маршрут, затримку на кожній ділянці і де саме пакет може застрягти, якщо таке сталося.По суті, ми бачимо шлях пакета від нас до цільового сервера і можемо віддебажити проблему у разі її виникнення.Це важливо, тому що ми можемо зрозуміти, де саме обривається маршрут, це дає нам розуміння - проблема на сервері чи по дорозі до нього. Ми можемо побачити точки з високою затримкою, можливо, там є якісь проблеми з маршрутизацією або перевантаженням. Крім цього можна зрозуміти, чи є якісь фаєрволи або додаткова фільтрація по дорозі: якщо ми замість ICMP відповіді отримуємо (* * *), це означає, що вузол не відповідає на traceroute, що може бути пов’язано з фільтрацією або обмеженням ICMP, але трафік при цьому може йти далі.#️⃣ Якщо коротко, то ping показує, чи доступний хост у принципі, а traceroute показує шлях до нього і допомагає зрозуміти, на якій ділянці мережі виникає проблема.@Zatishna_Galera
#ЗатишнийDigest👍 П'ятничний digest новин зі світу IT:1️⃣ OPENAI ВІДКРИВАЄ ПРИЙОМ ЗАЯВОК ДЛЯ СВОГО МАГАЗИНУChatGPT запускає свій магазин додатків всередині ChatGPT. Загалом достатньо очікуваний крок, нам уже показали багато прикладів таких додатків (той самий Photoshop, про який ми писали минулого тижня). Додатки можна буде викликати через «@».2️⃣ COURSERA КУПУЄ UDEMYОдна освітня платформа поглинає іншу. Круто, що тут скажеш, дві мої улюблені освітні платформи тепер будуть одним цілим, таке ми схвалюємо. Сподіваюсь, регулятори теж.3️⃣ ВИЙШОВ GEMINI 3 FLASHЧудова моделька, яка за багатьма показниками ліпша за 2.5 Pro, при цьому супер дешева. Мільйон токенів контексту, текст, аудіо, картинки на вхід, але на вихід лише текст. Міркування можна вимкнути. Ціна порівняно з 2.5 Flash за токен стала вищою, але обіцяють більшу ефективність, що в сумі має не занадто розгойдати загальну вартість.4️⃣ OPENAI ВИКОТИЛИ GPT IMAGE 1.5Вийшла відповідь на Google NanoBanana, але вже від OpenAI. Що стало краще - робота з реалізмом, у модельку додали «міркування», генерація стала сильно швидшою, за заявами аж у 4 рази, але здається максимум у рази 2, а швидше в 1.5.5️⃣ VOLKSWAGEN ЗАКРИВАЄ ЗАВОД У НІМЕЧЧИНІМова про завод у Дрездені. Справи у німецького автопрому якось не дуже, а китайці продовжують давити.6️⃣ CHATGPT НАЙБІЛЬШЕ ЗАВАНТАЖУВАНИЙ ЗАСТОСУНОКПравда, це на ринку США і за 2025 рік, і для Apple. Позаду - Google, TikTok, YouTube та інші. Минулого року ChatGPT був 4-им.@Zatishna_Galera
#iOSКомпас 🧭1️⃣3️⃣3️⃣ Завдання 133Розкажи про механізм скасування в `Operation`З вами знову той самий автор каналу @badlinkschannel. Сподіваюсь вам ще не набридла ця ідея капітана галери про iOS розробника.🤔 В `Operation` (раніше `NSOperation`) у `Foundation` є вбудований механізм скасування, який дозволяє коректно завершити операцію, якщо вона більше не потрібна. Але важливо: скасування не перериває виконання автоматично - код усередині операції повинен сам перевіряти прапорець `isCancelled` і завершуватися.🔣 Як працює скасування `Operation`?Виклик `cancel()` лише ставить операцію в стан скасування `(isCancelled = true)`.Після цього операція не припиняється автоматично й продовжить роботу, якщо не перевірятиме `isCancelled`.Тому всередині `main()` треба робити ручні перевірки і завершувати роботу самостійно.class MyOperation: Operation { override func main() { for i in 1...10 { if isCancelled { return } print("Виконується крок \(i)") sleep(1) } }}let queue = OperationQueue()let operation = MyOperation()queue.addOperation(operation)DispatchQueue.main.asyncAfter(deadline: .now() + 3) { operation.cancel()}
🔣 Скасування асинхронних операційДля асинхронних операцій `(isAsynchronous = true)` однієї перевірки `isCancelled` недостатньо. Розробник повинен вручну оновлювати стани `isExecuting` та `isFinished`, щоб `OperationQueue` могла коректно керувати життєвим циклом операції.class AsyncOperation: Operation { private var _executing = false private var _finished = false override var isAsynchronous: Bool { true } override var isExecuting: Bool { get { _executing } set { willChangeValue(for: \.isExecuting) _executing = newValue didChangeValue(for: \.isExecuting) } } override var isFinished: Bool { get { _finished } set { willChangeValue(for: \.isFinished) _finished = newValue didChangeValue(for: \.isFinished) } } override func start() { if isCancelled { isFinished = true return } isExecuting = true executeTask() } private func executeTask() { DispatchQueue.global().asyncAfter(deadline: .now() + 3) { if self.isCancelled { self.complete() return } print("Асинхронна операція завершена") self.complete() } } private func complete() { isExecuting = false isFinished = true }}let queue = OperationQueue()let asyncOp = AsyncOperation()queue.addOperation(asyncOp)DispatchQueue.main.asyncAfter(deadline: .now() + 1) { asyncOp.cancel()}
🔣 Скасування залежних операційСкасування операції не скасовує залежні автоматично - але якщо залежність не завершиться `(isFinished = true)`, залежні просто не будуть запущені. Вони не отримають статус `isCancelled`, але не виконаються.let op1 = MyOperation()let op2 = MyOperation()op2.addDependency(op1)let queue = OperationQueue()queue.addOperations([op1, op2], waitUntilFinished: false)op1.cancel()
Якщо потрібне автоматичне скасування всього ланцюжка, це треба реалізовувати вручну.@Zatishna_Galera
#ЧарівнийКомпас 🧭2️⃣1️⃣9️⃣ Завдання 219Для чого потрібен Host в HTTP/1.1 і що буде без нього? Заголовок Host вказує, до якого саме сайту або віртуального хосту звертається клієнт.Це важливо, тому що:➖ одна IP-адреса може обслуговувати кілька сайтів (virtual hosting),➖ серверу потрібно розуміти, який саме сайт має обробити запит.Приклад запиту:
GET /api/users HTTP/1.1Host: api.example.com Якщо ми будемо звертатися лише по IP-адресі, то сервер не зрозуміє, що звернення йде саме до api.example.com.🔣 Що буде, якщо Host опуститиВ HTTP/1.1 заголовок Host обов’язковий за стандартом. Якщо клієнт його не передав, можливі кілька варіантів:➖Сервер поверне 400 Bad Request. Це найкоректніша і найчастіша поведінка.➖ Деякі сервери можуть повернути «дефолтний» сайт або сторінку, але за дефолтом у більше кейсів це все таки буде 400, якщо Host відсутній.Заголовок Host потрібен, щоб сервер зрозумів, до якого домену і віртуального хосту належить запит. В HTTP/1.1 він обов’язковий. Під час тестування API через curl або автотести можна випадково забути Host і отримати дивні помилки. Це часта причина багів, коли на UI у вас усе працювало, а в автотестах ні.@Zatishna_Galera