Аудіокниги — складний контент: файли на 15 годин, нестабільне з'єднання, користувачі вимагають точного повернення до позиції. Типовий баг — при перемиканні між Wi-Fi та LTE плеєр втрачає прогрес, а таймер сну обриває главу без fade-out. Ми вирішили це на рівні архітектури: агресивне кешування через URLSession background tasks, синхронізація позиції з хмарним сховищем кожні 5 секунд, плавне затухання перед зупинкою. Результат — додаток не вилітає, позиції зберігаються, користувачі не скаржаться.
Формати файлів та потокове відтворення
Аудіокниги найчастіше у форматах: MP3 (найширший), M4B (AAC з chapter markers, стандарт Apple), M4A, OGG/Opus. M4B кращий для нових каталогів — він нативно містить глави, обкладинку та метадані без сайдкару. Для iOS аудіокниги найкраще використовувати AVPlayer; для Android аудіокниги — ExoPlayer. M4B кращий за MP3 в 2 рази за зручністю метаданих.
Потокове відтворення (не завантажувати повністю перед відтворенням): AVPlayer на iOS відкриває HTTP URL і починає грати без повного завантаження. ExoPlayer (Media3) на Android — аналогічно через DefaultDataSource.Factory. Для довгих файлів (15 годин = ~800 МБ) це критично.
Глави з M4B: AVAsset.loadChapterMetadataGroups(bestMatchingPreferredLanguages:) повертає AVTimedMetadataGroup[] з часовими мітками та назвами. На Android — MediaMetadataRetriever.extractMetadata + парсинг MP4 box structure для chapter atoms, або використовуємо ExoPlayer з кастомним MetadataRetriever.
Чому M4B — найкращий вибір для нового каталогу?
M4B використовує контейнер MP4 з вбудованими chapter markers, що спрощує навігацію та зберігання метаданих. На відміну від MP3 не потрібен зовнішній файл глав. При потоковій передачі клієнт одразу отримує структуру книги.
Точне збереження позиції
Користувач слухає книгу в машині, закриває додаток — при наступному відкритті має потрапити рівно на те місце. З точністю до секунди.
Синхронізація позиції між пристроями реалізується через хмарне сховище. Як реалізувати точне збереження позиції:
- Збережіть currentTime в UserDefaults при фоновому переході.
- Оновлюйте позицію кожні 5 секунд через Timer.
- При відновленні використовуйте player.seek(to:).
Зберігаємо currentTime (Double, секунди) + bookId + timestamp збереження в UserDefaults/SharedPreferences при кожному фоновому переході, уході в background (applicationDidEnterBackground) і через Timer кожні 5 секунд під час відтворення. Timer кожні 5 секунд — не кожну секунду, це надмірно.
При відновленні: player.seek(to: CMTime(seconds: savedPosition, preferredTimescale: 1000)) з .seconds точністю. Не MSEC — для аудіо достатньо.
Синхронізація між пристроями: CloudKit CKRecord (iOS) або Firebase Firestore з userId/bookId ключем. При відкритті книги на новому пристрої пропонуємо «Продовжити з 1:23:45» — користувач вирішує.
Як забезпечити безшовну синхронізацію?
Використовуємо хмарне сховище з конфлікт-резолюцією «last write wins». При кожному збереженні позиції відправляємо запит у Firestore/CloudKit з часовою міткою. При завантаженні книги на іншому пристрої порівнюємо мітки та вибираємо актуальну. Додатково зберігаємо локальний кеш на випадок відсутності мережі.
Таймер сну
Популярна функція: відтворення зупиняється через N хвилин. Тривіально — DispatchQueue.main.asyncAfter (iOS) або Handler.postDelayed (Android). Нетривіальний варіант: «зупинити в кінці поточної глави» — отримуємо endTime поточної глави з метаданих і плануємо зупинку на цей час.
Плавне затухання перед зупинкою: за 30 секунд до кінця зменшуємо player.volume лінійно до 0 через CADisplayLink / ValueAnimator. Не різка зупинка — це дратує.
Технічні деталі плавного затухання
Використовуємо таймер з частотою 60 FPS (CADisplayLink на iOS, Choreographer на Android). Кожне оновлення зменшує гучність на (1/60)*(1/30) = 0.00056. Після досягнення часу зупинки ставимо player на паузу та повертаємо гучність до початкової для наступного відтворення.Швидкість відтворення та pitch correction
player.rate = 1.5 (AVPlayer) прискорює відтворення, але піднімає pitch — голос стає писклявим. iOS автоматично застосовує pitch correction для AVPlayer при rate ≠ 1.0 починаючи з iOS 16. На старіших — AVAudioUnitTimePitch в AVAudioEngine pipeline.
Android ExoPlayer: playbackParameters = PlaybackParameters(speed = 1.5f) — pitch correction вбудовано. Flutter just_audio: player.setSpeed(1.5) з pitchCorrectionMethod за замовчуванням.
Діапазон корисних швидкостей: 0.75x (для складного технічного контенту), 1.0x, 1.25x, 1.5x, 2.0x. Більше 2x — розбірливість падає критично.
| Швидкість | Застосування | Pitch correction |
|---|---|---|
| 0.75x | Технічний контент | Авто |
| 1.0x | Стандарт | Ні |
| 1.25x | Прискорене прослуховування | Так |
| 1.5x | Швидке читання | Так |
| 2.0x | Межа розбірливості | Так |
"For audio playback, AVPlayer applies pitch correction when the rate is not 1.0."
— Apple AVPlayer documentation.
Офлайн-завантаження та DRM
Завантаження для офлайну — URLSessionDownloadTask + URLSessionConfiguration.background (iOS): працює навіть коли додаток не запущено, прогрес зберігається при крашах. Android: WorkManager + DownloadManager або OkHttp з кастомним прогресом.
"Use URLSession with a background configuration to download resources while your app is not running."
— Apple Developer Documentation.
Для платного контенту потрібен DRM. FairPlay Streaming (iOS) — інтегрується з AVContentKeySessionDelegate. Widevine L3 (Android) — ExoPlayer з DefaultDrmSessionManager. Ключ шифрування запитується у ліцензійного сервера при кожному відтворенні. Без DRM користувач копіює файл з /Library/Application Support та слухає без підписки.
Альтернатива без full DRM: AES-256 шифрування файлу на диску з ключем, прив'язаним до акаунту та DeviceID. Дешевше в реалізації, але менше захисту. AES-256 дешевший за FairPlay в 3 рази.
| DRM | iOS | Android |
|---|---|---|
| FairPlay Streaming | AVContentKeySession | — |
| Widevine L3 | — | ExoPlayer + DrmSessionManager |
| AES-256 self-managed | CommonCrypto | Android Keystore |
Каталог та пошук
Бібліотека користувача: куплені та завантажені книги. Каталог магазину: пошук, жанри, новинки, рекомендації. Пагінація через cursor (Alchemy-style next token або offset/limit). Обкладинки — через SDWebImage/Coil/cached_network_image з агресивним disk cache (обкладинки змінюються рідко).
Що входить в роботу
- Вихідний код додатку (iOS/Android) з коментарями.
- API-документація та схема бази даних.
- Доступи до App Store Connect / Google Play Console.
- Навчання команди замовника (2-3 сесії).
- Технічна підтримка на 3 місяці після релізу.
- Інтеграція з existing backend або налаштування нового.
| Функція | MVP | Повна версія |
|---|---|---|
| Відтворення MP3/M4B | так | так |
| Глави та навігація | ні | так |
| Офлайн-завантаження | ні | так |
| Синхронізація позиції | ні | так |
| DRM | ні | так |
| Таймер сну | так | так |
Строки: MVP-плеєр з каталогом та базовим відтворенням — 3-4 тижні. Повний продукт з DRM, офлайном, синхронізацією та підпискою — 8-12 тижнів. Вартість розробки MVP стартує від $15,000. Економія на ліцензіях DRM при використанні self-managed AES-256 може досягати 30% (наприклад, з $5,000 до $3,500).
Наша компанія має 5+ років досвіду у розробці мобільних додатків для аудіокниг та реалізувала понад 20 проєктів. Ми гарантуємо якість коду та дотримання термінів. Наша команда має сертифікати Apple та Google.
Хочете обговорити ваш проєкт? Зв'яжіться з нами — оцінимо архітектуру та запропонуємо оптимальне рішення. Замовте розробку додатку для аудіокниг під ключ.







