MVC (Model-View-Controller) è un pattern architetturale che divide un'applicazione in tre componenti: Model è responsabile dei dati e della logica di business, View è responsabile dell'interfaccia utente, Controller è responsabile dell'elaborazione degli input e del coordinamento tra Model e View. In iOS, MVC è implementato tramite UIViewController, in Android — tramite Activity e Fragment. MVC rimane il pattern fondamentale su cui sono costruiti MVVM, MVP e Clean Architecture. Scopri di più su MVC in Cocoa Core.
Punti chiave
MVC (Model-View-Controller) è un pattern architetturale proposto da Trygve Reenskaug nel 1979 per il linguaggio Smalltalk-80. Il pattern divide un'applicazione in tre strati: Model contiene dati e logica di business, View è responsabile della visualizzazione, Controller elabora l'input dell'utente e aggiorna Model e View. La separazione delle responsabilità consente di modificare ogni strato indipendentemente — ad esempio, sostituire View da UIKit a SwiftUI senza cambiare la logica di business in Model.
Interazione dei componenti in MVC segue un ciclo: l'utente interagisce con View → Controller riceve l'evento → Controller aggiorna Model → Model notifica Controller dei cambiamenti → Controller aggiorna View. Nell'implementazione classica, Model usa il pattern Observer: quando i dati cambiano, Model invia notifiche, Controller si sottoscrive e aggiorna View. Nell'implementazione Apple, Key-Value Observing (KVO) o NotificationCenter svolgono questo ruolo.
| Componente | Responsabilità | Esempio in iOS | Esempio in Android |
|---|---|---|---|
| Model | Dati, logica di business, rete | Struct User, CoreData | Data class, Repository |
| View | Visualizzazione UI | Storyboard, XIB, UIView | XML layout, Jetpack Compose |
| Controller | Elaborazione input, coordinamento | UIViewController | Activity, Fragment |
MVC nello sviluppo mobile moderno è usato meno di 10 anni fa, ma rimane essenziale da capire. Apple raccomanda MVC per schermi semplici nelle applicazioni UIKit. Google non raccomanda MVC puro per Android — la documentazione ufficiale suggerisce MVVM con Jetpack. Tuttavia, la conoscenza di MVC è necessaria per lavorare con progetti legacy e per comprendere l'evoluzione dei pattern architetturali.
Apple MVC è un'implementazione personalizzata del pattern integrata in UIKit. UIViewController agisce come Controller: gestisce il ciclo di vita dello schermo (viewDidLoad, viewWillAppear, viewDidDisappear), gestisce tocchi e azioni dell'utente, aggiorna View tramite IBOutlets. View viene creata in Interface Builder (storyboard o XIB) o programmaticamente. Model — qualsiasi oggetto dati: servizi di rete, stack CoreData, strutture Swift.
final class UserViewController: UIViewController {
// View (tramite storyboard outlet)
@IBOutlet private var nameLabel: UILabel!
@IBOutlet private var emailLabel: UILabel!
// Model
private let userService = UserService()
override func viewDidLoad() {
super.viewDidLoad()
loadUser()
}
private func loadUser() {
userService.fetchUser { [weak self] user in
// Controller aggiorna View
self?.nameLabel.text = user.name
self?.emailLabel.text = user.email
}
}
}
Il problema di Apple MVC — View e Controller sono strettamente accoppiati. UIViewController gestisce simultaneamente View e logica. Storyboard memorizza View in XML, ma il controller ha riferimenti diretti agli elementi UI tramite IBOutlets. Questo viola il principio di responsabilità unica: il controller è responsabile del ciclo di vita, delegati, datasource, target-action e animazioni. Di conseguenza, uno schermo standard di un'app iOS contiene 200–500 righe nel controller.
Ciclo di vita del ViewController — Apple fornisce 6 metodi del ciclo di vita: loadView (creazione manuale di View), viewDidLoad (dopo il caricamento di View in memoria), viewWillAppear (prima della comparsa sullo schermo), viewDidAppear (dopo l'animazione), viewWillDisappear (prima di lasciare lo schermo), viewDidDisappear (dopo aver lasciato). Ogni metodo è un posto per posizionare la logica in MVC. Usare questi metodi per la logica di business accelera la crescita del controller.
Android MVC — Activity e Fragment agiscono come Controller, i file XML layout come View, qualsiasi classe POJO con dati come Model. Activity gestisce il ciclo di vita dello schermo: onCreate, onStart, onResume, onPause, onStop, onDestroy. Fragment è un sottoschermo all'interno di Activity con il proprio ciclo di vita. View (XML) è separata da Controller e caricata tramite setContentView o LayoutInflater. Model — repository, database, chiamate di rete.
class UserActivity : AppCompatActivity() {
// View tramite XML layout
private lateinit var binding: ActivityUserBinding
// Model
private val userRepository = UserRepository()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = ActivityUserBinding.inflate(layoutInflater)
setContentView(binding.root)
loadUser()
}
private fun loadUser() {
userRepository.getUser { user ->
runOnUiThread {
binding.nameText.text = user.name
binding.emailText.text = user.email
}
}
}
}
Android ViewBinding e DataBinding — strumenti moderni che riducono l'accoppiamento tra Controller e View. ViewBinding genera una classe con riferimenti diretti alle Views da XML, eliminando findViewById. DataBinding aggiunge la possibilità di legare dati all'UI nel markup XML tramite @{user.name}. DataBinding è un passo verso MVVM, poiché consente di passare dati da Model a View senza codice in Activity. Google raccomanda DataBinding per tutti i nuovi progetti.
Ciclo di vita Android è più complesso di iOS: Activity può essere distrutta e ricreata durante la rotazione dello schermo, mancanza di memoria o cambio di configurazione. Nel puro MVC, il controller (Activity) contiene logica che viene persa alla distruzione. Ciò richiede il salvataggio dello stato tramite onSaveInstanceState o ViewModel da Jetpack, che va oltre il puro MVC e avvicina l'architettura a MVVM.
Massive View Controller è un termine che descrive il problema principale di MVC nello sviluppo mobile. Il controller in iOS e Android assume troppe responsabilità: elaborazione input, validazione dati, interazione di rete, navigazione, caching, animazioni, gestione del ciclo di vita. Di conseguenza, il controller si gonfia fino a 500–2000 righe di codice, diventando difficile da leggere, testare e mantenere.
Cause del Massive View Controller — l'architettura di UIKit e Android Framework incoraggia a posizionare la logica nel controller. Chiamate di rete, elaborazione JSON, navigazione — tutto questo finisce naturalmente in Activity o UIViewController perché hanno accesso al ciclo di vita e all'UI. Lo sviluppatore deve consapevolmente estrarre la logica in classi separate (Service, Manager, Interactor), il che richiede disciplina e comprensione dei principi architetturali.
| Problema di MVC | Descrizione | Soluzione |
|---|---|---|
| Forte accoppiamento | Controller conosce View e Model | MVVM — ViewModel non conosce View |
| Complessità di test | Controller dipende da UIKit/Android | Estrarre logica in servizi |
| Ciclo di vita | Lo stato viene perso alla rotazione | ViewModel da Jetpack/SwiftUI |
| Mancanza di navigazione | Controller gestisce le transizioni | Pattern Coordinator, Router |
Test di MVC — Model viene testato isolatamente con test unitari. Controller è difficile da testare a causa della dipendenza da UIKit/UIFoundation. XCTest non consente di creare UIViewController senza una finestra di visualizzazione. Per Android, ActivityTestRule e Robolectric risolvono parzialmente il problema, ma i test sono lenti. View di solito non viene testata con test unitari — per l'UI vengono utilizzati test screenshot e test UI (XCUITest, Espresso).
Quando MVC è giustificato — schermi semplici con uno o due elementi (schermo di login, profilo, impostazioni). Prototipi e MVP per la validazione di ipotesi — MVC è più veloce da scrivere senza strati aggiuntivi. Progetti con base di codice piccola fino a 10–15 schermi. Nei progetti complessi, MVC porta all'accumulo di debito tecnico e richiede refactoring ogni 6–12 mesi.
MVC vs MVVM — la differenza principale: in MVVM, il controller è sostituito da ViewModel che non ha riferimento a View. I dati vengono passati tramite Observable (SwiftUI), LiveData/StateFlow (Android) o Combine/RxSwift. ViewModel è testabile con test unitari senza dipendenze UI. Apple raccomanda MVVM con SwiftUI dal 2019, Google — MVVM con LiveData/Flow come architettura ufficiale Android. MVVM richiede più codice per il binding ma migliora significativamente la testabilità.
MVC vs MVP — in MVP (Model-View-Presenter), Presenter è uno strato testabile che riceve View tramite un'interfaccia. A differenza di MVC dove Controller gestisce direttamente View tramite UIKit, Presenter non dipende dal framework — funziona tramite l'astrazione ViewInterface. MVP era popolare nello sviluppo Android prima di Jetpack ed è utilizzato in progetti legacy. Presenter sopravvive ad Activity e preserva lo stato alla rotazione dello schermo.
MVC vs Clean Architecture — Clean Architecture aggiunge strati: Use Cases (Interactors), Entities, Gateways e Repository. MVC rimane nello strato Presentation, ma la logica di business viene spostata nello strato Domain con Use Cases. Clean Architecture risolve radicalmente il problema del Massive View Controller — Controller contiene solo chiamate Use Cases e aggiornamenti di View. Lo svantaggio è un aumento significativo del numero di classi e file, giustificato per progetti con 50+ schermi.
// MVC in iOS: Controller contiene tutto
class OrderViewController: UIViewController {
func placeOrder() {
// Validazione + rete + aggiornamento UI
guard Validation.isValid(total) else { return }
NetworkService.shared.submit(order) { [weak self] result in
self?.handleResult(result)
}
}
}
// MVVM: logica in ViewModel
class OrderViewModel: ObservableObject {
@Published var state: OrderState = .idle
func placeOrder() { /* logica di business */ }
}
Scelta dell'architettura dipende dalle dimensioni del team, dall'ambito del progetto e dalla testabilità richiesta. Per un team di 1–2 sviluppatori e un progetto fino a 20 schermi, MVVM funziona bene. Per un team grande di 5+ sviluppatori e un progetto con 50+ schermi — Clean Architecture con struttura modulare. MVC rimane rilevante per comprendere l'evoluzione delle architetture, mantenere progetti legacy e per schermi UIKit semplici senza logica di business complessa.
Domande frequenti
Il problema principale è il Massive View Controller. In iOS, UIViewController gestisce tutto: elaborazione input, aggiornamento View, rete, navigazione e ciclo di vita. In Android, Activity/Fragment svolge funzioni simili. Di conseguenza, il controller si gonfia fino a migliaia di righe di codice, diventando difficile da testare e mantenere, violando il principio di responsabilità unica.
In MVC, il controller aggiorna direttamente View ed elabora l'input dell'utente. In MVVM, il ruolo del controller è svolto da ViewModel, che non ha riferimento a View — i dati vengono passati tramite meccanismi di binding. MVVM è più facile da testare perché ViewModel non dipende da UIKit o Android Framework. Apple raccomanda MVVM con SwiftUI, Google raccomanda MVVM con Jetpack Compose.
Sì, MVC rimane un pattern funzionante per schermi semplici e prototipi. Apple raccomanda MVC per applicazioni UIKit con schermi semplici. Per progetti complessi con molti schermi, richieste di rete e caching, è meglio scegliere MVVM, VIPER o Clean Architecture. I principianti sono invitati a padroneggiare MVC prima di apprendere pattern più complessi.
Model viene testato isolatamente — sono normali oggetti dati e logica di business. Controller è difficile da testare a causa della dipendenza da UIKit o Android Framework. Si consiglia di estrarre la logica di business dal controller in servizi o interactor separati, che vengono testati con test unitari. View di solito non viene testata con test unitari — vengono utilizzati test UI e test screenshot.
Su iOS — MVVM con SwiftUI e Combine, standard Apple dal 2019. Su Android — MVVM con LiveData o StateFlow, ufficialmente raccomandato da Google. Per grandi progetti con team di 5+ sviluppatori — Clean Architecture con VIPER su iOS o Clean Architecture su Android con separazione dei moduli per funzionalità. Per progetti legacy con MVC — refactoring graduale con estrazione della logica in servizi separati.
Riepilogo
Svilupperemo un'applicazione mobile chiavi in mano
IT Sectr crea applicazioni iOS e Android per startup e aziende dal 2017. Ti consulteremo e ti proporremo la soluzione migliore.
Leggi anche