MVVM: was es ist, das Model-View-ViewModel-Muster in der mobilen Entwicklung

Autor: IT Sectr Veröffentlicht: 2026-02-16 Lesezeit: 9 Min.

MVVM (Model-View-ViewModel) ist ein Architekturmuster, bei dem ViewModel den Presenter ersetzt und reaktive Mechanismen zur Kommunikation mit der View verwendet: ObservableObject in SwiftUI, LiveData/StateFlow in Android. ViewModel hat keine Referenz auf die View — Daten werden über Abonnement übergeben, wodurch ViewContract-Schnittstellen überflüssig werden und das Testen noch einfacher wird. Apple empfiehlt MVVM mit SwiftUI seit 2019, Google empfiehlt MVVM mit Jetpack als offizielle Android-Architektur. Weitere Informationen finden Sie im Android Architecture Guide.

Wichtigste Erkenntnisse

  • MVVM — Model (Daten), View (Schnittstelle), ViewModel (Zustand und Logik ohne Referenz auf View)
  • Reactive Binding — LiveData, StateFlow, ObservableObject aktualisieren die UI automatisch bei Datenänderungen
  • ViewModel — überlebt Bildschirmdrehung und ist unabhängig von Android SDK/UIKit, mit Unit-Tests testbar
  • Android Jetpack — ViewModel, LiveData, DataBinding — offizieller Stack von Google für MVVM
  • SwiftUI + Combine — native MVVM-Implementierung in iOS mit @Published und @ObservedObject

Was ist MVVM: Das Wesen des Model-View-ViewModel-Musters

MVVM (Model-View-ViewModel) ist ein Architekturmuster, das von John Gossman im Jahr 2005 für Windows Presentation Foundation (WPF) von Microsoft beschrieben wurde. ViewModel ist die zentrale Komponente, die den Bildschirmzustand und die Geschäftslogik enthält, aber keine Referenz auf die View hat. Daten werden über reaktive Bindungsmechanismen übergeben: Die View abonniert Änderungen des ViewModels und wird bei Datenänderungen automatisch neu gerendert.

Hauptunterschied zwischen MVVM und MVP — Fehlen von ViewContract. In MVP ruft der Presenter Methoden wie view.showUser(data) auf, d.h. der Presenter "schiebt" Daten aktiv in die View. In MVVM "zieht" die View selbst Daten aus dem ViewModel über ein Abonnement: Das ViewModel weiß nicht, ob es einen Abonnenten hat. Dies beseitigt das Problem der getrennten View — wenn die Activity bei Drehung zerstört wird, arbeitet das ViewModel weiter, und die neue Activity abonniert einfach die aktuellen Daten. Bei IT Sectr verwenden wir seit 2020 MVVM in allen neuen Projekten — der Code ist vorhersagbarer geworden, die Tests stabiler.

KomponenteVerantwortungPlattform
ModelDaten, Geschäftslogik, RepositoriesAndroid/iOS
ViewAnzeige, Abonnement des ViewModelsActivity/Composable, UIView/SwiftUI View
ViewModelBildschirmzustand, Logik, NavigationViewModel (Jetpack), ObservableObject

Reaktive Bindung — die Grundlage von MVVM. In Android ist LiveData (Teil von Jetpack) ein beobachtbarer Datenhalter. Die Activity abonniert über observe(): viewModel.user.observe(this) { user -> binding.name.text = user.name }. Wenn sich user ändert, erhalten alle Abonnenten automatisch den neuen Wert. In iOS verwendet SwiftUI @Published-Eigenschaften im ViewModel — Änderungen rendern die View automatisch neu. Dies eliminiert manuelle showUser/hideLoading-Aufrufe, die in MVP erforderlich sind.

MVVM in Android: ViewModel, LiveData und StateFlow

ViewModel von Jetpack — die offizielle Komponente von Google zur Implementierung von MVVM. ViewModel überlebt die Bildschirmdrehung: Bei Konfigurationsänderungen wird die Activity zerstört und neu erstellt, während das ViewModel im Speicher bleibt. Die neue Activity-Instanz erhält dasselbe ViewModel über ViewModelProvider. Das ViewModel hat keine Referenzen auf Activity, Context oder View — es ist sauber und mit Unit-Tests ohne Robolectric testbar.

kotlin
// ViewModel mit StateFlow — moderne MVVM-Implementierung
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) abonniert 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 -> /* Ladevorgang anzeigen */
                is UserState.Success -> /* Daten anzeigen */
                is UserState.Error -> /* Fehler anzeigen */
            }
        }.launchIn(lifecycleScope)
        viewModel.loadUser(42)
    }
}

LiveData vs StateFlow — LiveData (2017) — die erste reaktive Komponente von Jetpack, optimiert für den Activity-Lebenszyklus: automatische Abbestellung bei onStop. StateFlow (2021) — Kotlin Flow-Implementierung, nicht an den Lebenszyklus gebunden, erfordert jedoch manuelle Abbestellung über lifecycleScope. StateFlow unterstützt Coroutinen, concat, map und andere Flow-Operatoren, die LiveData nicht hat. Bei IT Sectr verwenden wir StateFlow für alle neuen ViewModels — es ist kürzer, leistungsfähiger und lässt sich besser mit Coroutinen integrieren.

DataBinding und ViewBinding — DataBinding bindet das ViewModel an XML über @{viewModel.user.name} direkt im Layout und eliminiert Code in der Activity. ViewBinding generiert eine typsichere Klasse für den Zugriff auf Views. Google empfiehlt ViewBinding für einfache Projekte und DataBinding für Projekte mit komplexer Datenbindung. In Jetpack Compose ist DataBinding nicht erforderlich — @Composable-Funktionen werden bei State-Änderungen automatisch neu gerendert.

MVVM in iOS: ObservableObject und SwiftUI

MVVM in iOS wird über ObservableObject aus Combine implementiert. ViewModel ist eine Klasse, die ObservableObject erbt, mit @Published-Eigenschaften. Die SwiftUI View abonniert das ViewModel über @ObservedObject oder @StateObject. Wenn sich eine @Published-Eigenschaft ändert, rendert SwiftUI automatisch die View neu, die von dieser Eigenschaft abhängt. Apple stellte SwiftUI 2019 auf der WWDC zusammen mit Combine vor — seitdem ist MVVM das offiziell empfohlene Muster für iOS.

swift
import SwiftUI
import Combine

// ViewModel — ObservableObject mit @Published-Feldern
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 — abonniert 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 erstellt das ViewModel und verwaltet seinen Lebenszyklus (einmal pro View-Lebensdauer). @ObservedObject — das ViewModel wird extern erstellt und an die View übergeben. Die WWDC 2022 empfiehlt @StateObject zur Erstellung und @ObservedObject zur Übergabe des ViewModels zwischen Views. In iOS 17 (2023) erschien das @Observable-Makro — es automatisiert das Abonnement und macht @Published-Annotationen überflüssig. @Observable ist die Weiterentwicklung von Combine und bringt die iOS-Entwicklung näher an die Kotlin-Flow-Reaktivität.

UIKit + MVVM — für UIKit-Projekte (ohne SwiftUI) wird MVVM über Combine und @Published mit Abonnement im UIViewController über sink() implementiert. Das ViewModel bleibt gleich, die View ist ein UIViewController mit Abonnements auf @Published. Combine ist seit iOS 13 (2019) verfügbar und systemintegriert — keine zusätzlichen Abhängigkeiten erforderlich. Laut Apple Developer Survey (2025) verwenden 45% der iOS-Projekte Combine auch mit UIKit, 35% verwenden SwiftUI + Combine, 20% verwenden RxSwift (Legacy).

Vergleich von MVVM mit MVP: Vorteile und Nachteile

MVVM gewinnt gegenüber MVP in drei Schlüsselaspekten: Fehlen von ViewContract-Schnittstellen, automatische Abonnementverwaltung und Überleben der Bildschirmdrehung. In MVP erfordert jeder Bildschirm eine ViewContract-Schnittstelle + Presenter-Klasse + Abonnement/Kündigung in onStart/onStop. In MVVM wird nur das ViewModel erstellt — das Abonnement in der Activity erfolgt über observe() ohne manuelles detach().

KriteriumMVPMVVM
ViewContract-Schnittstellen1 pro BildschirmNicht erforderlich
AbonnementverwaltungManuelles attach/detachAutomatisch (lifecycle-aware)
BildschirmdrehungRetain-FragmentViewModel überlebt Drehung
TestenMock ViewContractSaubere Klasse ohne Abhängigkeiten
ReaktivitätCallbacks im PresenterLiveData/StateFlow/Combine

Nachteile von MVVM — Komplexität beim Debuggen reaktiver Ketten und Risiko von Speicherlecks bei falschem Abonnement. LiveData löst die Lebenszyklussicherheit, StateFlow erfordert lifecycleScope, Combine erfordert sink mit AnyCancellable. In MVP sind alle Aufrufe explizit (view.showUser), in MVVM kommen Daten über einen reaktiven Stream — die Rückverfolgung erfordert Debug-Breakpoints in subscribe-Closures. In großen ViewModels mit mehreren StateFlows kann ein UI-Update verpasst werden, wenn die View ein bestimmtes Flow nicht abonniert hat.

Wann MVP immer noch besser ist — in Projekten mit einer Mindest-Android-Version unter API 21 (Android 5), wo Jetpack ViewModel ohne AndroidX nicht verfügbar ist, und in Projekten mit reinem UIKit ohne Combine (iOS 12 und niedriger). Für Legacy-Projekte, deren gesamte Codebasis bereits auf MVP basiert, ist ein vollständiger Übergang zu MVVM nicht immer gerechtfertigt — es ist günstiger, MVP mit schrittweiser Auslagerung der Logik in Dienste zu pflegen, als 100 Bildschirme in 3 Monaten neu zu schreiben.

ViewModel-Testing auf Android und iOS

ViewModel wird mit Unit-Tests getestet ohne Plattformabhängigkeiten — das ist das Hauptargument für MVVM. Auf Android enthält das ViewModel keine Activity, keinen Context und keine View — alle Abhängigkeiten (Repository, UseCase) werden über den Konstruktor übergeben und durch Mock-Objekte ersetzt. Auf iOS wird ObservableObject über XCTest ohne App-Start getestet, was Stabilität und Testgeschwindigkeit bietet.

kotlin
// Android ViewModel-Unit-Test mit 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 wird ähnlich getestet: Mock UserService injizieren, loadUser aufrufen, Zustand über XCTestExpectation prüfen. Combine Publisher wird über XCTestCase mit wait(for: expectations, timeout: 1.0) getestet. Die UserState-Struktur — ein Enum mit assoziierten Werten — ermöglicht die Überprüfung des genauen Bildschirmzustands nach einer Operation.

Codeabdeckung in IT Sectr-Projekten mit MVVM beträgt 75–90% für ViewModel und Repository. ViewModel wird durch Unit-Tests abgedeckt, Repository durch Integrationstests mit einer Testdatenbank. View in SwiftUI und Jetpack Compose wird für kritische Szenarien mit UI-Tests (XCUITest, Compose Test) getestet. Die restliche UI wird mit Screenshot-Tests (Snapshot Testing) überprüft — das ist schneller als UI-Tests und bietet 95% Sicherheit in der korrekten Darstellung.

Häufig gestellte Fragen

Was ist der Hauptunterschied zwischen MVVM und MVP?

In MVVM hat das ViewModel keine Referenz auf die View — Daten werden über reaktive Mechanismen (LiveData, StateFlow, @Published) übergeben. In MVP ruft der Presenter direkt View-Methoden über die ViewContract-Schnittstelle auf. MVVM eliminiert ViewContract und manuelles attach/detach, erfordert jedoch Verständnis reaktiver Streams. Das ViewModel überlebt die Bildschirmdrehung auf Android, der Presenter benötigt ein retain-Fragment.

Welche Bibliotheken werden für MVVM auf Android benötigt?

Mindestsatz: lifecycle-viewmodel-ktx (ViewModel), lifecycle-livedata-ktx oder kotlinx-coroutines-core (StateFlow). Für Injektion — Hilt oder Koin. Für asynchrone Operationen — Kotlin Coroutines. Für komplexe Datenbindung — DataBinding. In Jetpack Compose (von Google seit 2022 empfohlen) sind compose-runtime und lifecycle-viewmodel-compose ausreichend.

Warum empfiehlt Apple MVVM für iOS?

SwiftUI (2019) ist für reaktive Architektur konzipiert: @State und @Published rendern die View bei Datenänderungen automatisch neu. MVVM passt natürlich zu SwiftUI: View — @ViewBuilder, ViewModel — ObservableObject. Apple erzwingt MVVM nicht als einziges Muster, aber alle Schulungsmaterialien seit 2019 verwenden ViewModel + SwiftUI. Für UIKit empfiehlt Apple MVC oder Coordinator.

Wie vermeidet man Speicherlecks im ViewModel?

Android: viewModelScope bricht Coroutinen beim Löschen des ViewModels automatisch ab. iOS: AnyCancellable aus Combine kündigt das Abonnement automatisch beim Freigeben des haltenden Objekts. SwiftUI @StateObject verwaltet den Lebenszyklus automatisch. Hauptregeln: Keine Referenzen auf View/Context im ViewModel speichern, langlaufende Operationen beim Bereinigen abbrechen, weak self in Closures verwenden.

Was wählen: LiveData oder StateFlow für Android?

StateFlow ist die moderne Wahl. LiveData ist einfacher und lebenszyklussicher, aber StateFlow ist leistungsfähiger: arbeitet mit Coroutinen, unterstützt flatMap, combine, filter, benötigt keine @Nullable-Annotation. Das einzige Szenario, in dem LiveData vorzuziehen ist — die Arbeit mit Java-Code, wo StateFlow (Kotlin Flow API) nicht verfügbar ist. Google empfiehlt StateFlow für neue Kotlin-Projekte.

Zusammenfassung

  • MVVM (Model-View-ViewModel) — reaktives Muster, bei dem ViewModel keine Referenz auf View hat
  • ViewModel — überlebt Bildschirmdrehung, mit Unit-Tests testbar, unabhängig von UI
  • Android — ViewModel + StateFlow + Kotlin Coroutines — moderner Stack von Google
  • iOS — ObservableObject + @Published + SwiftUI — native MVVM-Implementierung
  • MVVM vs MVP — MVVM eliminiert ViewContract und manuelles attach/detach
  • Testen — ViewModel wird durch Unit-Tests ohne Plattformabhängigkeiten abgedeckt
  • Empfehlung — MVVM für neue Projekte; MVP für Legacy-Support

Wir entwickeln eine mobile Applikation schlüsselfertig

IT Sectr entwickelt seit 2017 iOS- und Android-Apps für Startups und Unternehmen. Wir beraten Sie und schlagen die beste Lösung vor.

Projekt besprechen

Lesen Sie auch