Scopri perché Apple ha creato Swift, come ha progressivamente sostituito Objective‑C nelle app iOS e cosa significa quel cambiamento per tooling, assunzioni e codebase oggi.

Swift non è nato solo perché Apple voleva un nuovo linguaggio “per gioco”. È stata una risposta a problemi concreti nello sviluppo iOS: iterazione lenta, pattern insicuri facili da scrivere per errore e una crescente discrepanza tra la complessità delle app moderne e il design più datato di Objective‑C.
Questo post risponde a una domanda pratica: perché esiste Swift, come è diventato il predefinito e perché quella storia influenza ancora oggi il tuo codebase e le decisioni del team.
Avrai una timeline chiara e leggera — dalle prime release di Swift fino a una toolchain stabile e ampiamente adottata — senza perderti in trivia. Lungo il percorso collegheremo la storia a conseguenze quotidiane: come gli sviluppatori scrivono codice più sicuro, come sono evolute le API, cosa è cambiato nei workflow di Xcode e cosa significa “Swift moderno” con feature come la concorrenza e SwiftUI.
Objective‑C è ancora presente in molte app di successo, specialmente codebase più vecchie e certe librerie. L'obiettivo qui non è creare allarme: Swift non ha cancellato Objective‑C dall'oggi al domani; l'ha gradualmente sostituito tramite interoperabilità e cambiamenti nell'ecosistema.
Objective‑C è stato il fondamento dello sviluppo Apple per decenni. Quando arrivò il primo SDK per iPhone nel 2008, Objective‑C (insieme ai framework Cocoa Touch) era il modo principale per costruire app, proprio come era successo per Mac OS X con Cocoa. Se hai scritto app iOS nei primi anni, hai in pratica appreso le convenzioni della piattaforma attraverso Objective‑C.
Objective‑C aveva molti punti di forza — soprattutto se si aderiva al “modo Cocoa” di costruire software.
Era costruito su un runtime dinamico potente: messaging, introspezione, category e method swizzling permettevano pattern flessibili e “plug-in friendly”. Convenzioni Cocoa come delegation, target–action, notifiche e KVC/KVO erano profondamente integrate e ben documentate.
Non meno importante, aveva un ecosistema maturo. I framework Apple, le librerie di terze parti e anni di risposte su Stack Overflow assumevano Objective‑C. Tooling e API erano costruiti attorno a lui e le aziende potevano assumere sviluppatori con skill prevedibili.
I punti dolenti non erano filosofici, ma pratici, attriti quotidiani.
Objective‑C poteva essere verboso, soprattutto per operazioni apparentemente "semplici". I nomi dei metodi, le parentesi e il boilerplate allungavano il codice e ne rendevano difficile la lettura rapida. Molte API esponevano concetti con puntatori che aumentavano la probabilità di errori, specialmente prima che ARC (Automatic Reference Counting) diventasse standard.
Memoria e sicurezza erano preoccupazioni costanti. Anche con ARC, serviva capire ownership, reference cycle e come la nullability potesse sorprendere a runtime.
Interfacciarsi con API C era comune e non sempre piacevole: bridgare tipi C, gestire Core Foundation e il “toll‑free bridging” aggiungeva overhead mentale che non sembrava scrivere codice app moderno.
Le codebase iOS legacy spesso si basano su Objective‑C perché sono stabili, collaudate e costose da riscrivere. Molte app longeve includono layer Objective‑C (o dipendenze più vecchie) che fanno ancora lavoro reale — e lo fanno in modo affidabile.
Apple non ha creato Swift perché Objective‑C fosse “rotto”. Objective‑C ha alimentato anni di app iPhone e Mac di successo. Ma con l'aumento delle dimensioni delle app, dei team e delle API, i costi di alcune impostazioni di default di Objective‑C sono diventati più visibili — specialmente quando si moltiplicano piccoli rischi su milioni di righe di codice.
Un obiettivo principale era rendere gli errori comuni più difficili da scrivere. La flessibilità di Objective‑C è potente, ma può nascondere problemi fino al runtime: inviare messaggi a nil, tipi id ambigui o gestire male la nullability nelle API. Molti di questi problemi si gestivano con disciplina, convenzioni e review, ma restavano costosi su larga scala.
Swift incorpora dei paletti: gli optional ti costringono a pensare “questo può mancare?”, il tipaggio forte riduce l'uso improprio accidentale e feature come guard, l'esaurienza di switch e una gestione più sicura delle collection spostano molti bug dal runtime al tempo di compilazione.
Swift ha anche modernizzato l'esperienza quotidiana di scrittura del codice. Sintassi concisa, type inference e una libreria standard più ricca rendono molte operazioni più chiare con meno boilerplate rispetto a header/implementation, workaround per generics verbosi o uso intensivo di macro.
Sul fronte prestazioni, Swift è stato progettato per permettere ottimizzazioni aggressive del compilatore (soprattutto con tipi valore e generics). Questo non significa che ogni app Swift sia automaticamente più veloce di ogni app Objective‑C, ma dà ad Apple un modello linguistico che può evolvere verso performance migliori senza affidarsi tanto al runtime dinamico.
Apple aveva bisogno che lo sviluppo iOS fosse accessibile ai nuovi sviluppatori e sostenibile per prodotti di lunga vita. Le convenzioni di naming di Swift, l'intento più chiaro nei call site e l'enfasi su tipi espressivi mirano a ridurre la "conoscenza tribale" e rendere le codebase più facili da leggere a distanza di mesi.
Il risultato: meno trappole per i piedi, API più pulite e un linguaggio che supporta meglio team grandi che mantengono app per anni — senza pretendere che Objective‑C non fosse in grado di fare il lavoro.
Swift non ha “vinto” dall'oggi al domani. Apple lo ha introdotto come opzione migliore per nuovo codice, poi ha passato anni a renderlo stabile, più veloce e più facile da adottare insieme alle app Objective‑C esistenti.
La stabilità ABI significa che il runtime e le librerie standard Swift sono compatibili a livello binario tra le versioni Swift 5 sulle piattaforme Apple. Prima di Swift 5 molte app dovevano includere librerie Swift dentro l'app, aumentando le dimensioni e complicando la distribuzione. Con ABI stabile, Swift è diventato più simile a Objective‑C nel modo in cui il codice compilato può eseguire attraverso aggiornamenti OS, aiutando Swift a sembrare "sicuro" per codebase di produzione a lunga durata.
Per anni molte squadre hanno usato Swift per nuove feature lasciando i moduli core in Objective‑C. Questo percorso graduale — invece di una riscrittura totale — ha reso la crescita di Swift pratica per app reali e scadenze reali.
Swift non ha vinto costringendo ogni team a buttare via il codice Objective‑C funzionante. Apple ha fatto in modo che entrambi i linguaggi potessero coesistere nello stesso target app. Questa compatibilità è una grande ragione per cui l'adozione di Swift non si è fermata subito.
Una codebase mista è normale su iOS: potresti tenere networking, analytics o componenti UI più vecchi in Objective‑C mentre scrivi nuove feature in Swift. Xcode supporta tutto questo direttamente, quindi “Swift che sostituisce Objective‑C” di solito significa cambiamento graduale, non una riscrittura unica e massiva.
L'interoperabilità funziona tramite due meccanismi complementari:
YourModuleName-Swift.h) che espone le classi e i metodi Swift compatibili con Objective‑C. Di solito ti abiliti con attributi come @objc o ereditando da NSObject.Non serve memorizzare il plumbing per beneficiarne, ma capire che c'è un passo esplicito “esponi questo all'altro linguaggio” aiuta a spiegare perché alcuni tipi appaiono automaticamente e altri no.
Le squadre incontrano alcuni punti ricorrenti:
Le app reali vivono a lungo. L'interoperabilità ti permette di migrare feature per feature, continuare a rilasciare e ridurre il rischio — soprattutto quando SDK di terze parti, componenti legacy o vincoli di tempo rendono irrealistica una conversione totale.
Swift non ha solo modernizzato la sintassi: ha cambiato l'aspetto del codice iOS nella quotidianità. Molti pattern che prima si basavano su convenzioni (e review attente) sono diventati cose che il compilatore può far rispettare.
nil esplicitoGli sviluppatori Objective‑C erano abituati a inviare messaggi a nil senza effetti apparenti. Swift rende l'assenza esplicita con gli optional (String?), spingendoti a gestire i valori mancanti con if let, guard o ??. Questo tende a prevenire categorie intere di crash e bug logici “perché questo è vuoto?” — senza illudere che gli errori non possano più accadere.
Swift può inferire i tipi in molti punti, riducendo boilerplate mantenendo il codice leggibile:
let title = "Settings" // inferred as String
Più importante, i generics permettono di scrivere codice riutilizzabile e type-safe senza affidarsi a id e controlli runtime. Confronta “array di qualsiasi cosa” con “array esattamente del tipo che mi aspetto”: meno oggetti inaspettati e meno cast forzati.
Il throw/try/catch di Swift incoraggia percorsi di errore espliciti invece di ignorare i fallimenti o passare NSError **. Le collection sono fortemente tipizzate ([User], [String: Int]) e le stringhe sono String Unicode-correct, invece di un mix di C string, NSString e scelte manuali di codifica. L'effetto netto è meno off-by-one, meno ipotesi invalide e meno “compila ma si rompe a runtime”.
Il runtime di Objective‑C è ancora prezioso per pattern runtime-heavy: method swizzling, dynamic message forwarding, certi approcci di dependency injection, codice guidato da KVC/KVO e architetture in stile plugin più vecchie. Swift può interoperare con questi, ma quando serve vera dinamicità runtime Objective‑C rimane uno strumento pratico.
Swift non ha solo cambiato la sintassi — ha costretto l'ecosistema iOS a modernizzare tooling e convenzioni. La transizione non è sempre stata liscia: le prime release Swift spesso comportavano build più lente, autocomplete meno affidabile e refactor più rischiosi. Col tempo l'esperienza sviluppatore è diventata uno dei maggiori vantaggi di Swift.
Il supporto a Swift in Xcode è migliorato in modi pratici:
Se hai usato Swift nelle versioni 1.x/2.x probabilmente ricordi degli spigoli vivi. Da allora la tendenza è stata costante: indexing migliore, stabilità delle sorgenti e meno momenti in cui “Xcode è confuso”.
Swift Package Manager (SPM) ha ridotto la necessità di sistemi di dipendenze esterni in molte squadre. A livello base dichiari pacchetti e versioni, Xcode li risolve e li integra senza manipolazioni complesse dei file di progetto. Non è perfetto per ogni setup, ma per molte app ha semplificato onboarding e aggiornamenti di dipendenze.
Le API Design Guidelines di Apple hanno spinto i framework verso naming più chiari, comportamenti di default migliori e tipi che comunicano l'intento. Questa influenza si è estesa: le librerie di terze parti hanno sempre più API Swift-first, rendendo le codebase moderne più coerenti — anche quando continuano a bridgare a Objective‑C sotto il cofano.
UIKit non è sparito. La maggior parte delle app iOS di produzione lo usa ancora intensamente, soprattutto per navigazione complessa, gesture avanzate, gestione testuale fine e per la lunga serie di componenti UI collaudati nel tempo. Quel che è cambiato è che Swift è ora il modo predefinito di scrivere codice UIKit — anche se il framework sottostante è lo stesso.
Per molte squadre “app UIKit” non implica più Objective‑C. Storyboard, nib e viste programmatiche sono guidate da view controller e modelli in Swift.
Questo cambiamento conta perché Apple progetta sempre più nuove API pensando all'ergonomia Swift (naming più chiaro, optionals, generics, result types). Anche quando un'API esiste per Objective‑C, l'overlay Swift spesso sembra la superficie “intenzionata”, il che accelera la produttività dei nuovi sviluppatori in una codebase UIKit.
SwiftUI non è stato solo un nuovo modo di disegnare pulsanti. Ha introdotto un modello mentale diverso:
Nella pratica ha cambiato le discussioni sull'architettura. Le squadre che adottano SwiftUI tendono a mettere più attenzione su unidirectional data flow, componenti view più piccoli e isolamento degli effetti collaterali (networking, persistenza) fuori dal codice view. Anche se non si adotta al 100%, SwiftUI riduce il boilerplate per schermate composte principalmente da form, liste e layout guidati dallo stato.
Le app in produzione spesso combinano entrambi i framework:
UIHostingController per nuove schermate;Questo approccio ibrido permette di mantenere l'infrastruttura UIKit matura (stack di navigazione, coordinator, design system) mentre si adotta SwiftUI dove porta un vantaggio netto. Mantiene anche il rischio gestibile: puoi pilotare SwiftUI in aree contenute senza riscrivere l'intero layer UI.
Se stai iniziando oggi, la storia UI più nuova di Apple è SwiftUI e molte tecnologie adiacenti (widget, Live Activities, alcune estensioni app) sono fortemente orientate a Swift. Anche al di fuori della UI, le API Swift-friendly su tutte le piattaforme Apple tendono a modellare i default: i nuovi progetti sono solitamente pianificati con Swift e la decisione diventa “quanto UIKit ci serve?” piuttosto che “usiamo Objective‑C?”.
Il risultato non è tanto una sostituzione di framework, quanto Swift che diventa la lingua comune sia per il percorso compatibile con il legacy (UIKit) sia per il nuovo percorso Swift-native (SwiftUI).
La concorrenza è come un'app fa più cose “allo stesso tempo” — caricare dati, decodificare JSON e aggiornare lo schermo — senza bloccare l'interfaccia. L'approccio moderno di Swift è pensato per far sembrare questo lavoro più simile a codice normale e meno come un esercizio di gestione dei thread.
Con async/await puoi scrivere lavoro asincrono (come una richiesta di rete) in stile top-to-bottom:
async marca una funzione che può sospendersi mentre aspetta qualcosa.await è il punto in cui la funzione "si mette in pausa fino al risultato".Invece di callback annidati leggendolo come una ricetta: fetch dati, parse, poi aggiorna lo stato.
Swift abbina async/await alla structured concurrency, che significa: “il lavoro in background dovrebbe avere un proprietario e una durata chiari.” I task sono creati in uno scope e i child task sono legati al parent.
Questo migliora:
Due concetti aiutano a ridurre crash dovuti ad accessi concorrenti:
La maggior parte dei team non cambia tutto dall'oggi al domani. È comune mescolare la nuova concorrenza Swift con pattern più vecchi — OperationQueue, GCD, callback delegate e completion handler — specialmente quando si integrano librerie legacy o flussi UIKit più datati.
Migrare una vera app iOS non è un progetto “converti tutto” ma di gestione del rischio. L'obiettivo è continuare a rilasciare mentre riduci gradualmente la quantità di Objective‑C da mantenere.
Un approccio comune è la migrazione incrementale:
Questo ti permette di costruire fiducia contenendo il raggio d'azione — particolarmente utile quando le scadenze non aspettano una transizione linguistica.
Alcuni pattern Objective‑C non si traducono bene:
objc_runtime può richiedere un redesign più che una traduzione.Tratta la migrazione come uno swap di implementazione, non come un cambiamento di feature. Aggiungi test di caratterizzazione sui flussi critici (networking, caching, pagamenti, auth) prima di portare il codice. I snapshot test possono aiutare a individuare regressioni UI quando il codice UIKit si sposta in Swift.
Crea uno standard leggero per il team: convenzioni di coding, linting (SwiftLint o simili), boundary di modulo, regole per il bridging header e “no nuovo Objective‑C a meno che non giustificato.” Metterlo per iscritto evita che la codebase diventi bilingue in modi incoerenti.
Swift non ha solo cambiato la sintassi — ha cambiato cosa significa «normale» in un team iOS. Anche se il tuo prodotto contiene ancora Objective‑C, le decisioni quotidiane su persone, processo e manutenzione a lungo termine sono oggi modellate da aspettative Swift-first.
La maggior parte dei nuovi ruoli iOS assume Swift come default. I candidati potrebbero non aver mai prodotto Objective‑C in ambito professionale, e questo impatta i tempi di ramp-up in una codebase mista.
Un consiglio pratico: considera Objective‑C come un “plus” e non come requisito, e prepara materiale di onboarding che spieghi dove esiste Objective‑C e perché. Una breve guida interna su bridging header, ownership dei file e aree “non toccare senza contesto” può evitare errori iniziali.
Il tipaggio più forte e gli optional di Swift possono rendere le review più rapide: i revisori spendono meno tempo a indovinare cosa può contenere un valore e più tempo a validare l'intento. Pattern come protocol, generics e tipi valore incoraggiano architetture più consistenti — se usati con giudizio.
Il rovescio della medaglia è la deriva di stile. I team beneficiano di una style guide condivisa e di formattazione/linting automatizzati così le review si concentrano sul comportamento, non sullo stile.
La manutenzione diventa più semplice quando puoi appoggiarti direttamente ad API moderne invece di portare wrapper custom intorno a pattern vecchi. Ma Objective‑C non sparirà dall'oggi al domani — soprattutto in app mature con moduli collaudati.
Pianifica realisticamente per codebase miste: programma la migrazione attorno a milestone di business, non come un "cleanup" infinito. Definisci cosa significa "finito" (es. tutto il nuovo codice in Swift, moduli legacy toccati solo opportunisticamente) e rivedi quella regola durante refactor importanti.
Per i framework decisionali vedi /blog/migrating-objective-c-to-swift.
Scegliere tra Swift e Objective‑C raramente è un dibattito filosofico: è una decisione su costi, rischio e timeline. La buona notizia è che raramente devi scegliere "tutto o niente". La maggior parte dei team ottiene il miglior risultato evolvendo la codebase in loco.
Se stai partendo da zero, Swift dovrebbe essere il default. Si allinea con le API più recenti di Apple, ha feature di sicurezza e dà accesso semplice a pattern moderni come async/await.
La prima decisione è la strategia UI: UIKit in Swift, SwiftUI o un mix. Se sei indeciso confronta i tradeoff in /blog/swiftui-vs-uikit.
Per un'app Objective‑C esistente, mantieni i moduli stabili in Objective‑C e introduci Swift dove otterrai benefici rapidi:
Una regola pratica: inizia un nuovo modulo Swift quando puoi definire confini chiari (surface API, ownership, test). Tieni Objective‑C stabile quando il codice è maturo, fortemente intrecciato o rischioso da toccare senza un refactor più ampio.
Per pianificare, consulta /blog/ios-migration-checklist.
Per persone e team questa sequenza si adatta bene allo sviluppo iOS quotidiano:
La modernizzazione del codice iOS spesso porta a lavori "adiacenti" che competono per lo stesso tempo ingegneristico: dashboard amministrative, tool interni, servizi backend e API da cui dipende l'app iOS. Se vuoi mantenere il tuo team iOS concentrato sulla migrazione Swift mentre continui a rilasciare software di supporto, Koder.ai può aiutarti a generare web app, backend Go (con PostgreSQL) o companion app Flutter tramite un flusso chat — poi esportare il codice sorgente, distribuire e usare snapshot/rollback per gestire l'iterazione in sicurezza.
Se vuoi aiuto esterno per definire il passo più sicuro successivo — nuovo modulo, migrazione parziale o “lascia tutto com'è” — vedi /pricing.
Swift è stato creato per ridurre i rischi comuni nello sviluppo iOS (come il comportamento inatteso di nil e la tipizzazione debole), migliorare leggibilità e manutenibilità nelle codebase grandi e permettere ottimizzazioni del compilatore nel tempo. Non si tratta di dire che Objective‑C fosse “cattivo”, ma di adottare impostazioni predefinite più sicure e moderne su larga scala.
Swift è diventato il linguaggio predefinito grazie a una progressiva combinazione di fattori:
Questo ha reso “scrivere nuovo codice in Swift” la scelta più semplice per molte squadre.
Swift 5 ha introdotto la stabilità ABI sulle piattaforme Apple, il che significa che il codice compilato in Swift può rimanere compatibile a livello binario tra le versioni runtime Swift 5.x fornite dal sistema operativo. Nella pratica ha ridotto la necessità di includere librerie Swift dentro l'app, migliorato le dimensioni e l'affidabilità delle distribuzioni e reso Swift più sicuro per codebase di produzione a lungo termine.
In un target app puoi mescolare entrambi i linguaggi:
YourModuleName-Swift.h) che espone le API Swift compatibili con Objective‑C, di solito usando @objc o ereditando da NSObject.Non tutte le feature Swift sono visibili a Objective‑C, quindi pianifica i confini con attenzione.
Le optionals (T?) rendono esplicita l'assenza di un valore e obbligano a gestirla a tempo di compilazione (es. if let, guard, ??). In Objective‑C inviare messaggi a nil e la nullabilità ambigua possono nascondere bug fino al runtime. Il vantaggio pratico è meno crash e meno errori legati a valori inaspettatamente vuoti.
Generics e tipizzazione forte riducono i cast e i controlli di tipo a runtime (comuni con id e collezioni non tipizzate in Objective‑C). Nella pratica ottieni:
[User] invece di “array di qualsiasi cosa”;Sì — Objective‑C è ancora la scelta giusta quando servono dinamiche runtime pesanti (ad es. uso intenso di KVC/KVO, method swizzling, API basate su selector o architetture in stile plugin). Swift può interoperare con questi pattern, ma le equivalenti puramente Swift spesso richiedono un riprogettazione invece che una traduzione diretta.
Un approccio pratico è la migrazione incrementale:
Trattala come gestione del rischio: continua a rilasciare mentre riduci i costi di manutenzione a lungo termine.
I problemi più comuni includono:
@objc, NSObject e feature limitate);Pianifica i confini e aggiungi test prima di sostituire implementazioni.
Assolutamente no. Molte app di produzione sono ibride:
UIHostingController per incorporare schermate SwiftUI dentro UIKit;Questo permette di adottare SwiftUI dove riduce boilerplate mantenendo la navigazione matura, i coordinator e i componenti complessi di UIKit.