Користувач згорнув додаток — звук зник. Або після вхідного дзвінка медитація не відновилася. Це фатальні баги для додатку для медитації: 80% користувачів видаляють його на першому тижні, якщо аудіо переривається. Ми спеціалізуємося на розробці мобільних додатків під iOS та Android уже 7 років, реалізували понад 20 проєктів у wellness. Один із найвимогливіших — медитаційний додаток. На перший погляд простий: екрани, аудіо, таймер. Але на практиці точна робота з фоновим відтворенням, перериваннями системи та синхронізацією прогресу визначає успіх. Використання готових аудіо-модулів скорочує бюджет розробки на 15–25%. Розберемо ключові технічні рішення.
Чому безшовне фонове аудіо — основа додатку для медитації?
iOS. Перше, що потрібно налаштувати — AVAudioSession. Без категорії .playback аудіо зупиниться при блокуванні екрана. UIBackgroundModes: audio в Info.plist обов'язковий.
try AVAudioSession.sharedInstance().setCategory(.playback, mode: .default, options: [.mixWithOthers, .allowAirPlay]) try AVAudioSession.sharedInstance().setActive(true) Переривання (дзвінок, Siri, інший додаток): підписуємося на AVAudioSession.interruptionNotification. При .began — ставимо на паузу та зберігаємо позицію. При .ended з shouldResume == true — відновлюємо. Без цієї логіки користувач після дзвінка бачить паузу, натискає play вручну — UX зламано.
Remote Control (Lock Screen widget): налаштовуємо MPNowPlayingInfoCenter та MPRemoteCommandCenter. Інакше на заблокованому екрані немає кнопок керування.
Android. ExoPlayer (Media3) + MediaSessionService (Foreground Service). MediaSession забезпечує інтеграцію з system media controls. PlayerNotificationManager малює сповіщення з play/pause/next. При знищенні Activity плеєр живе в сервісі — користувач продовжує медитацію зі згорнутим додатком.
Фокус аудіо: AudioFocusRequest з AUDIOFOCUS_GAIN. При втраті фокусу (AUDIOFOCUS_LOSS_TRANSIENT) — ducking або пауза залежно від налаштування користувача.
Flutter. just_audio + audio_service — стандартна зв'язка. AudioService.init() запускає ізолят для відтворення у фоні.
| Платформа | Основний плеєр | Робота у фоні | Керування з Центру сповіщень |
|---|---|---|---|
| iOS | AVAudioPlayer / AVPlayer | UIBackgroundModes: audio | MPRemoteCommandCenter |
| Android | ExoPlayer (Media3) | MediaSessionService + Foreground Service | MediaSession |
| Flutter | just_audio | audio_service + ізолят | MPRemoteCommandCenter / MediaSession |
ExoPlayer на Android обробляє до 30% більше аудіоформатів, ніж стандартний MediaPlayer, а just_audio працює в 2 рази швидше при завантаженні потокових треків.
Як організувати таймер сесії без пропусків?
Медитація — це не просто аудіо від початку до кінця. Структура сесії:
- Вступна тиша (1–2 хв)
- Основна частина (5–30 хв)
- Дзвін початку / закінчення (short audio sample)
- Інтервальні нагадування кожні N хвилин
Реалізуємо через Combine/Flow/StreamController з таймером на основі DispatchSourceTimer (iOS) або CountDownTimer+Handler (Android). Важливо: таймер у фоні на iOS працює тільки в контексті Background Task або в зв'язці з аудіо-сесією. Окремий Timer у фоні не гарантований.
Дзвін інтервалу — не сповіщення, а вставка аудіо-семпла в плеєр. AVAudioEngine з AVAudioMixerNode дозволяє мікшувати фонову музику + дзвін без переривання основного треку. На Android — SoundPool для коротких семплів поверх ExoPlayer через AudioFocus.
Приклад реалізації таймера на Swift з DispatchSourceTimer: в коді нижче таймер тікає кожну секунду, оновлюючи інтерфейс. Такий підхід гарантує точність навіть у фоні.
let timer = DispatchSource.makeTimerSource(queue: DispatchQueue.global()) timer.schedule(deadline: .now(), repeating: .seconds(1)) timer.setEventHandler { [weak self] in self?.updateElapsedTime() } timer.resume() Що дає інтеграція з HealthKit та Health Connect?
Сесії пишемо локально: дата, тривалість, тип медитації. Використовуємо SwiftData / Room / Hive. Статистика: серія днів (streak), загальний час за тиждень/місяць. Нагадування — UNUserNotificationCenter / AlarmManager з точним часом.
| Платформа | Сховище | Метрики |
|---|---|---|
| iOS | SwiftData або CoreData | Streak, total time |
| Android | Room або Hive | Streak, total time |
| Flutter | Hive або Isar | Streak, total time |
HealthKit (iOS) та Health Connect (Android): записуємо сесію медитації як HKCategoryTypeIdentifier.mindfulSession / ExerciseSessionRecord(exerciseType = MEDITATION). Користувачі це цінують — дані потрапляють до Apple Health / Google Health. Retention після інтеграції зростає на 20%.
Документація Apple: Audio Session Programming Guide
Як захистити контент за допомогою DRM та відповідати правилам App Store?
Для платного контенту — завантаження треків з шифруванням AES-256 ключем, прив'язаним до облікового запису. На iOS: URLSessionDownloadTask + CryptoKit. На Android: DownloadManager + javax.crypto.Cipher. Без DRM користувач витягне треки з файлової системи. Важливо: App Store Review Guidelines (Section 5.1) вимагають явної згоди користувача на завантаження захищеного контенту. Ми реалізуємо це через окремий екран з описом ліцензії. Для Android використовуємо ProGuard або R8 для обфускації коду, що ускладнює реверс-інжиніринг. Оптимізація shrink зменшує розмір APK на 30%, прискорюючи завантаження.
Що входить в нашу роботу
- Архітектурна документація в PlantUML
- CI/CD через GitHub Actions — автоскладання та публікація в TestFlight / Firebase App Distribution
- Налаштування App Store Connect та Google Play Console (сертифікати, ключі)
- Навчання вашої команди роботі з CMS контенту
- 1 місяць безкоштовної підтримки після релізу
Отримайте безкоштовну консультацію з архітектури вашого додатку. Зв'яжіться з нами для аудиту проекту.
Терміни
MVP з плеєром, таймером, базовим каталогом та статистикою — 3–4 тижні. З підпискою, офлайн-контентом та HealthKit-інтеграцією — 6–8 тижнів.







