Стендап Сьогодні
Що я зробив, що я хочу зробити, і що це все значить.
Повсякденні здобутки в форматі стендапу.
Детальніше в статті
Підписатись на RSS
📢
Канал в Telegram @stendap_sogodni
🦣
@stendap_sogodni@shevtsov.me в Федиверсі
30.12.2023
Дев-адвент 30: онбордінг
Зробив екран онбордінгу.
Взагалі, спостерігаю, що продукт для людей відрізняється від поробки для власного використання вже тим, що в ньому повинні бути ці “зайві” з точки зору функціонала, але життєво необхідні для користувача елементи — онбордінг, довідка, обробка порожнього стану і таке інше.
Радий тому, що з SwiftUI легко зробити красивий екран. До того ж він буде нормально відображатися на айфонах різного розміру. Більшість верстки робиться компонентами HStack, VStack та Spacer. Порівняно з CSS, це легше вхопити — немає історичного розмаїття підходів.
А ще мені було потрібно місце, де запросити дозвіл до Apple Health - це й стало головним CTA для екрана. До речі, цікаво, що задля приватності в Apple Health немає способу дізнатись, чи є в тебе доступ на читання — якщо немає, то всі запити будуть повертатися порожніми. Ну, зате “дозволу немає” та “даних немає” можна обʼєднати в єдиний випадок.
29.12.2023
Дев-адвент 29: екран поточного статусу
Нарешті дістався до екрана поточного статусу. (Також, мабуть, одна з перших потреб, але уявлення про нього довго не зʼявлялось.)
Замість “блокового” дизайну, який був на самому початку, обрав табличний. Блоки мені теж подобались, бо воно так барвисто та соковито — але на практиці ми опинимось з одним і тим самим набором блоків протягом довгих проміжків часу (бо вага так швидко не змінюється), та око від них просто втомлюється. А табличку легко продивитись та побачити, як справи. Та вона чудово доповнює графік, що над нею.
Щодо показників. Тренд вирішив показувати тижневий — це той проміжок, за який відбувається мінімальна помітна зміна. Окрім того, обчислюю вагу через місяць такого тренду. Це заміна більш класичному “до цілі залишилось Х днів”. За досвідом, якщо ціль далеко, то такі прогнози не ліпше за погоду — зате місяць цілком можливо витримати в одному темпі.
Нарешті, дефіцит чи надлишок калорій обчислюються за простою формулою: різниця ваги, помножена на 7700 ккал/кг. Саме стільки зберігає один кілограм жиру. А оскільки я беру різницю вже від “реальної”, тобто згладженої, ваги, то це достатньо точна оцінка.
28.12.2023
Дев-адвент 28: нарешті, назва
Готую програму до публікації. Як не кумедно, але тільки сьогодні остаточно обрав назву. До того була тільки робоча: HDiet.
З технічних аспектів: перейменувати проєкт в XCode досить просто, принаймні поки він не опублікований. Для цього XCode закривається, відкривається VSCode, та робиться масова заміна по коду назви; після чого залишається так само знайти та замінити в назвах файлів, для чого я не знайшов автоматичного механізму, бо ліньки (хоча довелося перейменовувати сьогодні разів пʼять.)
З бюрократичних: назва застосунку в App Store має бути унікальною! Але тільки для конкретної базової мови… я читав, що люди обходять це через вибір мови “Англійська (Канада)”, але мені не хотілося. Причому назва резервується на момент реєстрації в App Store Connect (це “залаштунки” App Store.) Навіть якщо застосунок не опублікований чи застарілий. Тому багато з обраних мною назв ніби не в пошуці не знаходились, втім не були дозволені до реєстрації.
З творчих: назва WeightPlot містить невеличку гру слів. “Plot” може значити або “графік/діаграма”, або “сюжет”. Обидва значення описують функціональність моєї програми: графік ваги та історію її змін. Слово “дієта” вирішив не притягувати, бо до харчування вона як раз не має відношення.
Отакої! Вже недовго залишилося.
27.12.2023
Дев-адвент 27: форматування та одиниці виміру
Переді мною стояла дилема: або я перекладаю програму українською, або залишаю англійською, але впроваджую підтримку фунтів. Інакше множина потенціальних користувачів залишається якщо не порожньою, то принаймні надто вузькою.
Повірте, переклад незрівнянно складніший, ніж підтримка одиниць виміру. Але й обчислення в різних одиницях — не так просто. Константи в моєму коді налаштовані на кілограми та метри; просто так замінити на фунти та фути не вийде, а до того ж ускладнить підтримку. Тому я вирішив, що внутрішні величини та обчислення завжди будуть вестися в метричній системі, а конвертація відбуватиметься на виводі.
Таким чином, зробив конвертацію одиниць виміру частиною форматування. Чудовий привід відрефакторити форматування, бо до цього часу по коду були розсипані шаблони на кшталт "%0.1f kg". А тепер повсюди одна бібліотечна функція.
Самі одиниці виміру я беру з Apple Health; звідти відомо, які одиниці обрав користувач, тож не доведеться про це питати. Єдине, що конвертування вбудованими засобами дуже повільно, бо це треба створити HKQuantity та викликати в неї doubleValue(for: unit). В циклі для графіка це непіднімно. На щастя, одиниці виміру ваги конвертуються множенням на коефіцієнт, а його можна дізнатись викликом:
HKQuantity(unit: kiloUnit, doubleValue: 1.0).doubleValue(for: preferredUnit)
26.12.2023
Дев-адвент 26: історія
Сьогодні історія набула приблизно такого вигляду, якою я хочу її бачити. Також додав деяку статистику (а для того треба було придумати, куди.)
З технічних моментів: у Swift є такі “обчислювані властивості”, які виглядають, як властивість з ініціалізатором. Я, чомусь, наївно думав, що такі властивості зберігають своє значення. Але насправді нічого такого немає, обчислювана властивість — це звичайна функція, відрізняється лише синтаксис її виклику. Довелося трохи порефакторити.
А ще виправив алгоритм, щоб він не утворював послідовні проміжки стабільності. Була в нього така особливість, що якщо вага дуже повільно зростає або зменшується, то виходить, наприклад, три тижні однієї “стабільної” ваги, а потім два тижні вже іншої “стабільної” ваги. Виправити було дуже легко — просто, буквально, не дозволяю алгоритму брати два стабільних відрізки підряд. В результаті перебір знайде наступний за оцінкою розвʼязок та все буде добре.
Історія буде ще цікавішою, коли там зʼявляться коментарі та світлини, але це не для MVP.
25.12.2023
Дев-адвент 25: яка статистика цікава?
Нарешті дістався до візуально цікавого — збагачення історії визначними точками. Ось поточний перелік ідей по тому, що можна вчепити, навіть без доступу до додаткових джерел інформації та ручного вводу:
-
Найбільша та найменша вага — за весь час та за рік. Звісно, з датами. Це, певно, найцікавіше з всього.
-
Найдовший проміжок — набору, схуднення, стабільності. Альтернативно: проміжки з найбільшою різницею. Довжина вказує на стійкість намірів, але різниця — на їхню успішність.
-
Перетин визначних значень: круглих дат, категорій індексу маси тіла. Наближення до найбільшої та найменшої ваги. (Також колись я додам можливість вказати значення-ціль, тоді можна буде відзначати наближення до цілі.)
-
Особливі дати — свята та сезони. З Apple Health можна навіть день народження взяти. (Інженер в мені каже, що можна інтегруватись з календарем, але насправді то надто складно та можливість додавати коментарі на дату цілком перекриває таку потребу.)
Перелік такого роду в голові виглядає довжелезним, аж поки не спробуєш його записати. А ще треба придумати набір коментарів до поточного стану.
24.12.2023
Дев-адвент 24: звʼязаний список
🎄 Всіх з Різдвом, хто святкує! Дізнався, що в App Store вже кілька років немає різдвяних канікул, та публікувати програми можна протягом всіх зимових свят. Що приємно, бо до Нового року я як раз дотягну решту функціонала.
Сьогодні нарешті закінчив роботу над збереженням стану системи та інкрементальним оновленням.
Як один з останніх нюансів, знайшов ефективнішу схему зберігання розвʼязку свого алгоритму. Як я писав, алгоритм будує розвʼязки для більших послідовностей через розвʼязки для коротших; але раніше я зберігав кожний розвʼязок наново. Виходила купа даних з квадратичним характером зростання.
Так от, зрозумів, що можна буквально зберігати кожну нову послідовність через номер коротшої послідовності, з якої вона побудована; виходить такий звʼязаний список. Тепер розмір даних зростає лінійно — та ще й без зайвого копіювання. Ба більше, я майже впевнений, що коли ми вчили динамічне програмування, то так воно й зберігалося.
Наступний етап — нарешті, доповнення історії різними цікавими статистичними фактами.
23.12.2023
Дев-адвент 23: правильне керування складністю
Трохи застопорився з оптимізацією обчислень. Хотілося розділяти графік на відрізки, коли є великі розриви в даних (більш як два тижні.) Намагався доробити алгоритм так, щоб він ділив послідовність на декілька. Це є набагато складніше, ніж мені спочатку здавалось.
Висновок 1: я запізно зрозумів, що відрізки між розривами, фактично, ніяк між собою не повʼязані, тому задачу можна розділити на 2 етапи: знайти безперервні відрізки та зробити аналіз по кожному окремо. Натомість я працював над розширенням алгоритму, щоб він враховував розриви.
Ніби в теорії те саме, але в залежності від перспективи можна отримати або ускладнення логіки, або акуратний підхід “розділяй та володарюй.”
Висновок 2: взагалі в мене вже є алгоритм, який працює з розривами — я про нього писав ще 2 тижні тому. Але той алгоритм “не чистий”, бо не робить з розривів “спеціальний випадок”. На практиці це майже нічого не значить - “спеціальність” можна показати вже в інтерфейсі.
Там був ще аргумент за оптимізацію пошуку відрізків, бо якщо ти натрапляєш на розрив, то раніше можна не шукати. Але, цей пошук і так і так має лінійну складність, тож багато не оптимізуєш.
Підсумок: треба було розв’язувати проблеми одна за одною, а не замахуватись разом все оптимізувати.
22.12.2023
Дев-адвент 22: async/await у Swift
Як і в JavaScript, у Swift є синтаксис async/await. Він зʼявився два роки тому - Swift молода мова, та не стримується від капітальних змін.
Async/await - одна з моїх улюблених особливостей JavaScript, та від Swift очікував чогось схожого. В базовому розумінні це дійсно одна й та сама фіча — вона надає можливість писати асинхронний код в послідовному стилі. Та як і в JavaScript, так і у Swift це робить код легшим до розуміння.
Втім, є важлива різниця. У Swift немає загально прийнятної абстракції Promise. Попереднім способом побудови асинхронного коду були колбеки. Проблема в тому, що немає способу замінити колбек на async/await - для цього потрібно переписати функцію, яку будемо викликати. Тому бібліотеки з підтримкою async/await зʼявляються тільки поступово.
Та ще й потрібно дізнатися, що альтернатива існує та знайти її. Наприклад, в Apple Health для переходу на async/await потрібно замінити HKSampleQuery на HKSampleQueryDescriptor.
Так що тепер ще більше ціную те, як JavaScript стандартизувався навколо Promise, бо вони не просто замінили колбеки, а ще й стали універсальним протоколом, який можна комбінувати як заманеться.
21.12.2023
Дев-адвент 21: все програмування — це побудова абстракцій
Успішно розвʼязав вчорашні проблеми та ще декілька — завдяки тому, що впровадив правильну абстракцію для дат.
Так, я хотів спочатку зробити тип “календарна дата”, як я робив для JavaScript. Але вранці придумав простіше та краще рішення: перекласти дати в порядкові числа.
Взяв за 0 перше січня 1970 - початок “епохи Unix”, і далі послідовно. Поточний індекс — лише 19712… тобто можна було б взяти початкову дату набагато раніше. Вийшов ось такий маленький клас.
Така абстракція чудово підходить для моїх обчислень, бо вони відбуваються за послідовністю дат. Відстань між датами обчислити легко. Зберігати тривіально.
Це порівняно з використанням стандартного класу Date, який насправді зберігає момент в часі, та з яким потрібно було завжди памʼятати, чи звів я момент до початку дня, а також ризикувати натрапити на проблему літнього часу, або високосну секунду…
Проблема не в тому, що це неможливо, а в тому, що доводиться тримати в голові інформацію, зайву для поточної задачі. В програмуванні так відбувається часто, та розвʼязок один — рефакторити так, щоб бачити тільки ті подробиці, які потрібні. Вирівнювати абстракцію.
Та в тому, думаю, й полягає успішність сучасного програміста — не хитрі алгоритми або енциклопедичні знання бібліотек, а вдале керування абстракцією.

