Тестування сумісності мобільного додатку на різних версіях ОС
Підтримка iOS 15 та Android 10 означає не просто «не падає» — означає, що всі функції працюють коректно, без артефактів UI, без silent failures у місцях, де новий API повернув би помилку, а старий просто нічого не робить. Розрив між minSdkVersion 26 та compileSdkVersion 35 — це 8 років еволюції Android API. Пропустити одну deprecated-заміну — та на Android 10 у продакшені упасти NoSuchMethodError.
Матриця версій: як вибрати
Тестувати на кожній версії ОС — нераціонально. Принцип:
| Пріоритет | Версії iOS | Версії Android |
|---|---|---|
| Обов'язково | Поточна − 1 (iOS 17, 18) | Android 12, 13, 14 (API 31–34) |
| Важливо | minDeploymentTarget (iOS 15) | minSdkVersion (API 26–28) |
| По аналітиці | Версії з часткою > 5% у вашої ЦА | То ж саме |
Аналітика Firebase або Mixpanel по os_version дає реальну картину. Якщо 8% користувачів на iOS 15 — тестуємо. Якщо 0.3% на iOS 14 — ні.
Deprecated API: де ловити проблеми
Android
Проблеми з застарілими API чаще всього у наступних областях:
Сповіщення (API 26+). На Android 8+ усі сповіщення вимагають NotificationChannel. Без нього notify() мовчки ігнорується. Додаток думає, що показав сповіщення — ні.
// Перевірка: створюємо канал тільки на API 26+
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
val channel = NotificationChannel(CHANNEL_ID, "General", NotificationManager.IMPORTANCE_DEFAULT)
notificationManager.createNotificationChannel(channel)
}
Разрешення (API 33+). READ_EXTERNAL_STORAGE на Android 13+ замінена на гранульовані: READ_MEDIA_IMAGES, READ_MEDIA_VIDEO, READ_MEDIA_AUDIO. Запит старого разрешення на Android 13 не дає доступу до медіа.
Foreground Service (API 34+). Android 14 вимагає вказувати тип foreground service: dataSync, mediaPlayback, location та ін. Без типу — SecurityException при старті.
Інструмент для перевірки: lint з правилом NewApi. У Android Studio: Analyze → Inspect Code. Флаги:
android {
lint {
abortOnError = true
error += setOf("NewApi", "InlinedApi")
}
}
NewApi — виклик API, доступного вище minSdkVersion без @RequiresApi або перевірки Build.VERSION.SDK_INT. Автоматично знаходить більшість проблем сумісності до запуску.
iOS
@available та #available — обов'язкові паттерни:
if #available(iOS 16.0, *) {
// NavigationStack, доступна з iOS 16
NavigationStack { ... }
} else {
NavigationView { ... } // deprecated, але працює до iOS 15
}
Компілятор попереджує про використання нового API без @available-перевірки — ловиться статично. Але є нюанс: попередження, не помилка. У великих кодових базах такі попередження теряються.
Найчастіша втрата: SwiftUI-компоненти, додані у нових версіях iOS. ContentUnavailableView (iOS 17), NavigationStack (iOS 16), Charts (iOS 16) — без fallback додаток упасти на iOS 15 з dyld: Symbol not found.
Інструмент: Xcode Simulator з конкретними версіями
Завантажуємо додаткові runtime: Xcode → Settings → Platforms → + → iOS 15.x Simulator Runtime. Після завантаження створюємо симулятор потрібної версії та тестуємо на ньому.
Тестовий процес
Не гоняємо весь E2E-suite на кожній версії — це надлишково. Диференційований підхід:
- Smoke-тест на мінімальній підтримуваній версії: основні флоу працюють, додаток стартує.
- Повна регресія на поточній версії ОС.
- Точкове тестування на проміжних версіях — тільки функції, які використовують API конкретної версії.
Список «рисковихх» функцій по версіям ведемо у документі: функція → мінімальна версія → тестується на.
Як виявляємо несумісності без пристроїв
Firebase Test Lab — тестування на віртуальних пристроях з різними API-рівнями. Швидко, дешево, охоплює більшість несумісностей. Для тонших проблем (кастомні прошивки Samsung, MediaTek vs Qualcomm) — реальні пристрої.
Статичний аналіз — lint для Android, Xcode Build & Analyze для iOS. Запускаємо у CI на кожен PR.
Строки
2–3 дні — складання матриці версій по аналітиці, тестування на приоритетних версіях, статичний аналіз на deprecated API, звіт з матрицею несумісностей. Вартість розраховується індивідуально.







