در توسعه موبایل، کار با داده، کش کردن و همگامسازی سه جنبه کلیدی هستند که عملکرد و قابلیت اطمینان برنامه را تعیین میکنند. به گفته Google Android Architecture Guide، معماری صحیح مدیریت داده به طور مستقیم بر سرعت پاسخ و تجربه کاربر تأثیر میگذارد. الگوی Repository یک نقطه دسترسی واحد به تمام منابع داده فراهم میکند.
نکات کلیدی
الگوی Repository یک رویکرد معماری است که در آن یک کلاس مخزن واحد تمام عملیات داده را مدیریت میکند و APIهای REST راه دور و ذخیرهسازی محلی 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 داده را در RAM ذخیره میکند — دسترسی بسیار سریع است، اما ظرفیت با اندازه heap برنامه محدود میشود. 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. باطلسازی مبتنی بر رویداد هنگام دریافت اعلان push درباره تغییرات، کش را پاک میکند. در برنامههای موبایل، انتخاب استراتژی کش به نوع داده بستگی دارد: تصاویر برای مدت طولانی کش میشوند، در حالی که خوراک خبری نیاز به باطلسازی مکرر دارد. Coil در Android و Kingfisher در iOS قبلاً LRU Cache را برای کار با تصاویر یکپارچه کردهاند.
Offline Queue یک ساختار داده است که عملیات کاربر (ایجاد، بهروزرسانی، حذف) را در پایگاه داده محلی ذخیره میکند هنگامی که دستگاه آفلاین است. وقتی اتصال بازیابی شد، Sync Manager این عملیات را به ترتیب روی سرور اعمال میکند. این نوع همگامسازی داده تضمین میکند که هیچ تغییری در هنگام قطع موقت شبکه از دست نرود. در توسعه موبایل، Offline Queue یک جزء حیاتی برای برنامههایی با اتصالات ناپایدار است.
صف بر روی یک جدول در Room یا SwiftData ساخته میشود با فیلدهای: نوع عملیات، بدنه درخواست JSON، timestamp و وضعیت. 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
}
}
}
}
عقبنشینی نمایی بین تلاشهای مجدد (1s، 2s، 4s، 8s) از سرور در برابر بار ناگهانی محافظت میکند و از تلاشهای بینهایت جلوگیری میکند. محدودیت ۵ تلاش از سرریز صف جلوگیری میکند. همگامسازی داده در برنامههای موبایل با پشتیبانی از idempotency در سمت سرور اجازه تلاش مجدد ایمن را میدهد و از تکرار جلوگیری میکند. این به ویژه برای تراکنشهای مالی و سفارشها مهم است.
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 برای توصیف تغییرات استفاده میکند. همگامسازی داده مناسب بین نسخههای برنامه نیاز دارد که مهاجرتها به صورت idempotent آزمایش شوند.
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) |
| نحو | حاشیهنویسی 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 مناسب خواهند بود.
همگامسازی داده بهینه برای عملیات بحرانی در هر تغییر و برای بقیه هر ۱۵-۳۰ دقیقه در پسزمینه است. برای تحویل فوری از اعلانهای push استفاده کنید.
خلاصه
ما یک اپلیکیشن موبایل به صورت کلید در دست توسعه خواهیم داد
IT Sectr از سال 2017 برنامههای iOS و Android را برای استارتاپها و کسبوکارها ایجاد میکند. ما به شما مشاوره میدهیم و بهترین راهحل را پیشنهاد خواهیم کرد.