#Ping
4 нотаток
- ActiveRecord
- AmazonRedshift
- API
- AppleScript
- AWS
- AWSLambda
- Cadencia
- CGO
- Chezmoi
- CI
- Clojure
- Cloudflare
- CloudflarePages
- CloudWatch
- CssParser
- CтохастичнийТаймтрекер
- DNS
- Docker
- Dotfiles
- Fly.io
- GCP
- Git
- GitHub
- GitHubActions
- Go
- Golang
- GTD
- HomeAssistant
- Hugo
- I18next
- JavaScript
- Jira
- JSON
- Kafka
- MacMiniВДорозі
- MacOS
- Markdown
- Mastodon
- Obsidian
- ObsidianCanvas
- OmniWOPE
- OpenSearch
- Oura
- PagerDuty
- Ping
- Plausible
- PostgreSQL
- ReactNative
- Redis
- RSS
- Ruby
- RubyOnRails
- Sentry
- Sintra
- Slack
- SMTP
- SQL
- SQLite
- Svelte
- Swift
- SwiftData
- SwiftUI
- Telegram
- Terraform
- TLS
- TypeScript
- Vercel
- VPN
- WeightPlot
- WordPress
- XCode
- Адвент2024
- Бази Даних
- БДС
- Безпека
- Блокнот
- Вебтехнології
- ВолюІнформації
- Гаджети
- ДизайнМовПрограмування
- Ігри
- Інструменти
- ІнтеграційніТести
- Кава
- КеруваннняЗадачами
- Кодогенерація
- Криптографія
- Локалізація
- Маркетинг
- МетаПост
- МоїПроєкти
- Навігація
- Оптимізація
- ОсновиІнтернетБезпеки
- Помічник ШІ
- ПомічникШІ
- ПостПроПохід
- Програмування
- Продуктивність
- Проєкти
- Проза
- РДУГ
- Рівночасність
- РобочийКомп
- Розробка
- РозумнийБудинок
- СинПопросивПриготувати
- Сон
- СтохастичнийТаймтрекер
- Танці
- ХмарніТехнології
-
Мій власний Steam Replay, або в що я грав у 2024 році
Як я обіцяв собі зробити звіт на кшталт Steam Replay, так і зробив. Причому навіть як функціональність застосунку, тобто можна такий саме звіт зібрати з будь-чого. Обираєш тег-“всесвіт” (бо якщо не обмежувати вибірку, то вийде повна каша) - тут це тег
gaming. А потім набір тегів-категорій - з тих, що з ним перетинаються. Отримуєш ось такий цікавий графік.…Насправді зі звітом все складніше. Те, що ви бачите — це я, після першого поганого досвіду, обрав топ-10 ігор. Бо там ще купа дрібних записів на 1-2 години, які вже перетворюють графік на шум. В кольорах розібратися неможливо. (Гадаю, треба відразу автоматично залишати на графіку топ-10.)
Технічно цей функціонал включав купу рефакторингу коду деревної мапи — бо там сидить обчислення “покривної” статистики. А ще форми пінга — бо там є UI вибору тегів. В цілому у Swift це приємно робиться, але: він любить кидатися помилкою про “не можу розібратися в типах”, та далі ми залишаємося на самоті: або розпилюй на менші функції — без допомоги IDE, або шукай помилку — теж наосліп. Вихід примітивний: коментую шматки коду, поки не скомпілюється; тоді поламане місце десь в коментарях.
…А тепер, до результатів. BG3 я так і не пройшов; досяг level cap та стало якось не цікаво. Сталкер здивував; але його поки теж закинув. Evil Within 2 - нудьгуватий, але компетентний. Hades - екшн-Roguelike з безліччю цікавих комбінацій. Echo Night Beyond на PS2 - гра випередила час, вигадлива, але логічна адвенчура від першого лиця. Ashes of the Apocalypse - вже писав, бумер-шутер з мистецьки зробленим світом.
Dread Delusion - не закінчив, але все ще планую, це як King’s Field з відкритим світом. Morrowind - цього року досліджував Tamriel Rebuilt. Rule of Rose - ще одна гра на PS2, SIlent Hill 2-like в цікавому сетінгу. Cyberpunk 2077 - а ви знаєте, що з виходом доповнення вони повністю переробили всю рольову систему та лут? Я таке вперше бачу.
Нагадаю, що вся ця статистика зібрана моїм стохастичним таймтрекером, який ось-ось можна буде завантажити в TestFlight… як тільки відділ підтримки Apple виспиться.
-
Резервне копіювання для Ping
Три місяці тому писав про резервне копіювання даних мого стохастичного таймтрекера Ping. Але нарешті тільки сьогодні вивів його в бету.
На жаль, забув випустити раніше. Але ось що допомогло. Моя власна копія почала зависати на запуску. В XCode дуже легко зрозуміти. що зависає — достатньо поставити застосунок на паузу. Тому коли дістався до XCode, то відразу побачив, що висне — не сама резервна копія — а її завантаження до iCloud, метод setUbiquitous.
Як мені здається, проблема була в тому, що моя альфа-версія робила копію на кожний запуск, та їх накопичилося дві з гаком тисячі в одній директорії в iCloud. Як мені здається, така кількість файлів уповільнює роботу iCloud. Бо більше нічого не змінювалося, а сам файл розміром лише 300 Кб.
Так що довелося закінчити фічу — робити копіювання раз на день, додати вимикач та допомогу. Завдяки використанню
@globalActorвиніс роботу з головного потоку. Тут у Swift цікаво:async/awaitроблять код рівночасним, але залишають виконання в головному потоці (тобто гальмують інтерфейс застосунку.) Щоб зробити код ще й паралельним, потрібно винести його в актора.Також зазначу. що бета-версії застосунків в Apple TestFlight згоряють за 90 днів. Тобто кожні 90 днів потрібно викладати нову версію, щоб бета-користувачі не втратили доступ. Та в мене сьогодні минуло 79! Радий, що через цю ситуацію встиг згадати та скоригувати курс.
-
SwiftUI - рефакторинг та оточення
Сьогодні додав маленьку фічу до свого стохастичного таймтрекера — а саме, можливість передивитися всі пінги, які відмічені конкретним тегом. Що корисно, щоб згадати минуле. Щоправда, я досі не зробив перегляду більше за 50 пінгів, бо для того потрібне ліниве завантаження — тож не дуже далеке минуле.
Фіча звучить зовсім простою. Запит для останніх пінгів вже є. Додаєш до нього фільтр по тегу (до речі, із CTE) та виводиш списком. Але є й проблема.
Натискання на пінг відкриває форму редагування. Така можливість, звісно, потрібна й на новому списку. Ця форма не така проста річ — бо вона є модальним вікном, та на екрані ніколи не повинно бути двох форм. Тому її стан є глобальним.
Цей стан в мене поки зберігався просто в
@State var formModelна структурі застосунку, та передавався глибше через@Binding. Цього разу я прикинув, через скільки шарів доведеться прокинути@Bindingта вирішив, нарешті, переробити.У SwiftUI є можливість, схожа на контексти в React - це Оточення,
@Environment. Нею можна передати вглиб структури презентації якесь значення. Але — саме значення, не змінну, редагувати його не можна.Або… можна? Спочатку я спробував передавати функцію-сетер,
setFormModel. Це спрацювало! Далі пошукав більш ідіоматичне рішення та знайшов, що взагалі-то в@Environmentможна передатиBinding(FormModel), та тоді його можна буде призначати черезformModel.wrappedValue = newFormModel.(
Binding, який лежить в основі моделі стану SwiftUI, як раз і є абстракцією над гетером та сетером, яка загортає змінне значення у незмінну обгортку.)Код став гнучкіше та чистіше. До речі, в останньому SwiftUI цей
@Environmentзначно спростили, так що якщо давно не бачили — подивіться.🕜🏷️ Нагадаю, що я пишу Ping - застосунок на iOS для нетрадиційного обліку часу. Якщо цікаво, до бети можна доєднатися тут.
-
Оновлення дати у SwiftUI
В моєму стохастичному таймтрекері Ping чимало логіки залежить від поточної дати. Почати хоча б з часограми на головному екрані. Це ставить цікаву задачу: як підтримувати дату актуальною? Календарну дату.
Як я дізнався, на платформах Apple є відразу декілька способів стежити за датою — залежно від потреб. Але я був здивований, що один з них — це NSCalendarDayChangedNotification - зроблений саме для мене. Це сповіщення покриває всі випадки зміни дати, яких насправді більше, ніж очікуєш — наприклад, зміна часового поясу. А головне, що не треба вигадувати власної логіки, як колись довелося для React.
Отже, отримати сповіщення від системи — дуже легко. Поширити ці дані в застосунку в сучасному SwiftUI теж легко — завдяки фреймворку Observation. Це проста, але дієва магія. Я створив одинак — джерело дати (тобто просто клас із властивостями
currentDateта щеcurrentStartOfWeek), помітив його@Observable, та тепер кожного разу, як я використовую його у SwiftUI, відбувається підписка на зміни. Байдуже, чи роблю я виклик прямо з компоненти, або десь в глибині коду. Observation лише зберігає відносини між компонентою та всіма атрибутами@Observable, які були використані.Оце й усе. Пройшовся по коду, замінив явне отримання дати на звернення до нового
DateSource, та застосунок став помітно уважнішим до часу.В продовження теми дат, також випускаю мікрооновлення для Reminders2JSON - тепер файл експорту містить в назві поточну дату — бо так зручніше для резервного копіювання. Та іконка теж змінилася. ✅