MVVM: vad är det, mönstret Model-View-ViewModel inom mobilutveckling

Författare: IT Sectr Publicerad: 2026-02-16 Lästid: 9 min

MVVM (Model-View-ViewModel) — ett arkitekturmönster där ViewModel ersätter Presenter och använder reaktiva mekanismer för kommunikation med View: ObservableObject i SwiftUI, LiveData/StateFlow i Android. ViewModel har ingen referens till View — data överförs via prenumeration, vilket eliminerar behovet av ViewContract-gränssnitt och gör testning ännu enklare. Apple rekommenderar MVVM med SwiftUI sedan 2019, Google — MVVM med Jetpack som den officiella Android-arkitekturen. Mer i Android Architecture Guide.

Huvudpunkter

  • MVVM — Model (data), View (gränssnitt), ViewModel (tillstånd och logik utan referens till View)
  • Reactive binding — LiveData, StateFlow, ObservableObject uppdaterar automatiskt UI när data ändras
  • ViewModel — överlever skärmrotation och är oberoende av Android SDK/UIKit, testas med enhetstester
  • Android Jetpack — ViewModel, LiveData, DataBinding — officiell stack från Google för MVVM
  • SwiftUI + Combine — inbyggd implementering av MVVM i iOS med @Published och @ObservedObject

Vad är MVVM: kärnan i mönstret Model-View-ViewModel

MVVM (Model-View-ViewModel) — ett arkitekturmönster som beskrevs av John Gossman 2005 för Windows Presentation Foundation (WPF) från Microsoft. ViewModel — den centrala komponenten som innehåller skärmens tillstånd och affärslogik, men har ingen referens till View. Data överförs via reaktiva bindningsmekanismer: View prenumererar på ViewModels ändringar och ritas automatiskt om när data ändras.

Den viktigaste skillnaden mellan MVVM och MVP — frånvaron av ViewContract. I MVP anropar Presenter metoder som view.showUser(data), det vill säga Presenter “skjuter” aktivt data till View. I MVVM “drar” View själv data från ViewModel via prenumeration: ViewModel vet inte om den har en prenumerant. Detta eliminerar problemet med frånkopplad View — om Activity förstörs vid rotation fortsätter ViewModel att arbeta, och den nya Activity prenumererar helt enkelt på aktuell data. På IT Sectr har vi använt MVVM i alla nya projekt sedan 2020 — koden blev mer förutsägbar, testerna mer stabila.

KomponentAnsvarPlattform
ModelData, affärslogik, repositoriesAndroid/iOS
ViewVisning, prenumeration på ViewModelActivity/Composable, UIView/SwiftUI View
ViewModelSkärmtillstånd, logik, navigeringViewModel (Jetpack), ObservableObject

Reaktiv bindning — grunden för MVVM. I Android är LiveData (del av Jetpack) en observerbar datalagring. Activity prenumererar via observe(): viewModel.user.observe(this) { user -> binding.name.text = user.name }. När user ändras får alla prenumeranter automatiskt det nya värdet. I iOS använder SwiftUI @Published-egenskaper i ViewModel — ändringar ritar automatiskt om View. Detta eliminerar manuella anrop av showUser/hideLoading som krävs i MVP.

MVVM i Android: ViewModel, LiveData och StateFlow

ViewModel från Jetpack — den officiella komponenten från Google för att implementera MVVM. ViewModel överlever skärmrotation: vid konfigurationsändring förstörs Activity och återskapas, medan ViewModel finns kvar i minnet. Den nya Activity-instansen får samma ViewModel via ViewModelProvider. ViewModel har inga referenser till Activity, Context eller View — den är ren och testas med enhetstester utan Robolectric.

kotlin
// ViewModel med StateFlow — modern implementering av 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) prenumererar på 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 -> /* visa laddning */
                is UserState.Success -> /* visa data */
                is UserState.Error -> /* visa fel */
            }
        }.launchIn(lifecycleScope)
        viewModel.loadUser(42)
    }
}

LiveData vs StateFlow — LiveData (2017) — den första reaktiva Jetpack-komponenten, optimerad för Activitys livscykel: automatisk avprenumeration vid onStop. StateFlow (2021) — Kotlin Flow-implementering, inte bunden till livscykeln, men kräver manuell avprenumeration via lifecycleScope. StateFlow stöder coroutines, concat, map och andra Flow-operatorer som LiveData saknar. På IT Sectr använder vi StateFlow för alla nya ViewModels — den är kortare, kraftfullare och integreras bättre med korutiner.

DataBinding och ViewBinding — DataBinding binder ViewModel till XML via @{viewModel.user.name} direkt i layouten, vilket eliminerar kod i Activity. ViewBinding genererar en typsäker klass för åtkomst till View. Google rekommenderar ViewBinding för enkla projekt och DataBinding för projekt med komplex databindning. I Jetpack Compose behövs inte DataBinding — @Composable-funktioner ritas automatiskt om när State ändras.

MVVM i iOS: ObservableObject och SwiftUI

MVVM i iOS implementeras via ObservableObject från Combine. ViewModel — en klass som ärver ObservableObject, med @Published-fält. SwiftUI View prenumererar på ViewModel via @ObservedObject eller @StateObject. När en @Published-egenskap ändras ritar SwiftUI automatiskt om View som är beroende av denna egenskap. Apple introducerade SwiftUI 2019 på WWDC tillsammans med Combine — från det ögonblicket blev MVVM det officiellt rekommenderade mönstret för iOS.

swift
import SwiftUI
import Combine

// ViewModel — ObservableObject med @Published-fält
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 — prenumererar på 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 skapar ViewModel och hanterar dess livscykel (en gång under Viewns livstid). @ObservedObject — ViewModel skapas externt och skickas till View. WWDC 2022 rekommenderar @StateObject för skapande och @ObservedObject för att skicka ViewModel mellan Views. I iOS 17 (2023) dök @Observable upp — ett makro som automatiserar prenumeration och eliminerar @Published-annoteringar. @Observable — evolutionen av Combine, som för iOS-utveckling närmare Kotlin Flows reaktivitet.

UIKit + MVVM — för projekt på UIKit (utan SwiftUI) implementeras MVVM via Combine och @Published med prenumeration i UIViewController via sink(). ViewModel är densamma, View — UIViewController med prenumerationer på @Published. Combine är tillgängligt sedan iOS 13 (2019) och är inbyggt i systemet — kräver inga extra beroenden. Enligt Apple Developer Survey (2025) använder 45% av iOS-projekten Combine även med UIKit, 35% använder SwiftUI + Combine, 20% — RxSwift (legacy).

Jämförelse av MVVM med MVP: fördelar och nackdelar

MVVM vinner över MVP i tre viktiga aspekter: frånvaro av ViewContract-gränssnitt, automatisk prenumerationshantering och överlevnad av skärmrotation. I MVP kräver varje skärm ett ViewContract-gränssnitt + Presenter-klass + prenumeration/avprenumeration i onStart/onStop. I MVVM skapas bara ViewModel — prenumeration i Activity sker via observe() utan manuell detach().

KriteriumMVPMVVM
ViewContract-gränssnitt1 per skärmBehövs inte
PrenumerationshanteringManuell attach/detachAutomatisk (lifecycle-aware)
SkärmrotationRetain-fragmentViewModel överlever
TestningMock ViewContractRen klass utan beroenden
ReaktivitetCallbacks i PresenterLiveData/StateFlow/Combine

Nackdelar med MVVM — komplexiteten i att felsöka reaktiva kedjor och risk för minnesläckor vid felaktig prenumeration. LiveData löser problemet med livscykelsäkerhet, StateFlow kräver lifecycleScope, Combine — sink med AnyCancellable. I MVP är alla anrop explicita (view.showUser), i MVVM kommer data via en reaktiv ström — spårning kräver debug-brytpunkter i subscribe-slutningen. I stora ViewModels med många StateFlows kan en UI-uppdatering missas om View inte prenumererar på en specifik Flow.

När MVP fortfarande är bättre — i projekt med lägsta Android-version under API 21 (Android 5), där Jetpack ViewModel inte är tillgänglig utan AndroidX, och i projekt på ren UIKit utan Combine (iOS 12 och lägre). För legacy-projekt där hela kodbasen redan är på MVP är en fullständig övergång till MVVM inte alltid motiverad — det är billigare att underhålla MVP med gradvis utflyttning av logik till tjänster än att skriva om 100 skärmar på 3 månader.

Testning av ViewModel på Android och iOS

ViewModel testas med enhetstester utan plattformsberoenden — detta är det främsta argumentet för MVVM. På Android innehåller ViewModel inte Activity, Context eller View — alla beroenden (Repository, UseCase) skickas via konstruktorn och ersätts med mock-objekt. På iOS testas ObservableObject via XCTest utan att starta applikationen, vilket ger stabilitet och snabbhet i testkörningen.

kotlin
// Enhetstest av Android ViewModel med 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 testas på liknande sätt: vi injicerar en mock UserService, anropar loadUser, kontrollerar tillståndet via XCTestExpectation. Combine Publisher testas via XCTestCase med wait(for: expectations, timeout: 1.0). Strukturen UserState — enum med associated values — gör det möjligt att kontrollera skärmens exakta tillstånd efter en operation.

Kodtäckning i IT Sectr-projekt på MVVM är 75-90% för ViewModel och Repository. ViewModel täcks av enhetstester, Repository — av integrationstester med en testdatabas. View i SwiftUI och Jetpack Compose testas med UI-tester (XCUITest, Compose Test) för kritiska scenarier. Resterande UI kontrolleras med skärmbildstester (Snapshot Testing) — detta är snabbare än UI-tester och ger 95% förtroende för korrekt visning.

Vanliga frågor

Vad är den största skillnaden mellan MVVM och MVP?

I MVVM har ViewModel ingen referens till View — data överförs via reaktiva mekanismer (LiveData, StateFlow, @Published). I MVP anropar Presenter direkt View-metoder via ViewContract-gränssnittet. MVVM eliminerar ViewContract och manuell attach/detach, men kräver förståelse för reaktiva flöden. ViewModel överlever skärmrotation på Android, Presenter kräver retain-fragment.

Vilka bibliotek behövs för MVVM på Android?

Minsta uppsättning: lifecycle-viewmodel-ktx (ViewModel), lifecycle-livedata-ktx eller kotlinx-coroutines-core (StateFlow). För injicering — Hilt eller Koin. För asynkrona operationer — Kotlin Coroutines. För komplex databindning — DataBinding. I Jetpack Compose (rekommenderat av Google sedan 2022) räcker compose-runtime och lifecycle-viewmodel-compose.

Varför rekommenderar Apple MVVM för iOS?

SwiftUI (2019) är designat för reaktiv arkitektur: @State och @Published ritar automatiskt om View när data ändras. MVVM är en naturlig passform för SwiftUI: View — @ViewBuilder, ViewModel — ObservableObject. Apple tvingar inte MVVM som det enda mönstret, men allt utbildningsmaterial sedan 2019 använder ViewModel + SwiftUI. För UIKit rekommenderar Apple MVC eller Coordinator.

Hur undviker man minnesläckor i ViewModel?

Android: viewModelScope avbryter automatiskt korutiner vid rensning av ViewModel. iOS: AnyCancellable från Combine avprenumererar automatiskt när objektet som lagrar det frigörs. SwiftUI @StateObject hanterar livscykeln automatiskt. Huvudregler: lagra inte referenser till View/Context i ViewModel, avbryt långvariga operationer vid rensning, använd weak self i slutningar.

Vad välja: LiveData eller StateFlow för Android?

StateFlow — det moderna valet. LiveData är enklare och livscykelsäkert, men StateFlow är kraftfullare: arbetar med korutiner, stöder flatMap, combine, filter, kräver inte @Nullable-annotering. Det enda scenariot där LiveData är att föredra — arbete med Java-kod där StateFlow (Kotlin Flow-API) inte är tillgängligt. Google rekommenderar StateFlow för nya projekt i Kotlin.

Sammanfattning

  • MVVM (Model-View-ViewModel) — reaktivt mönster där ViewModel inte har referens till View
  • ViewModel — överlever skärmrotation, testas med enhetstester, oberoende av UI
  • Android — ViewModel + StateFlow + Kotlin Coroutines — modern stack från Google
  • iOS — ObservableObject + @Published + SwiftUI — inbyggd implementering av MVVM
  • MVVM vs MVP — MVVM eliminerar ViewContract och manuell attach/detach
  • Testning — ViewModel täckt av enhetstester utan plattformsberoenden
  • Rekommendation — MVVM för nya projekt; MVP för legacy-support

Vi utvecklar en mobil applikation nyckelfärdigt

IT Sectr skapar iOS- och Android-applikationer för startups och företag sedan 2017. Vi ger dig råd och föreslår den bästa lösningen.

Diskutera projektet

Läs också