Repository Pattern: шта је то, образац апстракције података у iOS и Android

Аутор: IT Sectr Објављено: 2026-02-17 Време читања: 7 мин

Repository Pattern — образац који додаје слој апстракције између пословне логике и извора података. Уместо директних позива API, базе или кеша Repository пружа јединствени интерфејс за добијање и чување података. Ово поједностављује тестирање и пребацивање између извора. Више у документацији Android Data Layer.

Главно

  • Repository Pattern — прослој између пословне логике и извора података (API, БП, кеш)
  • DataSource — одвојене класе за сваки извор: RemoteDataSource, LocalDataSource
  • Single source of truth — Repository постаје јединствени извор података за UI слој
  • Тестирање — Repository се лако замењује mock-објектом кроз DI за unit тестове
  • Компатибилност — ради са MVVM, Clean Architecture и другим архитектуралним обрасцима

Шта је Repository Pattern у мобилном развоју?

Repository Pattern — структурни образац који изолује пословну логику од директног приступа изворима података. Уместо да Activity, UIViewController или ViewModel директно позивају Retrofit, URLSession, Room или CoreData, они се обраћају Repository-ју. Repository одлучује одакле да узме податке: из мреже, базе или кеша, и враћа резултат у јединственом формату. Ово је имплементација принципа јединствене одговорности — UI не зна како и одакле су подаци добијени.

Компоненте Repository-ја укључују интерфејс (protocol), имплементацију и један или више DataSource-а. DataSource — класа која ради са једним извором: RemoteDataSource позива API преко HTTP клијента, LocalDataSource чита и пише у базу података. Repository прима DataSource-е кроз конструктор (Dependency Injection) и бира ком извору да се обрати. На пример, при захтеву за листу корисника Repository прво проверава кеш, затим базу, па мрежу.

Предности Repository Pattern-а: изолација промена извора података (промена API, миграција БП) не утиче на UI слој; unit тестирање кроз замену Repository-ја или DataSource-а; кеширање транспарентно за UI; пребацивање између online и offline режима без промене логике екрана. Заједница Android препоручује Repository као обавезан слој у Clean Architecture.

Repository Pattern у iOS на Swift: имплементација и пример

iOS имплементација Repository-ја се заснива на Swift протоколима. Протокол Repository декларише методе за добијање и чување података. Стварна имплементација се убризгава кроз иницијализатор — ово омогућава замену имплементације у тестовима и SwiftUI preview-ју. DataSource-и се такође декларишу протоколима: Protocol RemoteDataSource, Protocol LocalDataSource. ViewModel или Interactor не зна конкретну имплементацију — зна само протокол Repository-ја.

swift
protocol UserRepository {
    func getUsers() async throws -> [User]
}

protocol UserRemoteDataSource {
    func fetchUsers() async throws -> [User]
}

protocol UserLocalDataSource {
    func getCachedUsers() throws -> [User]
    func saveUsers(_: [User]) throws
}

final class UserRepositoryImpl: UserRepository {
    private let remote: UserRemoteDataSource
    private let local: UserLocalDataSource

    init(remote: UserRemoteDataSource, local: UserLocalDataSource) {
        self.remote = remote
        self.local = local
    }

    func getUsers() async throws -> [User] {
        if let cached = try? local.getCachedUsers() {
            return cached
        }
        let users = try await remote.fetchUsers()
        try local.saveUsers(users)
        return users
    }
}

Dependency Injection у iOS за Repository се обично подешава кроз фабрику или DI контејнер (Swinject, Factory). У тестовима, протокол UserRepository се замењује mock имплементацијом која враћа унапред дефинисане податке. Async-await чини код синхроним и читљивим без затварања и делегата. За Combine реактивност, методе Repository-ја враћају AnyPublisher уместо async throws.

Repository Pattern у Android на Kotlin: пример са Flow

Android имплементација Repository-ја широко користи Kotlin Coroutines и Flow за асинхрони рад. Google препоручује Repository у званичном водичу за Android архитектуру (Android Architecture Components). Repository прима RemoteDataSource (Retrofit) и LocalDataSource (Room) кроз конструктор, а ViewModel се претплаћује на Flow из Repository-ја. Repository управља стратегијом података: прво кеш, затим мрежа или увек мрежа са уписом у кеш.

kotlin
interface UserRepository {
    fun getUsers(): Flow<Result<List<User>>>
}

interface UserRemoteDataSource {
    suspend fun fetchUsers(): List<User>
}

interface UserLocalDataSource {
    fun getCachedUsers(): Flow<List<User>>
    suspend fun saveUsers(users: List<User>)
}

class UserRepositoryImpl(
    private val remote: UserRemoteDataSource,
    private val local: UserLocalDataSource
) : UserRepository {

    override fun getUsers(): Flow<Result<List<User>>> = flow {
        emit(Result.Loading)
        local.getCachedUsers().collect { cached ->
            if (cached.isNotEmpty()) {
                emit(Result.Success(cached))
            }
        }
        try {
            val users = remote.fetchUsers()
            local.saveUsers(users)
            emit(Result.Success(users))
        } catch (e: Exception) {
            emit(Result.Error(e))
        }
    }
}

Result омот у примеру изнад је стандардан за Android: sealed class Result обавештава ViewModel о статусу учитавања (Loading, Success, Error). ViewModel се претплаћује кроз collect и ажурира StateFlow или LiveData. Repository са Flow-ом аутоматски обавештава UI о променама у бази — ово је кључна разлика у односу на једнократне упите, где UI не сазнаје о променама без ручног освежавања.

DataSource: Remote, Local и кеширање података

DataSource — класе одговорне за рад са одређеним извором података. RemoteDataSource користи HTTP клијент (URLSession, Retrofit, Ktor) за добијање података из API-ја. LocalDataSource ради са локалним складиштем (CoreData, Realm, Room, UserDefaults, DataStore). Сваки DataSource има уску одговорност: RemoteDataSource зна само формат API захтева, LocalDataSource — шему базе података. Repository их комбинује, имплементирајући стратегију кеширања.

DataSourceПлатформа iOSПлатформа AndroidИзвор
RemoteURLSession + CodableRetrofit + Moshi/GsonREST / GraphQL API
Local (БП)CoreData, SwiftDataRoom, SQLDelightSQLite на уређају
Local (кеш)NSCache, UserDefaultsDataStore, EncryptedSPУ меморији / диск
ПодешавањаUserDefaults, KeychainSharedPreferences, EncryptedSPПодешавања, токени

Стратегије кеширања у Repository-ју: Cache-First (прво кеш, потом позадинско учитавање), Network-Only (само мрежа, за екране плаћања), Network-First-With-Cache-Backup (прво мрежа, при грешци — кеш). Избор стратегије зависи од сценарија: листу земаља може дуго да се кешира, курсеве валута — 15 минута, стање новчаника — само из мреже. Repository имплементира стратегију и мења је без измене ViewModel-а или UI-ја.

Repository Pattern vs Service Layer: разлике и избор

Repository и Service — различити обрасци са преклапајућим функцијама. Repository је одговоран за приступ подацима и њихово кеширање, враћајући моделе података. Service (или Interactor, Use Case) садржи пословну логику: валидацију, трансформацију података, оркестрацију позива више Repository-ја. Service може да комбинује UserRepository, OrderRepository и NotificationRepository за обраду поруџбине. Repository не садржи пословну логику — само CRUD и кеширање.

Када изабрати Repository — навигација подацима са више извора (API + БП + кеш), offline-first архитектура, потреба за кеширањем и транспарентним пребацивањем извора. Repository је обавезан у Clean Architecture и препоручује га Google за Android апликације. У VIPER архитектури на iOS, улогу Repository-ја обавља Interactor слој, који сарађује са Manager-ом или Service-ом за приступ подацима.

Када је довољан Service — једноставне апликације са једним извором података, read-only екрани без уписа, пројекти без offline режима. У таквим случајевима, DataSource се користи директно од стране ViewModel-а или Presenter-а, а Repository постаје сувишан слој. Међутим, додавање Repository-ја у раној фази не захтева велике трошкове и олакшава додавање кеширања и тестова у будућности.

Често постављана питања

По чему се Repository разликује од DataSource-а?

DataSource — класа која ради са једним извором (API, БП, кеш). Repository — класа која управља са више DataSource-а и пружа јединствени интерфејс. Repository одлучује из ког DataSource-а да узме податке и координира кеширање. DataSource не зна за постојање других извора, Repository не зна детаље имплементације сваког извора.

Да ли је Repository потребан у iOS са SwiftUI?

Да, Repository је користан у SwiftUI за одвајање података од View-а. ViewModel се претплаћује на Publisher из Repository-ја, а Repository управља кеширањем и синхронизацијом. У једноставним апликацијама може се користити URLSession директно у ViewModel-у, али за тестирање и скалабилност Repository је пожељнији. Apple не намеће овај образац, али је компатибилан са SwiftData и Network.framework.

Како тестирати Repository са више DataSource-а?

DataSource-и се замењују mock објектима кроз Dependency Injection. Тест креира mock RemoteDataSource (враћа унапред дефинисани JSON) и mock LocalDataSource (проверава да ли су подаци сачувани). Repository се тестира изоловано: проверава се стратегија кеширања, обрада грешака и правилан редослед позива. За интеграционе тестове користи се TestDispatcher (Kotlin) или MainActor.run (Swift).

Може ли се Repository користити без интерфејса (protocol)?

Може, али се не препоручује. Без протокола није могуће заменити имплементацију у тестовима и preview-ју. У Kotlin-у, интерфејс Repository омогућава замену имплементације кроз DI (Dagger, Hilt, Koin). У Swift-у, протокол Repository је обавезан за тестирање async-await и Combine кода. Изузетак — једноставни пројекти са једним извором података, где Repository не садржи логику кеширања.

Шта је offline-first у контексту Repository-ја?

Offline-first — стратегија у којој апликација ради без интернета, користећи локалне податке. Repository игра кључну улогу: прво враћа податке из локалног DataSource-а, а затим се синхронизује са сервером у позадини. Корисник види податке тренутно, а Repository их ажурира након учитавања из мреже. Room са Flow-ом обезбеђује реактивно ажурирање UI-ја при промени података у локалној бази.

Резиме

  • Repository Pattern — слој апстракције између UI-ја и извора података
  • DataSource — одвојене класе за API, БП и кеш
  • Протоколи — обавезни за тестирање и замену имплементација
  • Стратегије кеширања — Cache-First, Network-Only, Network-First-With-Cache-Backup
  • iOS — async-await или Combine са протоколима
  • Android — Kotlin Flow + Room + Retrofit, приступ препоручен од Google-а
  • Тестирање — mock DataSource кроз DI, провера стратегија кеширања

Развићемо мобилну апликацију под кључ

IT Sectr креира iOS и Android апликације за стартапе и предузећа од 2017. године. Саветоваћемо вас и предложити најбоље решење.

Разговарајте о пројекту

Прочитајте такође