Перейти к основному содержимому
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. Все четыре значения находятся в четырёх последовательных строках кода и могут быть показаны.

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

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

Что входит

Работа, по составляющим

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

На 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 требует согласия пользователя на уровне типа данных, а приложение в конфигурации объявляет, зачем оно их запрашивает. Политика в коде явно разделяет то, что читается, от того, что записывается обратно в Здоровье. Диагностика не выполняется и ничего к нам не передается.
Что Apple требует указать, независимо от того, что вы делаете с данными
Файл конфиденциальности приложения и описания использования для каждого разрешения (движение, камера, микрофон, здоровье, уведомления). Это условие принятия в магазине, а не опция, и они пишутся на языке, на котором говорит пользователь.
Сколько хранится
Сколько решает пользователь, когда данные находятся на телефоне: удаление приложения уносит их с собой, а удаление из приложения — это явная операция. Когда у проекта все же есть сервер, срок хранения прописывается в контракте до первой строки кода, потому что он определяет, что можно и что нельзя построить.
Учетная запись разработчика и ключи подписи
Они остаются у клиента, на компании клиента. Мы работаем с делегированным доступом. Причина практическая, а не принципиальная: приложение, опубликованное на аккаунте поставщика, становится невозможно перенести в день, когда поставщик меняется.

Случай

Приложение для iPhone, которое измеряет одну вещь и измеряет ее проверяемо

Ситуация

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

Что мы построили

Мы отделили ядро от интерфейса и записали пороги в коде, а не в документации: ниже 7° — прямо, между 7° и 13° — смещение, выше 13° в течение 3 секунд запускает тактильное оповещение, с экспоненциальным сглаживанием при коэффициенте 0,15. Данные поступают через интерфейс Apple для датчиков в наушниках, с явной обработкой отключения. Онбординг состоит из 24 шагов, из которых четыре требуют подтверждения ограничений, прежде чем позволить пользователю продолжить. Из того же ядра мы также построили вариант для macOS.

Что получилось

Набор логики компилируется с помощью `swiftc` и запускается на Mac за несколько секунд: 160 проверок в 16 этапах, все пройдены при последнем запуске. Приложение устанавливается и запускается в симуляторе. Любое утверждение из абзаца выше можно проверить, открыв четыре строки кода.

Чего этот случай не говорит

Его нет в App Store. В конфигурации проекта идентификатор команды Apple пуст, поэтому сборку невозможно подписать для распространения — это отсутствие регистрации в программе 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 создает сборку для Android, с адаптивной иконкой и монохромной версией, требуемой системой, уже настроенными. Нативного Android на Kotlin у нас в портфолио нет — если проект этого требует, мы говорим об этом заранее, а не после подписания.

Как вы проверяете, что логика правильная, если у вас нет моего телефона?

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

Может ли приложение читать мои данные из Здоровья?

Только если вы их одобрите, тип за типом, на экране Apple, и только те, которые объявлены. В коде политика явно задана для каждого вида данных: одни остаются только на телефоне, другие читаются из HealthKit, другие можно записывать обратно как тренировку или как сеанс расслабления. Диагностика не выполняется, а данные к нам не поступают.

Что происходит с данными, если у приложения нет сервера?

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

Можем ли мы взять существующее приложение, сделанное кем-то другим?

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

Вы делаете и десктопные приложения?

Для macOS — да, и из того же ядра: в нашем основном репозитории macOS-цель повторно использует пятнадцать файлов логики из приложения для iPhone как общие исходники. Windows не входит в портфолио.

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

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

Что вы хотели бы наладить?

Расскажите о своём процессе. Вместе определим, что стоит построить, что можно подключить и как проверим результат.

Давайте обсудим