MVVM: co to je, vzor Model-View-ViewModel v mobilním vývoji

Autor: IT Sectr Publikováno: 2026-02-16 Doba čtení: 9 min

MVVM (Model-View-ViewModel) — architektonický vzor, ve kterém ViewModel nahrazuje Presenter a používá reaktivní mechanismy pro komunikaci s View: ObservableObject ve SwiftUI, LiveData/StateFlow v Android. ViewModel nemá odkaz na View — data jsou přenášena prostřednictvím předplatného, což eliminuje potřebu rozhraní ViewContract a činí testování ještě jednodušším. Apple doporučuje MVVM se SwiftUI od roku 2019, Google — MVVM s Jetpack jako oficiální architekturu Android. Více v Android Architecture Guide.

Hlavní body

  • MVVM — Model (data), View (rozhraní), ViewModel (stav a logika bez odkazu na View)
  • Reactive binding — LiveData, StateFlow, ObservableObject automaticky aktualizují UI při změně dat
  • ViewModel — přežije otočení obrazovky a nezávisí na Android SDK/UIKit, testuje se unit testy
  • Android Jetpack — ViewModel, LiveData, DataBinding — oficiální stack od Google pro MVVM
  • SwiftUI + Combine — nativní implementace MVVM v iOS s @Published a @ObservedObject

Co je MVVM: podstata vzoru Model-View-ViewModel

MVVM (Model-View-ViewModel) — architektonický vzor popsaný Johnem Gossmanem v roce 2005 pro Windows Presentation Foundation (WPF) od Microsoft. ViewModel — centrální komponenta, která obsahuje stav obrazovky a obchodní logiku, ale nemá odkaz na View. Data jsou přenášena prostřednictvím mechanismů reaktivního vázání: View se přihlásí k odběru změn ViewModel a automaticky se překreslí, když se data změní.

Klíčový rozdíl MVVM od MVP — absence ViewContract. V MVP Presenter volá metody view.showUser(data), to znamená, že Presenter aktivně “tlačí” data do View. V MVVM View sama “táhne” data z ViewModel prostřednictvím předplatného: ViewModel neví, zda má odběratele. To eliminuje problém odpojeného View — pokud je Activity zničeno při otočení, ViewModel pokračuje v práci a nové Activity se jednoduše přihlásí k aktuálním datům. V IT Sectr používáme MVVM ve všech nových projektech od roku 2020 — kód se stal předvídatelnějším, testy stabilnějšími.

KomponentaOdpovědnostPlatforma
ModelData, obchodní logika, repozitářeAndroid/iOS
ViewZobrazení, odběr ViewModelActivity/Composable, UIView/SwiftUI View
ViewModelStav obrazovky, logika, navigaceViewModel (Jetpack), ObservableObject

Reaktivní vázání — základ MVVM. V Android je LiveData (součást Jetpack) pozorovatelným úložištěm dat. Activity se přihlásí k odběru prostřednictvím observe(): viewModel.user.observe(this) { user -> binding.name.text = user.name }. Při změně user všichni odběratelé automaticky obdrží novou hodnotu. V iOS SwiftUI používá @Published vlastnosti ve ViewModel — změny automaticky překreslí View. To eliminuje ruční volání showUser/hideLoading, která jsou vyžadována v MVP.

MVVM v Android: ViewModel, LiveData a StateFlow

ViewModel z Jetpack — oficiální komponenta od Google pro implementaci MVVM. ViewModel přežije otočení obrazovky: při změně konfigurace je Activity zničeno a znovu vytvořeno, zatímco ViewModel zůstává v paměti. Nová instance Activity obdrží stejný ViewModel prostřednictvím ViewModelProvider. ViewModel nemá odkazy na Activity, Context nebo View — je čistý a testuje se unit testy bez Robolectric.

kotlin
// ViewModel se StateFlow — moderní implementace MVVM
class UserViewModel(
    private val repository: UserRepository
) : ViewModel() {

    private val _state = MutableStateFlow<UserState>(UserState.Loading)
    val state: StateFlow<UserState> = _state.asStateFlow()

    fun loadUser(userId: Int) {
        viewModelScope.launch {
            _state.value = UserState.Loading
            repository.getUser(userId)
                .onSuccess { user ->
                    _state.value = UserState.Success(user)
                }
                .onFailure { e ->
                    _state.value = UserState.Error(e.message ?: "Unknown")
                }
        }
    }
}

sealed interface UserState {
    data object Loading : UserState
    data class Success(val user: User) : UserState
    data class Error(val message: String) : UserState
}

// View (Activity) se přihlásí k odběru state
class UserActivity : AppCompatActivity() {
    private val viewModel: UserViewModel by viewModels()

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        viewModel.state.onEach { state ->
            when (state) {
                is UserState.Loading -> /* zobrazit načítání */
                is UserState.Success -> /* zobrazit data */
                is UserState.Error -> /* zobrazit chybu */
            }
        }.launchIn(lifecycleScope)
        viewModel.loadUser(42)
    }
}

LiveData vs StateFlow — LiveData (2017) — první reaktivní komponenta Jetpack, optimalizovaná pro životní cyklus Activity: automatické odhlášení při onStop. StateFlow (2021) — implementace Kotlin Flow, nezávislá na životním cyklu, ale vyžadující ruční odhlášení prostřednictvím lifecycleScope. StateFlow podporuje coroutines, concat, map a další operátory Flow, které LiveData nemá. V IT Sectr používáme StateFlow pro všechna nová ViewModel — je kratší, výkonnější a lépe se integruje s korutinami.

DataBinding a ViewBinding — DataBinding propojuje ViewModel s XML pomocí @{viewModel.user.name} přímo v rozvržení, čímž eliminuje kód v Activity. ViewBinding generuje typově bezpečnou třídu pro přístup k View. Google doporučuje ViewBinding pro jednoduché projekty a DataBinding pro projekty se složitým vázáním dat. V Jetpack Compose není DataBinding potřeba — funkce @Composable se automaticky překreslí při změně State.

MVVM v iOS: ObservableObject a SwiftUI

MVVM v iOS je implementováno prostřednictvím ObservableObject z Combine. ViewModel — třída dědící ObservableObject s @Published poli. SwiftUI View se přihlásí k odběru ViewModel prostřednictvím @ObservedObject nebo @StateObject. Při změně @Published vlastnosti SwiftUI automaticky překreslí View, které na této vlastnosti závisí. Apple představil SwiftUI v roce 2019 na WWDC spolu s Combine — od té chvíle se MVVM stal oficiálně doporučeným vzorem pro iOS.

swift
import SwiftUI
import Combine

// ViewModel — ObservableObject s @Published poli
final class UserViewModel: ObservableObject {
    @Published private(set) var state: UserState = .loading
    private let service: UserService

    init(service: UserService) {
        self.service = service
    }

    func loadUser(id: Int) {
        state = .loading
        service.fetchUser(id: id) { [weak self] result in
            guard let self else { return }
            switch result {
            case .success(let user):
                self.state = .success(user)
            case .failure(let error):
                self.state = .error(error.localizedDescription)
            }
        }
    }
}

enum UserState {
    case loading
    case success(User)
    case error(String)
}

// SwiftUI View — přihlásí se k odběru ViewModel
struct UserView: View {
    @StateObject private var viewModel: UserViewModel

    var body: some View {
        switch viewModel.state {
        case .loading:
            ProgressView()
        case .success(let user):
            VStack {
                Text(user.name).font(.title)
                Text(user.email).font(.body)
            }
        case .error(let message):
            Text(message).foregroundColor(.red)
        }
    }
}

@StateObject vs @ObservedObject — @StateObject vytváří ViewModel a spravuje jeho životní cyklus (jednou za dobu života View). @ObservedObject — ViewModel je vytvořen externě a předán do View. WWDC 2022 doporučuje @StateObject pro vytváření a @ObservedObject pro předávání ViewModel mezi Views. V iOS 17 (2023) se objevil @Observable — makro, které automatizuje odběr a eliminuje @Published anotace. @Observable — evoluce Combine, přibližující iOS vývoj reaktivitě Kotlin Flow.

UIKit + MVVM — pro projekty na UIKit (bez SwiftUI) je MVVM implementováno prostřednictvím Combine a @Published s odběrem v UIViewController pomocí sink(). ViewModel je stejný, View — UIViewController s odběry na @Published. Combine je dostupný od iOS 13 (2019) a je integrován v systému — nevyžaduje další závislosti. Podle Apple Developer Survey (2025) 45% iOS projektů používá Combine i při UIKit, 35% používá SwiftUI + Combine, 20% — RxSwift (legacy).

Srovnání MVVM s MVP: výhody a nevýhody

MVVM vítězí nad MVP ve třech klíčových aspektech: absence rozhraní ViewContract, automatická správa odběrů a přežití otočení obrazovky. V MVP každá obrazovka vyžaduje rozhraní ViewContract + třídu Presenter + odběr/odhlášení v onStart/onStop. V MVVM se vytváří pouze ViewModel — odběr v Activity se provádí prostřednictvím observe() bez ručního detach().

KritériumMVPMVVM
Rozhraní ViewContract1 na obrazovkuNejsou potřeba
Správa odběrůRuční attach/detachAutomatická (lifecycle-aware)
Otočení obrazovkyRetain-fragmentViewModel přežije
TestováníMock ViewContractČistá třída bez závislostí
ReaktivitaCallbacky v PresenterLiveData/StateFlow/Combine

Nevýhody MVVM — složitost ladění reaktivních řetězců a riziko úniku paměti při nesprávném odběru. LiveData řeší problém bezpečnosti životního cyklu, StateFlow vyžaduje lifecycleScope, Combine — sink s AnyCancellable. V MVP jsou všechna volání explicitní (view.showUser), v MVVM data přicházejí reaktivním proudem — sledování vyžaduje debug breakpointy v subscribe uzávěře. Ve velkých ViewModel s mnoha StateFlow lze zmeškat aktualizaci UI, pokud View není přihlášeno k odběru konkrétního Flow.

Kdy je MVP stále lepší — v projektech s minimální verzí Android pod API 21 (Android 5), kde Jetpack ViewModel není dostupný bez AndroidX, a v projektech na čistém UIKit bez Combine (iOS 12 a níže). Pro legacy projekty, kde je celá kódová báze již na MVP, není úplný přechod na MVVM vždy opodstatněný — levnější je udržovat MVP s postupným přesunem logiky do služeb, než přepisovat 100 obrazovek za 3 měsíce.

Testování ViewModel na Android a iOS

ViewModel je testován unit testy bez platformních závislostí — to je hlavní argument ve prospěch MVVM. Na Android ViewModel neobsahuje Activity, Context ani View — všechny závislosti (Repository, UseCase) jsou předávány konstruktorem a nahrazovány mock objekty. Na iOS je ObservableObject testován prostřednictvím XCTest bez spouštění aplikace, což poskytuje stabilitu a rychlost provádění testů.

kotlin
// Unit test Android ViewModel s MockK
class UserViewModelTest {

    private val repository = mockk<UserRepository>()
    private val viewModel = UserViewModel(repository)

    @Test
    fun loadUser_success_updatesState() = runTest {
        val user = User(1, "John", "john@test.com")
        coEvery { repository.getUser(1) } returns Result.success(user)

        viewModel.loadUser(1)

        assertEquals(UserState.Success(user), viewModel.state.value)
    }

    @Test
    fun loadUser_error_updatesErrorState() = runTest {
        val error = RuntimeException("Network error")
        coEvery { repository.getUser(1) } returns Result.failure(error)

        viewModel.loadUser(1)

        val state = viewModel.state.value
        assertTrue(state is UserState.Error)
        assertEquals("Network error", (state as UserState.Error).message)
    }
}

iOS ViewModel je testován obdobně: injektujeme mock UserService, zavoláme loadUser, zkontrolujeme stav prostřednictvím XCTestExpectation. Combine Publisher je testován prostřednictvím XCTestCase s wait(for: expectations, timeout: 1.0). Struktura UserState — enum s associated values — umožňuje zkontrolovat přesný stav obrazovky po operaci.

Pokrytí kódu v projektech IT Sectr na MVVM je 75-90% pro ViewModel a Repository. ViewModel je pokryt unit testy, Repository — integračními testy s testovací databází. View ve SwiftUI a Jetpack Compose je testováno UI testy (XCUITest, Compose Test) pro kritické scénáře. Zbývající UI je kontrolováno screenshot testy (Snapshot Testing) — to je rychlejší než UI testy a poskytuje 95% jistotu ve správnost zobrazení.

Často kladené otázky

Jaký je hlavní rozdíl mezi MVVM a MVP?

V MVVM ViewModel nemá odkaz na View — data jsou přenášena prostřednictvím reaktivních mechanismů (LiveData, StateFlow, @Published). V MVP Presenter přímo volá metody View prostřednictvím rozhraní ViewContract. MVVM eliminuje ViewContract a ruční attach/detach, ale vyžaduje pochopení reaktivních toků. ViewModel přežije otočení obrazovky na Android, Presenter vyžaduje retain-fragment.

Jaké knihovny jsou potřeba pro MVVM na Android?

Minimální sada: lifecycle-viewmodel-ktx (ViewModel), lifecycle-livedata-ktx nebo kotlinx-coroutines-core (StateFlow). Pro injekci — Hilt nebo Koin. Pro asynchronní operace — Kotlin Coroutines. Pro složité vázání dat — DataBinding. V Jetpack Compose (doporučeném Google od 2022) stačí compose-runtime a lifecycle-viewmodel-compose.

Proč Apple doporučuje MVVM pro iOS?

SwiftUI (2019) byl navržen pro reaktivní architekturu: @State a @Published automaticky překreslí View při změně dat. MVVM — přirozená shoda se SwiftUI: View — @ViewBuilder, ViewModel — ObservableObject. Apple nenutí MVVM jako jediný vzor, ale všechny vzdělávací materiály od roku 2019 používají ViewModel + SwiftUI. Pro UIKit Apple doporučuje MVC nebo Coordinator.

Jak se vyhnout únikům paměti ve ViewModel?

Android: viewModelScope automaticky ruší korutiny při čištění ViewModel. iOS: AnyCancellable z Combine se automaticky odhlásí při uvolnění objektu, který jej uchovává. SwiftUI @StateObject spravuje životní cyklus automaticky. Hlavní pravidla: neukládejte odkazy na View/Context ve ViewModel, rušte dlouho běžící operace při čištění, používejte weak self v uzávěrách.

Co vybrat: LiveData nebo StateFlow pro Android?

StateFlow — moderní volba. LiveData je jednodušší a bezpečná pro životní cyklus, ale StateFlow je výkonnější: pracuje s korutinami, podporuje flatMap, combine, filter, nevyžaduje anotaci @Nullable. Jediný scénář, kde je LiveData preferovanější — práce s Java kódem, kde StateFlow (Kotlin Flow-API) není dostupný. Google doporučuje StateFlow pro nové projekty v Kotlin.

Shrnutí

  • MVVM (Model-View-ViewModel) — reaktivní vzor, kde ViewModel nemá odkaz na View
  • ViewModel — přežije otočení obrazovky, testuje se unit testy, nezávislý na UI
  • Android — ViewModel + StateFlow + Kotlin Coroutines — moderní stack od Google
  • iOS — ObservableObject + @Published + SwiftUI — nativní implementace MVVM
  • MVVM vs MVP — MVVM eliminuje ViewContract a ruční attach/detach
  • Testování — ViewModel pokrytý unit testy bez platformních závislostí
  • Doporučení — MVVM pro nové projekty; MVP pro podporu legacy

Vyvineme mobilní aplikaci na klíč

IT Sectr vytváří aplikace pro iOS a Android pro startupy a podniky od roku 2017. Poradíme vám a navrhneme nejlepší řešení.

Prodiskutovat projekt

Přečtěte si také