У мобільній розробці робота з даними, кешування та синхронізація — три ключові аспекти, що визначають продуктивність і надійність додатка. Згідно з Google Android Architecture Guide, правильна архітектура обробки даних безпосередньо впливає на швидкість відгуку та користувацький досвід. Патерн Repository забезпечує єдину точку доступу до всіх джерел даних.
Головне
Патерн Repository — архітектурний підхід, при якому єдиний клас-репозиторій керує всіма операціями з даними, абстрагуючи віддалені REST API та локальне сховище Room або SwiftData. Така робота з даними дозволяє додатку отримувати інформацію спочатку з Memory Cache або Disk Cache, а потім із мережі, скорочуючи час відгуку. У мобільній розробці Repository став стандартом де-факто завдяки рекомендаціям Google та Apple.
Remote Data Source надає актуальну інформацію з сервера через HTTP-запити. Local Data Source — локальне сховище на пристрої, реалізоване через Room на Android або SwiftData на iOS. Репозиторій об'єднує обидва джерела: спочатку перевіряє локальний кеш, а при відсутності даних запитує віддалений API. Така організація роботи з даними дає додатку можливість функціонувати в офлайн-режимі та знижує навантаження на сервер.
class UserRepository(
private val remoteDataSource: UserRemoteDataSource,
private val localDataSource: UserLocalDataSource
) {
suspend fun getUsers(): List<User> {
localDataSource.getCachedUsers()?.let { return it }
val users = remoteDataSource.fetchUsers()
localDataSource.cacheUsers(users)
return users
}
}
class UserRepository {
private let remote: UserRemoteDataSource
private let local: UserLocalDataSource
func getUsers() async throws -> [User] {
if let cached = await local.getCached() { return cached }
let users = try await remote.fetch()
await local.save(users)
return users
}
}
LRU Cache (Least Recently Used) — алгоритм кешування, при якому при досягненні ліміту видаляється елемент, до якого найдовше не було звернень. У мобільних додатках LRU Cache застосовується для зображень, відповідей API та серіалізованих об'єктів. Правильне кешування даних скорочує кількість мережевих запитів і прискорює завантаження контенту. Кеш у мобільних додатках — обов'язковий компонент для високої продуктивності.
Memory Cache зберігає дані в оперативній пам'яті — доступ максимально швидкий, але обсяг обмежений розміром купи додатка. Disk Cache зберігає інформацію на файловій системі — працює повільніше, але вміщає більше та зберігається між сесіями. Оптимальна стратегія в мобільній розробці — дворівневий кеш: Memory Cache для гарячих даних та Disk Cache для холодних. При роботі з даними спочатку перевіряється кеш першого рівня в пам'яті, потім кеш другого рівня на диску.
class MemoryCache<K, V>(
private val maxSize: Int = 100
) {
private val cache = LinkedHashMap<K, V>(0, 0.75f, true)
fun get(key: K): V? = cache[key]
fun put(key: K, value: V) {
if (cache.size >= maxSize) {
cache.remove(cache.keys.first())
}
cache[key] = value
}
}
TTL-кеш (Time To Live) автоматично видаляє запис через заданий інтервал часу — підходить для даних API. Event-driven інвалідація очищає кеш при отриманні push-повідомлення про зміни. У мобільних додатках вибір стратегії кешування залежить від типу даних: зображення кешуються надовго, а новинна стрічка потребує частої інвалідації. Coil на Android та Kingfisher на iOS уже вбудували LRU Cache для роботи із зображеннями.
Offline Queue — структура даних, що зберігає операції користувача (створення, оновлення, видалення) у локальній базі, коли пристрій офлайн. При відновленні з'єднання Sync Manager послідовно застосовує ці операції до сервера. Така синхронізація даних гарантує, що жодна зміна не загубиться при тимчасовій втраті мережі. У мобільній розробці Offline Queue — критичний компонент для додатків із нестабільним з'єднанням.
Черга будується на таблиці в Room або SwiftData з полями: тип операції, JSON-тіло запиту, часова мітка та статус. Sync Manager — фоновий сервіс, який обробляє невиконані операції, відправляє їх на сервер, оновлює статус та видаляє успішні записи. Синхронізація даних через WorkManager на Android або BGTaskScheduler на iOS виконується навіть після перезавантаження пристрою. Використання Offline Queue в парі з грамотною роботою з даними забезпечує безшовний користувацький досвід.
@Entity
data class SyncOperation(
@PrimaryKey val id: Long,
val endpoint: String,
val method: String,
val body: String,
val createdAt: Long
)
class SyncManager(
private val dao: SyncOperationDao,
private val api: ApiService
) {
suspend fun syncPending() {
dao.getPendingOperations().forEach { op ->
try {
api.execute(op.endpoint, op.method, op.body)
dao.delete(op.id)
} catch (e: Exception) {
// retry on next cycle
}
}
}
}
Експоненціальна затримка між ретраями (1с, 2с, 4с, 8с) захищає сервер від лавинного навантаження та запобігає нескінченним повторам. Ліміт ретраїв — 5 спроб — запобігає переповненню черги. Синхронізація в мобільних додатках із підтримкою ідемпотентності на сервері дозволяє безпечно повторювати операції, уникаючи дублікатів. Це особливо важливо для фінансових транзакцій та замовлень.
Conflict Resolution — набір стратегій для ситуацій, коли одні й ті самі дані змінено на різних пристроях одночасно. Базова синхронізація даних потребує вибору підходу: Last-Write-Wins (перемагає останній запис), версіонування (перемагає вища версія) або ручне вирішення. У складних сценаріях застосовуються CRDT (Conflict-Free Replicated Data Types), що гарантують математичну збіжність даних.
Last-Write-Wins найпростіше в реалізації, але може втратити зміни користувача. Version Vector — кожен запис зберігає номер версії та ідентифікатор пристрою; конфлікт виникає при неспівпадінні версій. CRDT — найнадійніша, але складна стратегія: дані математично сходяться до єдиного стану без централізованого координатора. Синхронізація в мобільних додатках на основі CRDT використовується в спільному редагуванні Google Docs та синхронізації нотаток Notion.
При оновленні додатка структура локальної БД змінюється: додаються колонки, таблиці, індекси. Schema Migration — процес перетворення існуючої бази до нової схеми без втрати даних. Room підтримує міграції через клас Migration зі старою та новою версією. SwiftData використовує VersionedSchema для опису змін. Правильна синхронізація даних між версіями додатка потребує, щоб міграції були протестовані ідемпотентно.
val migration1to2 = object : Migration(1, 2) {
override fun migrate(database: SupportSQLiteDatabase) {
database.execSQL("ALTER TABLE users ADD COLUMN avatar_url TEXT")
}
}
@Database(
entities = [User::class],
version = 2
)
abstract class AppDatabase : RoomDatabase() {
abstract fun userDao(): UserDao
}
enum ConflictStrategy {
case lastWriteWins
case versionVector
case crdt
}
struct VersionedDocument {
let id: String
let version: Int
let data: Data
let editedBy: String
func resolve(with remote: VersionedDocument) -> VersionedDocument {
return version >= remote.version ? self : remote
}
}
Room — бібліотека Google для локального зберігання на Android, побудована поверх SQLite та яка надає анотації для декларативного опису запитів. SwiftData — фреймворк Apple для iOS, macOS, watchOS та visionOS, спадкоємець Core Data з лаконічним синтаксисом Swift Macro. Обидва інструменти вирішують завдання роботи з даними на пристрої, але з різними підходами до організації коду. Кеш у мобільних додатках часто будується саме на цих технологіях.
Room використовує анотації @Entity для таблиць та @Dao для запитів. DAO інкапсулює всі SQL-операції з перевіркою на етапі компіляції — помилки синтаксису SQL виявляються до запуску. Type Converter перетворює складні типи (Date, List) у примітиви SQLite. Сучасна робота з даними в Android-додатках будується навколо Room + Flow, забезпечуючи реактивне оновлення UI при змінах у кеші або локальній БД.
SwiftData використовує макрос @Model для визначення сутностей та @Query для спостереження за даними. Фреймворк автоматично відстежує залежності та оновлює інтерфейс при змінах. Для міграції схеми застосовується VersionedSchema з описом усіх версій. Синхронізація даних між SwiftData та сервером реалізується через кастомний Sync Manager, підписаний на оновлення через @Query.
@Model
final class UserModel {
var id: String
var name: String
var email: String
var updatedAt: Date
init(id: String, name: String, email: String) {
self.id = id
self.name = name
self.email = email
self.updatedAt = Date()
}
}
| Критерій | Room | SwiftData |
|---|---|---|
| Платформа | Android | Apple (iOS, macOS, visionOS) |
| Основа | SQLite | SQLite (Core Data stack) |
| Синтаксис | Анотації Kotlin | Swift Macro |
| Міграції | Клас Migration | VersionedSchema |
| Реактивність | Flow / LiveData | Property wrapper @Query |
| Кроссплатформа | Тільки Android | Тільки Apple |
Часті запитання
LRU Cache — алгоритм кешування, який при заповненні ліміту видаляє найменш нещодавно використаний елемент. Застосовується для зображень та API-даних у мобільних додатках.
Offline Queue зберігає операції користувача в локальній БД при відсутності мережі. Sync Manager виконує їх при відновленні з'єднання, гарантуючи доставку змін на сервер.
Conflict Resolution — стратегія вирішення конфліктів при синхронізації даних. Основні підходи: Last-Write-Wins, Version Vector та CRDT для розподілених систем.
Для Android вибирайте Room — зріла бібліотека з перевіркою SQL на етапі компіляції. Для iOS — SwiftData з декларативним синтаксисом. Для кроссплатформних проєктів підійде SQLDelight або Realm.
Оптимальна синхронізація даних — при кожній зміні для критичних операцій і фонова кожні 15–30 хвилин для решти. Для миттєвої доставки використовуйте push-повідомлення.
Підсумки
Ми розробимо мобільний застосунок під ключ
IT Sectr створює застосунки для iOS та Android для стартапів і бізнесу з 2017 року. Ми проконсультуємо вас і запропонуємо найкраще рішення.