Перейти до основного вмісту
megapromotingПоговорімо

Експертиза · Мобільні застосунки

Застосунки для телефону, побудовані так, щоб логіку можна було перевірити без телефону.

Ми будуємо нативні iOS-додатки на Swift, із сенсорами та HealthKit, і додатки з одним кодом для iOS, Android і web, коли проєкт не потребує сенсорів. Ядро логіки тестується окремо від екрана.

Вже збудованоЄ три власні реалізації, не одна. Репозиторій із п’ятьма цілями застосунку (чотири iOS і одна macOS), визначеними в `project.yml`, чиї тести логіки ми сьогодні запускали: 160 із 160 проходять. Друга iOS-застосунок, в іншому проєкті, з 40 файлами Swift і HealthKit. І третій напрямок, з одним кодом для iOS, Android і web, чия web-версія відповідає сьогодні. Резерв, що змінює відповідь на запитання, яке будь-який покупець ставить передусім: жоден із наших застосунків не опублікований в App Store або Google Play. Причина одна й має назву — `DEVELOPMENT_TEAM` порожній у конфігурації проєкту, тобто відсутній ідентифікатор команди в Apple Developer Program. Машина має чинну ідентичність підписування, достатню для встановлення на власний пристрій, недостатню для розповсюдження в магазині. Це проблема облікового запису, не коду, але вона лишається проблемою, і ми пишемо про неї тут, а не внизу.

Мобільний застосунок має дві частини, які ламаються по-різному. Частина, що читає сенсори й обчислює щось — кут, бал, стан — і частина, що малює екрани. Ми тримаємо їх окремо, і не з естетичних причин: у нашому головному iOS-репозиторії файли ядра не імпортують інтерфейс, тож їх можна компілювати напряму за допомогою `swiftc` на Mac і запускати як звичайну програму, без симулятора і без телефону. Набір має 160 перевірок, згрупованих у 16 етапів; ми запускали його сьогодні, і він проходить повністю. Саме тому ми можемо сказати, що саме робить алгоритм, замість того щоб казати, що він «добре працює».

На iOS ми працюємо в Swift 6, з мінімальною цільовою версією iOS 17. Той самий репозиторій визначає п’ять застосунків як окремі цілі: чотири для iPhone і одну для macOS, а macOS-версія повторно використовує п’ятнадцять файлів із ядра iOS-версії, без копіювання. Сенсори читаються з джерела: інтерфейс Apple для сенсорів у навушниках надає орієнтацію голови, а застосунок порівнює поточний кут із положенням, яке користувач калібрує на початку сесії. Пороги — це не «внутрішні налаштування»: нижче 7° — рівно, між 7° і 13° — відхилення, понад 13° протягом трьох секунд запускає тривогу, а згладжування використовує експоненціальне середнє з коефіцієнтом 0,15. Усі чотири значення стоять у чотирьох послідовних рядках коду і їх можна показати.

Коли проєкт не потребує сенсорів, ми не рекомендуємо нативно. Третій напрям у портфоліо — це один код, який компілюється для iPhone, Android і браузера — React Native з Expo — а веб-варіант опублікований і відповідає сьогодні. Практична різниця для клієнта: одна анкета, один калькулятор або одна панель керування не виправдовують дві команди і два репозиторії; застосунок, який слухає сенсор на двадцять вибірок на секунду, так.

Чого ми ще не можемо. Жоден наш застосунок не в App Store або Google Play. Не тому, що не компілюється — компілюється, встановлюється на симулятор і проходить тести — а тому, що в конфігурації проєкту ідентифікатор команди Apple порожній, тобто немає реєстрації в Apple Developer Program, пов’язаної з цими збірками. На Mac є дійсна ідентичність підпису, типу розробки, якої достатньо для встановлення на власний пристрій і недостатньо для магазину. Для проєкту клієнта обліковий запис розробника відкривається на компанії клієнта і лишається його — це обговорення треба вести на початку, а не наприкінці.

Що входить

Робота, за складовими

Читаємо сенсор у джерела, а не через проміжну бібліотеку

На iOS ми використовуємо напряму інтерфейси Apple: `CMHeadphoneMotionManager` для орієнтації голови з навушників, з делегатом для підключення і відключення, та явну перевірку стану авторизації перед запуском потоку. Якщо сенсор недоступний, стан переходить у `waiting`, а не в мовчазну помилку.

Ядро логіки не знає, що існує екран

Файли аналізу, калібрування, історії та політик — це звичайний Swift. Вони компілюються з `swiftc` разом із файлом тестів і виконуються як бінарний файл командного рядка. Результат сьогодні: 160 перевірок, 16 етапів по 10, усі пройдені. Тест, який запускається за дві секунди на Mac, запускається при кожній зміні; той, що потребує симулятор, — ні.

Інтерфейс будується поверх ядра, одночасно на iOS і на macOS

SwiftUI для обох. Ціль macOS з того самого репозиторію включає п’ятнадцять файлів із ядра iOS як спільні джерела, а не як скопійовані файли, тож виправлення в алгоритмі одночасно потрапляє в обидва застосунки.

Один код для iOS, Android і web, коли це правильний вибір

Expo поверх React Native, з `react-native-web` для браузерної версії, шрифтами, завантаженими з пакета, і окремими піктограмами застосунку для Android, включно з монохромною версією, яку вимагає система. Одна й та сама програма працює на телефоні й на сторінці, з одного репозиторію.

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

Ми не «читаємо HealthKit», а таблиця в коді: одинадцять типів даних (постуральні кути, сирий рух із навушників, пульс і варіабельність, сон, ходьба, аудіовплив, керовані сесії та інше) і п’ять можливих режимів (лише локально, читання з HealthKit, запис тренування, запис сесії розслаблення, запис маршруту). Кожен тип отримує один із режимів явно. Друга iOS-застосунок у портфоліо, з приватного проєкту, використовує той самий підхід на 40 файлах Swift.

Файли конфіденційності, які вимагає Apple, присутні від початку

Чотири цілі мають `PrivacyInfo.xcprivacy`, включений як ресурс збірки, а не доданий похапцем після першої відмови. Описання використання для руху, камери, мікрофона, HealthKit і сповіщень написані румунською, у конфігурації проєкту, а не згенеровані автоматично.

Що виходить із телефона: у випадку еталонного iOS-застосунку — нічого

Пошук за `URLSession` у вихідних кодах iOS-цілі повертає нуль результатів. Історія записується на пристрої через `FileManager` і `UserDefaults`, з можливістю видалення файлу. Єдиний мережевий код у репозиторії — у функції macOS-застосунку, в окремому файлі. Коли застосунку не потрібен сервер, ми його не додаємо.

Початковий шлях, перевірений як стан, а не як екран

Онбординг має 24 кроки з унікальними та впорядкованими ідентифікаторами, а деякі кроки вимагають явного підтвердження (межі сенсора, робота на передньому плані, конфіденційність, підготовка), без якого кнопка продовження залишається заблокованою. Стан кодується і відновлюється. Десять із 160 тестів перевіряють саме це.

Підписання й розповсюдження, сказані на початку

Перевіряємо, який обліковий запис розробника існує, на яку компанію, хто ним керує і чого бракує, щоб збірку можна було підписати для магазину. Це короткий і нудний список, але саме він вирішує, чи буде застосунок працювати на нашому телефоні, чи потрапить до користувачів.

Як це виглядає

Шлях, крок за кроком.

01

Вирішуємо: нативно чи спільним кодом, на основі сенсорів

Питання не в уподобанні, а в апаратному забезпеченні: чи потрібні застосунку рух, здоров’я, Bluetooth, камера в реальному часі, фонова робота? Якщо так — нативно. Якщо йдеться про форми, обчислення, списки та панель керування, один код для iOS, Android і web робить ту саму роботу з удвічі меншим обслуговуванням. Ми передаємо письмове рішення з обґрунтуванням.

02

Будуємо ядро до екранів і покриваємо його тестами, що працюють без телефона

Алгоритм, пороги, стани та збереження. Тести компілюються безпосередньо на Mac і запускаються за кілька секунд. Ми передаємо набір тестів і його результат, а не обіцянку якості.

03

Накладаємо інтерфейс на ядро і запускаємо на симуляторі та на пристрої

SwiftUI на iOS і macOS, або React Native, коли ми обрали спільний код. Перевіряємо початковий шлях, дозволи та поведінку, коли сенсор відсутній або від’єднується посеред сесії — випадок, що часто ламається на практиці.

04

Готуємо розповсюдження, з обліковим записом клієнта

Ідентифікатор пакета, версія, піктограми, файл конфіденційності, описи дозволів, ідентифікатор команди з Apple Developer Program. Саме тут блокуються проєкти, які не обговорили обліковий запис на початку — зокрема й наші, і це саме причина, чому ми записуємо цей крок на папері.

Ce vede utilizatorulЗастосунокМережа та безпекаРозміщення та даніКОЖЕН ШАР, ОБРАНИЙ І ПОЯСНЕНИЙ
Чотири шари, знизу вгору: сенсори платформи (рух із навушників, HealthKit, сповіщення); ядро логіки на звичайному Swift, яке не імпортує інтерфейс і тому може запускатися на Mac без телефону; інтерфейс SwiftUI, той самий для iPhone і macOS; і шар дистрибуції — файл приватності, заявлені дозволи, підпис. Останній шар — єдиний, який ми не пройшли до кінця, і тому він намальований контуром.

Дані

Чого торкаємося, де воно лежить і скільки лишається

Запитання, які ставить кожен, у кого є відповідальний за захист даних, — поставлені тут раніше, ніж їх поставить він.

Де зберігаються дані, коли у програми немає сервера
На пристрої. Історія постави серіалізується у власний файл через `FileManager`, а налаштування — у `UserDefaults`, із ключами, що мають префікс застосунку. Є команда для видалення файлу історії. Нічого не надсилається на сервер із простої причини, що застосунок не містить мережевого коду.
Дані про здоров’я читаються лише зі згоди, і лише ті, що заявлені
HealthKit вимагає згоди користувача на рівні типу даних, а застосунок у конфігурації заявляє, для чого їх запитує. Політика в коді явно розділяє те, що читається, від того, що записується назад у Health. Діагностика не виконується, і нічого не передається нам.
Що Apple вимагає від вас заявити, незалежно від того, що ви робите з даними
Файл конфіденційності застосунку та описи використання для кожного дозволу (рух, камера, мікрофон, здоров’я, сповіщення). Це умова прийняття в магазині, а не опція, і вони пишуться мовою, якою говорить користувач.
Скільки зберігається
Стільки, скільки вирішить користувач, коли дані зберігаються на телефоні: видалення застосунку забирає їх із собою, а видалення зсередини застосунку є явною операцією. Коли в проєкті все ж є сервер, строк зберігання прописують у контракті до першого рядка коду, бо він визначає, що можна і чого не можна побудувати.
Обліковий запис розробника та ключі підписування
Вони залишаються у клієнта, на фірмі клієнта. Ми працюємо з делегованим доступом. Причина практична, а не принципова: застосунок, опублікований на обліковому записі постачальника, стає неможливо перенести в день, коли постачальник змінюється.

Випадок

Додаток для iPhone, який вимірює одну річ і вимірює її перевірно

Ситуація

Власний проєкт, не клієнтський: таймер концентрації, який спостерігає, наскільки сильно голова нахилилася від положення, відкаліброваного користувачем на початку сесії. Нам потрібен був випадок, у якому твердження про алгоритм можна було б показати, а не описати.

Що ми збудували

Я відокремив ядро від інтерфейсу і записав пороги в коді, а не в документації: нижче 7° — прямо, між 7° і 13° — ковзання, понад 13° протягом 3 секунд запускає гаптичне сповіщення, з експоненційним згладжуванням на коефіцієнті 0,15. Дані надходять з інтерфейсу Apple для сенсорів у навушниках, з явною обробкою від’єднання. Onboarding має 24 кроки, з яких чотири вимагають підтвердження меж перед тим, як дозволити користувачеві йти далі. Я побудував із того самого ядра й версію для macOS.

Що вийшло

Сукупність логіки компілюється з `swiftc` і запускається на Mac за кілька секунд: 160 перевірок у 16 етапах, усі пройдені під час останнього запуску. Додаток встановлюється і запускається на симуляторі. Будь-яке твердження з абзацу вище можна перевірити, відкривши чотири рядки коду.

Чого цей випадок не каже

Його немає в App Store. У конфігурації проєкту ідентифікатор команди Apple порожній, тож build не можна підписати для розповсюдження — це відсутність реєстрації в програмі Apple, а не відсутність функціональності. І ще одне обмеження, саме продукту, а не коду: він вимірює нахил голови, а не здоров’я хребта, і працює з навушниками, які відкривають сенсори руху через інтерфейс Apple.

Питання

Про що нас питають, перш ніж зателефонувати

У вас є опублікований в App Store застосунок, щоб я міг його побачити?

Ні. Жоден із наших застосунків не є в App Store або Google Play, і правильно запитати про це першою. Що ми можемо показати: п’ять цілей застосунку, визначених у тому самому проєкті, з яких чотири для iPhone і одна для macOS, які компілюються та встановлюються на симулятор, плюс сукупність логіки з 160 перевірок, запущена на вимогу перед вами. Блокування публікації — порожній `DEVELOPMENT_TEAM` у конфігурації проєкту, тобто відсутність ідентифікатора команди з Apple Developer Program. На Mac є чинна ідентичність підписування, типу development — її достатньо для встановлення на власний пристрій, але не для магазину.

Тоді звідки я знаю, що ви можете довести застосунок до магазину?

З нічого, якщо ви просите доказ публікації — у нас його немає, і ми його не вигадуємо. Що ви можете перевірити — це решту ланцюга: код, який компілюється, тести, що проходять, наявні файли конфіденційності, задекларовані дозволи, налаштовані іконки та версії. Крок, якого бракує, — це платна реєстрація в програмі Apple, оформлена на фірму, що володіє застосунком. У клієнтському проєкті той обліковий запис належить клієнту і відкривається на початку, саме щоб він не став кроком, який когось несподівано наздожене наприкінці.

Нативний чи один застосунок для iOS і Android?

Залежить від сенсорів. Якщо застосунок зчитує рух, здоров’я, Bluetooth або камеру в реальному часі, — нативний: ті інтерфейси належать платформі, і будь-який проміжний шар додає затримку та нові способи зламатися. Якщо застосунок по суті є формами, обчисленнями та екранами результату, один код покриває iOS, Android і браузер з одного репозиторію. У портфоліо маємо обидва варіанти й обираємо їх із письмовим обґрунтуванням, а не за замовчуванням.

Ви робите також застосунки для Android?

Через спільний код — так: той самий проєкт Expo створює build для Android, уже з налаштованими адаптивною іконкою та монохромною версією, які вимагає система. Нативного Android на Kotlin у нашому портфоліо немає — якщо проєкт цього вимагає, ми кажемо це до підписання, а не після.

Як ви перевіряєте, що логіка правильна, якщо у вас немає мого телефона?

Завдяки тому, що ядро не залежить від екрана. Файли аналізу компілюються окремо, компілятором Swift, і запускаються як програма на Mac. Поточна сукупність має 160 перевірок у 16 етапах і виконується за кілька секунд. Те, що так не перевірити — як відчувається вібрація, як виглядає екран на конкретному телефоні, як поводяться навушники, коли виходять із одного вуха, — тестується на пристрої, і це називається окремо.

Чи може застосунок читати мої дані зі Здоров’я?

Лише якщо ви схвалите їх, тип за типом, на екрані Apple, і лише ті, що заявлені. У коді політика є явною для кожного виду даних: одні залишаються лише на телефоні, інші читаються з HealthKit, інші можна записувати назад як тренування або як сесію розслаблення. Діагностика не виконується, а дані до нас не надходять.

Що відбувається з даними, якщо застосунок не має сервера?

Вони залишаються на телефоні. В еталонному iOS-застосунку з нашого портфоліо немає жодного мережевого виклику — пошук за `URLSession` у вихідному коді цього target повертає нуль результатів. Історія записується в локальний файл і може бути видалена з застосунку. Коли проєкту потрібен сервер, ми його будуємо, але не додаємо його за замовчуванням.

Чи можемо ми взяти в роботу наявний застосунок, зроблений кимось іншим?

Починається з перевірки сумісності, а не з пропозиції: яку версію платформи він цілить, які залежності має і які з них ще підтримуються, чи відтворюється build на чистій машині, хто володіє обліковим записом розробника і ключами. Є проєкти, в яких чесна відповідь така: переписати ядро коштує менше, ніж підтримувати його.

Ви робите і desktop-застосунки?

На macOS — так, і з того самого ядра: у нашому основному репозиторії target для macOS повторно використовує п’ятнадцять файлів логіки з застосунку для iPhone як спільні sources. Windows не входить до портфоліо.

На чому ґрунтуються твердження вище (11 джерел)
  1. Веб-варіант застосунку зі спільним кодом відповідає сьогодніhttps://longevita-alpha.vercel.app/ · 2026-09-06

10 з них — це код і файли з наших репозиторіїв. Ми не публікуємо їхні назви чи номери рядків: разом, на одній сторінці, це надто точно описало б, як побудовані системи, які належать не лише нам. Ми проходимо їх разом із вами, у репозиторії, за запитом — перевірка залишається можливою, просто вона відбувається в розмові.

Що вам хотілося б налагодити?

Розкажіть про свій процес. Разом визначимо, що варто збудувати, що можна підключити і як перевіримо результат.

Поговорімо