HTML CSS JAVASCRIPT SQL MYSQL PYTHON JAVA PHP C C++ C# PACKET TRACER CYBERSECURITY REACT TYPESCRIPT NODE.JS GIT & GITHUB DATA SCIENCE BOOTSTRAP GO KOTLIN JQUERY DJANGO VUE ANGULAR MONGODB POSTGRESQL SASS NUMPY PANDAS SCIPY DSA RUST SWIFT BASHXMLW3.CSSANGULARJSAIAWSPROGRAMMAZIONEEXCEL WORD POWERPOINT
Corso gratuito

Corso Swift in italiano

Il linguaggio moderno di Apple per sviluppare app iOS e macOS: sicuro, veloce ed espressivo. Impara variabili, Optional, closures, struct, classi, protocolli e gestione degli errori. Con esempi pratici ed esercizi.

Introduzione a Swift

Swift è il linguaggio di programmazione moderno sviluppato da Apple nel 2014 per costruire app per iOS, macOS, watchOS e tvOS. È potente, sicuro, espressivo e veloce, e ha sostituito Objective-C come linguaggio principale dell'ecosistema Apple.

CaratteristicaDescrizione
SicuroOptional e type safety eliminano molti crash a runtime
VeloceCompilato e ottimizzato, performance vicine al C
ModernoSintassi pulita, inferenza di tipo, closures, generics
Open sourceDisponibile anche su Linux e Windows
SwiftUIFramework dichiarativo per costruire interfacce

Con Swift puoi creare app per iPhone e iPad, applicazioni desktop per Mac, e persino servizi backend lato server.

💡 Questo corso copre i fondamenti del linguaggio Swift: la base indispensabile prima di affrontare SwiftUI e lo sviluppo di app iOS complete.

Installazione e primi passi

Per programmare in Swift hai diverse opzioni a seconda del tuo sistema operativo.

PiattaformaStrumento
macOSXcode (gratuito su Mac App Store) — IDE completo
iPadSwift Playgrounds — app per imparare interattivamente
Linux / WindowsSwift toolchain da swift.org
BrowserSwift Playground online — nessuna installazione
Verifica installazione e REPL
# Verifica la versione installata
swift --version

# Avvia il REPL interattivo
swift

# Esegui un file .swift
swift main.swift
Il tuo primo programma Swift
// main.swift
print("Ciao, mondo!")

let nome = "Alice"
print("Benvenuta, \(nome)!")    // interpolazione di stringa

// Niente punto e virgola necessario a fine riga
💡 In Swift il punto e virgola a fine riga è opzionale. L'interpolazione con \(...) inserisce valori dentro le stringhe.

Sintassi base

Struttura e output
// Commento su una riga
/* Commento
   su più righe */

// Output con print
print("Riga 1")
print("Riga 2")

// print accetta più argomenti
print("a", "b", "c")              // a b c
print("x", "y", separator: "-")   // x-y
print("senza a capo", terminator: "")

// Interpolazione di stringhe
let eta = 25
let pi = 3.14159
print("Età: \(eta), Pi: \(pi)")
print("Somma: \(2 + 3)")         // espressioni dentro \()
🎯 Swift usa l'inferenza di tipo: spesso non devi specificare il tipo, il compilatore lo deduce dal valore. Ma il linguaggio resta fortemente tipizzato.

Variabili e tipi di dato

Swift distingue tra costanti (let, immutabili) e variabili (var, modificabili). Si preferisce let quando il valore non cambia.

let vs var
// Costante: non può essere riassegnata
let nome = "Alice"
// nome = "Bob"   // ERRORE!

// Variabile: modificabile
var eta = 25
eta = 26          // OK

// Tipo esplicito (annotazione)
let pi: Double = 3.14159
var contatore: Int = 0
let attivo: Bool = true
let lettera: Character = "A"
TipoDescrizioneEsempio
IntNumeri interi42, -7
Double / FloatVirgola mobile3.14
BoolBooleanotrue, false
StringTesto"ciao"
CharacterSingolo carattere"A"
Type casting (conversione)
let intero = 10
let decimale = 3.5

// Conversione esplicita obbligatoria
let somma = Double(intero) + decimale   // 13.5
let troncato = Int(decimale)            // 3

// Stringa ↔ numero
let testo = "42"
let numero = Int(testo)        // Int? (Optional) → 42
let str = String(intero)       // "10"

print(somma, troncato, str)
⚠️ Swift non converte automaticamente tra tipi numerici. Devi convertire esplicitamente: Double(intero). Questo previene errori subdoli.

Operatori

Aritmetici, confronto, logici
let a = 10
let b = 3

// Aritmetici
print(a + b)    // 13
print(a - b)    // 7
print(a * b)    // 30
print(a / b)    // 3  (divisione intera)
print(a % b)    // 1  (resto)

// Confronto (restituiscono Bool)
print(a > b)    // true
print(a == b)   // false
print(a != b)   // true

// Logici
let x = true, y = false
print(x && y)   // false (AND)
print(x || y)   // true  (OR)
print(!x)       // false (NOT)

// Operatori composti
var c = 5
c += 3          // c = 8
c *= 2          // c = 16
Operatori di range
// Range chiuso: include entrambi gli estremi
for i in 1...5 {
    print(i)          // 1 2 3 4 5
}

// Range semi-aperto: esclude l'estremo destro
for i in 0..<3 {
    print(i)          // 0 1 2
}

// Operatore ternario
let eta = 20
let stato = eta >= 18 ? "maggiorenne" : "minorenne"
print(stato)          // maggiorenne

// Nil-coalescing: valore di default se nil
let nome: String? = nil
print(nome ?? "Anonimo")   // Anonimo

Stringhe

Operazioni sulle stringhe
var saluto = "Ciao"
saluto += ", mondo!"          // concatenazione
print(saluto)                 // Ciao, mondo!

let nome = "Swift"
print("Linguaggio: \(nome)")  // interpolazione

// Proprietà e metodi
print(saluto.count)                  // numero caratteri
print(saluto.uppercased())           // CIAO, MONDO!
print(saluto.lowercased())
print(saluto.hasPrefix("Ciao"))      // true
print(saluto.hasSuffix("!"))         // true
print(saluto.contains("mondo"))      // true
print(saluto.isEmpty)                // false

// Sostituzione
let nuovo = saluto.replacingOccurrences(
    of: "mondo", with: "Swift")
print(nuovo)
Stringhe multilinea e iterazione
// Stringa multilinea con \"\"\"
let poesia = """
    Riga uno
    Riga due
    Riga tre
    """
print(poesia)

// Iterare sui caratteri
for carattere in "Swift" {
    print(carattere, terminator: " ")  // S w i f t
}
print()

// Dividere una stringa
let csv = "mela,pera,kiwi"
let frutti = csv.split(separator: ",")
print(frutti)   // ["mela", "pera", "kiwi"]

Array, Set e Dizionari

Swift offre tre tipi di collezione principali: Array (ordinato), Set (unico, non ordinato) e Dictionary (coppie chiave-valore).

Array
// Array tipizzato
var numeri = [1, 2, 3, 4, 5]
var nomi: [String] = ["Alice", "Bob"]

// Accesso e modifica
print(numeri[0])           // 1
numeri.append(6)           // aggiungi in fondo
numeri.insert(0, at: 0)    // inserisci all'inizio
numeri.remove(at: 1)       // rimuovi per indice

print(numeri.count)        // numero elementi
print(numeri.isEmpty)      // false
print(numeri.first ?? 0)   // primo (Optional)
print(numeri.contains(3))  // true

// Iterazione
for n in numeri {
    print(n, terminator: " ")
}
Set e Dictionary
// Set: valori unici, nessun duplicato
var colori: Set<String> = ["rosso", "verde", "blu"]
colori.insert("rosso")     // ignorato (già presente)
print(colori.count)        // 3
print(colori.contains("verde"))  // true

// Operazioni insiemistiche
let a: Set = [1, 2, 3]
let b: Set = [2, 3, 4]
print(a.union(b))          // [1, 2, 3, 4]
print(a.intersection(b))   // [2, 3]

// Dictionary: coppie chiave-valore
var voti = ["Alice": 9, "Luca": 7]
voti["Marco"] = 8          // aggiungi
print(voti["Alice"] ?? 0)  // 9 (Optional)
voti.removeValue(forKey: "Luca")

for (nome, voto) in voti {
    print("\(nome): \(voto)")
}

Controllo di flusso: if e switch

if / else if / else
let temperatura = 22

if temperatura < 0 {
    print("Gelido")
} else if temperatura < 15 {
    print("Freddo")
} else if temperatura < 25 {
    print("Mite")           // stampato
} else {
    print("Caldo")
}

// if let: unwrap sicuro di un Optional
let input: String? = "42"
if let valore = input {
    print("Valore: \(valore)")
} else {
    print("Nessun valore")
}
switch — potente e flessibile
let voto = 8

switch voto {
case 10:
    print("Eccellente")
case 8, 9:                      // più valori
    print("Ottimo")            // stampato
case 6...7:                     // range
    print("Sufficiente")
case 1..<6:
    print("Insufficiente")
default:
    print("Voto non valido")
}

// switch con binding e where
let punto = (2, 2)
switch punto {
case let (x, y) where x == y:
    print("Sulla diagonale")   // stampato
case (let x, 0):
    print("Asse X a \(x)")
default:
    print("Altrove")
}
💡 A differenza di altri linguaggi, lo switch di Swift non ha bisogno di break (niente fall-through automatico) e deve essere esaustivo: copri tutti i casi o usa default.

Cicli: for e while

for-in
// Su un range
for i in 1...3 {
    print("Iterazione \(i)")
}

// Su un array
let frutti = ["mela", "pera", "kiwi"]
for frutto in frutti {
    print(frutto)
}

// Con indice (enumerated)
for (i, frutto) in frutti.enumerated() {
    print("\(i): \(frutto)")
}

// Su un dizionario
let voti = ["Alice": 9, "Luca": 7]
for (nome, voto) in voti {
    print("\(nome) = \(voto)")
}

// Ignorare la variabile con _
for _ in 1...3 {
    print("ripeti")
}
while, repeat-while, break/continue
// while: controlla prima di eseguire
var contatore = 3
while contatore > 0 {
    print(contatore)
    contatore -= 1
}

// repeat-while: esegue almeno una volta
var n = 0
repeat {
    print("almeno una volta")
    n += 1
} while n < 1

// break e continue
for i in 1...10 {
    if i == 3 { continue }   // salta il 3
    if i == 6 { break }      // esci al 6
    print(i)                 // 1 2 4 5
}

Funzioni

Le funzioni in Swift si dichiarano con func. I parametri hanno etichette che rendono le chiamate molto leggibili.

Definizione e chiamata
// Funzione semplice
func saluta(nome: String) -> String {
    return "Ciao, \(nome)!"
}
print(saluta(nome: "Alice"))   // l'etichetta è obbligatoria

// Più parametri
func somma(_ a: Int, _ b: Int) -> Int {
    return a + b
}
print(somma(3, 7))   // _ rende l'etichetta non necessaria

// Etichetta esterna diversa da interna
func muovi(da partenza: String, a destinazione: String) {
    print("Da \(partenza) a \(destinazione)")
}
muovi(da: "Roma", a: "Milano")

// Valore di default
func potenza(base: Int, esp: Int = 2) -> Int {
    var risultato = 1
    for _ in 0..<esp { risultato *= base }
    return risultato
}
print(potenza(base: 3))        // 9 (esp default = 2)
print(potenza(base: 2, esp: 4)) // 16
Ritorno multiplo e parametri variadici
// Restituire una tupla
func minMax(_ numeri: [Int]) -> (min: Int, max: Int) {
    var minimo = numeri[0], massimo = numeri[0]
    for n in numeri {
        if n < minimo { minimo = n }
        if n > massimo { massimo = n }
    }
    return (minimo, massimo)
}
let r = minMax([4, 1, 9, 2])
print("min: \(r.min), max: \(r.max)")   // min: 1, max: 9

// Parametri variadici (numero variabile)
func media(_ numeri: Double...) -> Double {
    let totale = numeri.reduce(0, +)
    return totale / Double(numeri.count)
}
print(media(1, 2, 3, 4, 5))   // 3.0

Optional — il cuore della sicurezza di Swift

Un Optional è un tipo che può contenere un valore oppure nil (assenza di valore). Si dichiara con ?. È il modo di Swift per gestire in sicurezza i valori mancanti, eliminando i crash da null pointer.

Dichiarare e srotolare Optional
// Optional: può essere un valore o nil
var nome: String? = "Alice"
nome = nil                      // permesso

// Unwrap NON sicuro (crash se nil!)
// print(nome!)                 // pericoloso

// if let: unwrap sicuro
if let n = nome {
    print("Nome: \(n)")
} else {
    print("Nessun nome")
}

// guard let: esci presto se nil
func saluta(_ nome: String?) {
    guard let n = nome else {
        print("Nome mancante")
        return
    }
    print("Ciao, \(n)!")
}
saluta("Bob")    // Ciao, Bob!
saluta(nil)      // Nome mancante
Nil-coalescing e optional chaining
// Nil-coalescing: valore di default
let input: String? = nil
let nome = input ?? "Anonimo"
print(nome)                   // Anonimo

// Optional chaining: accedi in sicurezza
struct Indirizzo { var citta: String }
struct Persona { var indirizzo: Indirizzo? }

let p = Persona(indirizzo: Indirizzo(citta: "Roma"))
let citta = p.indirizzo?.citta   // String? (nil se manca)
print(citta ?? "sconosciuta")    // Roma

let p2 = Persona(indirizzo: nil)
print(p2.indirizzo?.citta ?? "sconosciuta")  // sconosciuta
🎯 Gli Optional rendono esplicito nel codice quali valori possono mancare. Il compilatore ti obbliga a gestire il caso nil, prevenendo intere categorie di crash.

Closures

Le closures sono blocchi di codice autonomi che puoi passare e usare nel tuo codice, simili alle funzioni anonime (lambda) di altri linguaggi.

Sintassi delle closures
// Closure assegnata a una variabile
let saluta = { (nome: String) -> String in
    return "Ciao, \(nome)!"
}
print(saluta("Alice"))

// Closure come argomento (ordinamento)
let numeri = [5, 2, 8, 1, 9]
let ordinati = numeri.sorted(by: { (a, b) in a < b })
print(ordinati)   // [1, 2, 5, 8, 9]

// Sintassi abbreviata con $0, $1
let decrescente = numeri.sorted(by: { $0 > $1 })
print(decrescente)   // [9, 8, 5, 2, 1]
map, filter, reduce
let numeri = [1, 2, 3, 4, 5, 6]

// map: trasforma ogni elemento
let quadrati = numeri.map { $0 * $0 }
print(quadrati)        // [1, 4, 9, 16, 25, 36]

// filter: tiene gli elementi che soddisfano la condizione
let pari = numeri.filter { $0 % 2 == 0 }
print(pari)            // [2, 4, 6]

// reduce: combina in un singolo valore
let somma = numeri.reduce(0) { $0 + $1 }
print(somma)           // 21

// Concatenazione (functional style)
let risultato = numeri
    .filter { $0 % 2 == 0 }
    .map { $0 * 10 }
    .reduce(0, +)
print(risultato)       // 120

Enum

Gli enum definiscono un tipo con un insieme finito di valori possibili. In Swift possono avere valori associati e metodi.

Enum di base e con valori grezzi
// Enum semplice
enum Direzione {
    case nord, sud, est, ovest
}
let d = Direzione.nord
switch d {
case .nord: print("Vai a nord")
case .sud:  print("Vai a sud")
default:    print("Altra direzione")
}

// Enum con valori grezzi (raw values)
enum Pianeta: Int {
    case mercurio = 1, venere, terra, marte
}
print(Pianeta.terra.rawValue)        // 3
let p = Pianeta(rawValue: 4)         // Optional → marte
print(p ?? .terra)
Enum con valori associati
// Ogni caso può trasportare dati diversi
enum Risultato {
    case successo(String)
    case errore(codice: Int, messaggio: String)
}

func elabora(_ r: Risultato) {
    switch r {
    case .successo(let msg):
        print("OK: \(msg)")
    case .errore(let codice, let messaggio):
        print("Errore \(codice): \(messaggio)")
    }
}

elabora(.successo("Dati salvati"))
elabora(.errore(codice: 404, messaggio: "Non trovato"))

Struct e Classi

Swift offre due modi per creare tipi custom: struct (value type, copiato) e class (reference type, condiviso per riferimento). In Swift si preferiscono spesso le struct.

Struct (value type)
struct Punto {
    var x: Double
    var y: Double

    // Metodo
    func distanzaDallOrigine() -> Double {
        return (x * x + y * y).squareRoot()
    }
}

var p1 = Punto(x: 3, y: 4)
print(p1.distanzaDallOrigine())   // 5.0

// Le struct sono COPIATE all'assegnazione
var p2 = p1
p2.x = 100
print(p1.x)   // 3  (p1 non è cambiata!)
print(p2.x)   // 100
Class (reference type) ed ereditarietà
class Animale {
    var nome: String
    init(nome: String) {        // inizializzatore
        self.nome = nome
    }
    func verso() -> String {
        return "..."
    }
}

// Ereditarietà
class Cane: Animale {
    override func verso() -> String {
        return "Bau!"
    }
}

let c = Cane(nome: "Rex")
print("\(c.nome): \(c.verso())")   // Rex: Bau!

// Le classi sono CONDIVISE per riferimento
let a1 = Animale(nome: "Gatto")
let a2 = a1
a2.nome = "Micio"
print(a1.nome)   // Micio (stesso oggetto!)
StructClass
SemanticaValue (copia)Reference (condivisa)
EreditarietàNo
Inizializzatore automaticoSì (memberwise)No

Proprietà e metodi

Computed property e property observer
struct Cerchio {
    var raggio: Double

    // Computed property: calcolata, non memorizzata
    var area: Double {
        return Double.pi * raggio * raggio
    }

    var diametro: Double {
        get { raggio * 2 }
        set { raggio = newValue / 2 }
    }
}

var c = Cerchio(raggio: 5)
print(c.area)        // 78.53...
c.diametro = 20
print(c.raggio)      // 10
Metodi mutating e static
struct Contatore {
    var valore = 0

    // mutating: può modificare le proprietà di una struct
    mutating func incrementa() {
        valore += 1
    }

    // Metodo static (sul tipo, non sull'istanza)
    static func crea() -> Contatore {
        return Contatore(valore: 0)
    }
}

var cont = Contatore.crea()
cont.incrementa()
cont.incrementa()
print(cont.valore)   // 2

Protocolli

Un protocollo definisce un insieme di metodi e proprietà che un tipo deve implementare — simile alle interfacce di Java. È alla base della "Protocol-Oriented Programming", lo stile preferito in Swift.

Definire e conformarsi a un protocollo
protocol Descrivibile {
    var descrizione: String { get }
    func saluta() -> String
}

// Struct che si conforma al protocollo
struct Persona: Descrivibile {
    var nome: String
    var descrizione: String {
        return "Persona di nome \(nome)"
    }
    func saluta() -> String {
        return "Ciao, sono \(nome)"
    }
}

let p = Persona(nome: "Alice")
print(p.descrizione)   // Persona di nome Alice
print(p.saluta())      // Ciao, sono Alice

// Polimorfismo tramite protocollo
let elementi: [Descrivibile] = [
    Persona(nome: "Bob"),
    Persona(nome: "Carol")
]
for e in elementi {
    print(e.descrizione)
}
💡 Protocolli comuni della libreria standard: Equatable (confronto con ==), Comparable (ordinamento), Codable (serializzazione JSON). Conformarsi a questi sblocca funzionalità potenti.

Gestione degli errori

Swift gestisce gli errori in modo esplicito con throw, try e catch. Le funzioni che possono fallire si marcano con throws.

throw, try, catch
// Definisci gli errori con un enum
enum ErroreBanca: Error {
    case saldoInsufficiente(richiesto: Double)
    case importoNegativo
}

func preleva(saldo: Double, importo: Double) throws -> Double {
    if importo < 0 {
        throw ErroreBanca.importoNegativo
    }
    if importo > saldo {
        throw ErroreBanca.saldoInsufficiente(richiesto: importo)
    }
    return saldo - importo
}

// Gestione con do-catch
do {
    let nuovo = try preleva(saldo: 100, importo: 150)
    print("Nuovo saldo: \(nuovo)")
} catch ErroreBanca.saldoInsufficiente(let richiesto) {
    print("Saldo insufficiente per \(richiesto)€")
} catch {
    print("Errore: \(error)")
}
try? e try!
enum ErroreParsing: Error { case nonValido }

func parseNumero(_ s: String) throws -> Int {
    guard let n = Int(s) else { throw ErroreParsing.nonValido }
    return n
}

// try? → restituisce un Optional (nil se errore)
let a = try? parseNumero("42")    // Optional(42)
let b = try? parseNumero("abc")   // nil
print(a ?? -1)   // 42
print(b ?? -1)   // -1

// try! → crash se fallisce (usare solo se sicuri)
let c = try! parseNumero("100")
print(c)         // 100

Esercizi pratici

🏋️ Esercizio 1 — FizzBuzz

Stampa i numeri da 1 a 20. Per i multipli di 3 stampa "Fizz", per i multipli di 5 "Buzz", per i multipli di entrambi "FizzBuzz".

Soluzione Esercizio 1
for i in 1...20 {
    switch (i % 3, i % 5) {
    case (0, 0): print("FizzBuzz")
    case (0, _): print("Fizz")
    case (_, 0): print("Buzz")
    default:     print(i)
    }
}
🏋️ Esercizio 2 — Gestione Optional

Scrivi una funzione che prenda un array di stringhe, le converta in Int ignorando quelle non valide, e restituisca la somma dei numeri validi.

Soluzione Esercizio 2
func sommaValidi(_ stringhe: [String]) -> Int {
    return stringhe
        .compactMap { Int($0) }   // ignora i nil
        .reduce(0, +)
}

let input = ["10", "abc", "20", "x", "5"]
print(sommaValidi(input))   // 35
🏋️ Esercizio 3 — Struct e protocollo

Crea un protocollo Forma con un metodo area(). Implementalo per Cerchio e Rettangolo, poi stampa l'area totale di un array di forme.

Soluzione Esercizio 3
protocol Forma {
    func area() -> Double
}

struct Cerchio: Forma {
    var raggio: Double
    func area() -> Double { Double.pi * raggio * raggio }
}

struct Rettangolo: Forma {
    var base: Double, altezza: Double
    func area() -> Double { base * altezza }
}

let forme: [Forma] = [
    Cerchio(raggio: 2),
    Rettangolo(base: 3, altezza: 4)
]
let totale = forme.reduce(0) { $0 + $1.area() }
print("Area totale: \(totale)")   // ~24.56

E ora?

Complimenti! Hai completato il corso sui fondamenti di Swift. Ora padroneggi la sintassi, i tipi, gli Optional, le closures, struct/classi, protocolli e la gestione degli errori — tutto ciò che serve per iniziare a sviluppare app iOS.

Cosa hai imparato:
  • Variabili (let/var), tipi e type casting esplicito
  • Stringhe, Array, Set e Dictionary
  • if, switch esaustivo, cicli for/while
  • Funzioni con etichette, closures e map/filter/reduce
  • Optional: il sistema di sicurezza di Swift
  • Enum con valori associati, struct vs class, protocolli
  • Gestione degli errori con throw/try/catch

Il passo successivo? Imparare SwiftUI, il framework dichiarativo di Apple per costruire interfacce, e pubblicare la tua prima app sull'App Store con Xcode.

Vai al corso Kotlin → Torna ai Corsi 📅 Prenota una lezione