dicembre 21, 2025
13 min di lettura

Domande colloquio sviluppatore iOS junior: Swift, UIKit e SwiftUI

interview
career-advice
job-search
entry-level
Domande colloquio sviluppatore iOS junior: Swift, UIKit e SwiftUI
Milad Bonakdar

Milad Bonakdar

Autore

Esercitati con domande da colloquio iOS junior su Swift, UIKit, SwiftUI, networking, persistenza e codice asincrono, con risposte pratiche.


Introduzione

In un colloquio per sviluppatore iOS junior, di solito vogliono capire se sai spiegare le basi di Swift, creare UI semplici con UIKit o SwiftUI, recuperare e decodificare dati, scegliere una persistenza adatta ed evitare errori comuni di memoria o threading. Le risposte migliori sono brevi, concrete e collegate a come costruiresti una piccola app.

Usa questa guida per esercitarti sulle domande più probabili per un ruolo iOS entry-level: cosa significa il concetto, quando lo useresti e quale rischio dovrebbe controllare uno sviluppatore junior.


Fondamenti di Swift (6 Domande)

1. Qual è la differenza tra var e let in Swift?

Risposta:

  • var: Dichiara una variabile mutabile. Il suo valore può essere modificato dopo l'inizializzazione.
  • let: Dichiara una costante immutabile. Una volta impostato, il suo valore non può essere modificato.
  • Best Practice: Usa let di default per sicurezza e chiarezza. Usa var solo quando sai che il valore cambierà.
let name = "John"  // Non può essere modificato
var age = 25       // Può essere modificato
age = 26           // Valido
// name = "Jane"   // Errore: Impossibile assegnare un valore

Rarità: Molto Comune Difficoltà: Facile


2. Spiega gli Optional in Swift. Cos'è l'Optional Binding?

Risposta: Gli Optional rappresentano un valore che potrebbe essere nil (assenza di un valore).

  • Dichiarazione: Usa ? dopo il tipo: var name: String?
  • Metodi di Unwrapping:
    • Force Unwrapping: name! (pericoloso, crasha se nil)
    • Optional Binding: Unwrapping sicuro usando if let o guard let
    • Nil Coalescing: name ?? "Default"
    • Optional Chaining: user?.address?.city
var email: String? = "[email protected]"

// Optional Binding
if let unwrappedEmail = email {
    print("Email: \(unwrappedEmail)")
} else {
    print("Nessuna email")
}

// Guard statement
guard let email = email else { return }
print(email)

Rarità: Molto Comune Difficoltà: Facile


3. Qual è la differenza tra una class e una struct in Swift?

Risposta:

  • Class: Tipo di riferimento (memorizzato nell'heap). Supporta l'ereditarietà. Passato per riferimento.
  • Struct: Tipo di valore (memorizzato nello stack). Nessuna ereditarietà. Passato per copia.
  • Quando usare:
    • Struct: Per modelli di dati semplici, quando si desidera una semantica di valore (copie indipendenti)
    • Class: Quando è necessaria l'ereditarietà, la semantica di riferimento o i deinitializer
struct Point {
    var x: Int
    var y: Int
}

class Person {
    var name: String
    init(name: String) { self.name = name }
}

var p1 = Point(x: 0, y: 0)
var p2 = p1  // Copia creata
p2.x = 10    // p1.x è ancora 0

var person1 = Person(name: "John")
var person2 = person1  // Stesso riferimento
person2.name = "Jane"  // person1.name è anche "Jane"

Rarità: Molto Comune Difficoltà: Media


4. Cosa sono le Closure in Swift?

Risposta: Le Closure sono blocchi di funzionalità autonomi che possono essere passati e utilizzati nel tuo codice. Sono simili alle lambda o alle funzioni anonime in altri linguaggi.

  • Sintassi: { (parametri) -> ReturnType in statements }
  • Cattura di Valori: Le Closure possono catturare e memorizzare riferimenti a variabili dal loro contesto circostante.
  • Uso Comune: Completion handler, callback, operazioni su array.
// Closure semplice
let greet = { (name: String) -> String in
    return "Ciao, \(name)!"
}
print(greet("Alice"))

// Sintassi di trailing closure
let numbers = [1, 2, 3, 4, 5]
let doubled = numbers.map { $0 * 2 }
print(doubled)  // [2, 4, 6, 8, 10]

Rarità: Molto Comune Difficoltà: Media


5. Spiega la differenza tra riferimenti weak e unowned.

Risposta: Entrambi vengono utilizzati per prevenire cicli di ritenzione (perdite di memoria) nei tipi di riferimento.

  • weak: Riferimento opzionale che diventa automaticamente nil quando l'oggetto a cui si fa riferimento viene deallocato. Usare quando il riferimento potrebbe sopravvivere all'oggetto.
  • unowned: Riferimento non opzionale che presuppone che l'oggetto esisterà sempre. Si arresta in modo anomalo se vi si accede dopo la deallocazione. Usare quando si è certi che il riferimento non sopravviverà all'oggetto.
class Person {
    var name: String
    weak var apartment: Apartment?  // weak per prevenire il ciclo di ritenzione
    init(name: String) { self.name = name }
}

class Apartment {
    var unit: String
    unowned let tenant: Person  // unowned - l'appartamento ha sempre un inquilino
    init(unit: String, tenant: Person) {
        self.unit = unit
        self.tenant = tenant
    }
}

Rarità: Comune Difficoltà: Media


6. Cosa sono i Protocolli in Swift?

Risposta: I Protocolli definiscono un blueprint di metodi, proprietà e altri requisiti adatti a un particolare compito o funzionalità. Classi, struct ed enum possono adottare protocolli.

  • Simile a: Interfacce in altri linguaggi
  • Estensioni di Protocollo: Possono fornire implementazioni predefinite
  • Programmazione Orientata al Protocollo: Paradigma preferito di Swift
protocol Drawable {
    func draw()
}

struct Circle: Drawable {
    var radius: Double
    
    func draw() {
        print("Disegno cerchio con raggio \(radius)")
    }
}

struct Rectangle: Drawable {
    var width: Double
    var height: Double
    
    func draw() {
        print("Disegno rettangolo \(width)x\(height)")
    }
}

Rarità: Molto Comune Difficoltà: Facile


Basi di UIKit (5 Domande)

7. Qual è la differenza tra UIView e UIViewController?

Risposta:

  • UIView: Rappresenta un'area rettangolare sullo schermo. Gestisce il disegno e la gestione degli eventi. Esempi: UILabel, UIButton, UIImageView.
  • UIViewController: Gestisce una gerarchia di viste. Gestisce il ciclo di vita della vista, le interazioni dell'utente e la navigazione. Contiene una vista radice e gestisce le sue sottoviste.
  • Relazione: Un view controller gestisce le viste. Un view controller può avere molte viste.

Rarità: Molto Comune Difficoltà: Facile


8. Spiega i metodi del ciclo di vita di un View Controller.

Risposta: I view controller hanno metodi specifici del ciclo di vita chiamati in ordine:

  1. viewDidLoad(): Chiamato una volta quando la vista viene caricata in memoria. Impostazioni che devono avvenire una sola volta.
  2. viewWillAppear(_:): Chiamato prima che la vista appaia sullo schermo. Può essere chiamato più volte.
  3. viewDidAppear(_:): Chiamato dopo che la vista appare sullo schermo. Avvia le animazioni qui.
  4. viewWillDisappear(_:): Chiamato prima che la vista scompaia.
  5. viewDidDisappear(_:): Chiamato dopo che la vista scompare. Pulisci le risorse.
override func viewDidLoad() {
    super.viewDidLoad()
    // Impostazione una tantum: configura l'interfaccia utente, carica i dati
}

override func viewWillAppear(_ animated: Bool) {
    super.viewWillAppear(animated)
    // Aggiorna i dati, inizia ad ascoltare le notifiche
}

Rarità: Molto Comune Difficoltà: Facile


9. Cos'è Auto Layout e come si usano i vincoli (constraints)?

Risposta: Auto Layout è un sistema di layout basato su vincoli che ti consente di creare interfacce utente adattive che funzionano su diverse dimensioni dello schermo e orientamenti.

  • Vincoli (Constraints): Definiscono le relazioni tra le viste (larghezza, altezza, spaziatura, allineamento)
  • Metodi:
    • Interface Builder: Editor visuale in Xcode
    • Programmatico: API NSLayoutConstraint o NSLayoutAnchor
    • Visual Format Language: Basato su stringhe (meno comune ora)
let button = UIButton()
button.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(button)

NSLayoutConstraint.activate([
    button.centerXAnchor.constraint(equalTo: view.centerXAnchor),
    button.centerYAnchor.constraint(equalTo: view.centerYAnchor),
    button.widthAnchor.constraint(equalToConstant: 200),
    button.heightAnchor.constraint(equalToConstant: 50)
])

Rarità: Molto Comune Difficoltà: Media


10. Qual è la differenza tra frame e bounds?

Risposta:

  • frame: La posizione e le dimensioni della vista nel sistema di coordinate della sua supervista. Usato per posizionare la vista.
  • bounds: La posizione e le dimensioni della vista nel suo proprio sistema di coordinate. L'origine è solitamente (0, 0). Usato per disegnare il contenuto all'interno della vista.
  • Differenza Chiave: frame è relativo al genitore, bounds è relativo a se stesso.
let view = UIView(frame: CGRect(x: 50, y: 100, width: 200, height: 150))
print(view.frame)   // (50, 100, 200, 150) - posizione nella supervista
print(view.bounds)  // (0, 0, 200, 150) - proprio sistema di coordinate

Rarità: Comune Difficoltà: Media


11. Come gestisci l'input utente da un UITextField?

Risposta: Esistono diversi approcci:

  • Target-Action: Aggiungi target per l'evento .editingChanged
  • Delegate: Conformarsi al protocollo UITextFieldDelegate
  • Combine: Usa i publisher (approccio moderno)
class ViewController: UIViewController, UITextFieldDelegate {
    let textField = UITextField()
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        // Approccio Delegate
        textField.delegate = self
        
        // Approccio Target-Action
        textField.addTarget(self, action: #selector(textChanged), for: .editingChanged)
    }
    
    @objc func textChanged(_ textField: UITextField) {
        print("Testo: \(textField.text ?? "")")
    }
    
    // Metodo Delegate
    func textFieldShouldReturn(_ textField: UITextField) -> Bool {
        textField.resignFirstResponder()
        return true
    }
}

Rarità: Molto Comune Difficoltà: Facile


Basi di SwiftUI (4 Domande)

12. Che cos’è SwiftUI e in cosa è diverso da UIKit?

Risposta: SwiftUI è il framework dichiarativo di Apple per creare interfacce. Descrivi la UI come funzione dello stato, e SwiftUI aggiorna la vista quando quello stato cambia. UIKit è imperativo: crei e modifichi direttamente oggetti come UIView, UILabel e UIViewController.

In un colloquio junior, non presentarlo come se SwiftUI sostituisse UIKit ovunque. Molte app in produzione usano entrambi. Una buona risposta: SwiftUI è conciso per nuove schermate e UI basate sullo stato; UIKit resta importante per codebase esistenti, controlli personalizzati e API non ancora coperte completamente da SwiftUI.

struct CounterView: View {
    @State private var count = 0

    var body: some View {
        VStack {
            Text("Count: \(count)")
            Button("Increment") {
                count += 1
            }
        }
    }
}

Rarità: Molto Comune Difficoltà: Facile


13. Spiega @State, @Binding e @ObservedObject in SwiftUI.

Risposta: Questi wrapper indicano dove vive lo stato e chi può modificarlo.

  • @State: Stato privato posseduto da una vista, di solito per stato locale della UI.
  • @Binding: Connessione bidirezionale verso stato posseduto da una vista padre. La vista figlia può leggerlo e modificarlo senza possederlo.
  • @ObservedObject: Riferimento a dati osservabili esterni. Si usa quando un altro oggetto possiede i dati e la vista deve aggiornarsi quando cambiano. Nel codice SwiftUI più recente puoi vedere anche il framework Observation e @Observable, anche se molti colloqui chiedono ancora questi wrapper.
class UserData: ObservableObject {
    @Published var name = ""
}

struct ParentView: View {
    @State private var isOn = false
    @ObservedObject var userData = UserData()

    var body: some View {
        VStack {
            ChildView(isOn: $isOn)
            Text(userData.name)
        }
    }
}

struct ChildView: View {
    @Binding var isOn: Bool

    var body: some View {
        Toggle("Switch", isOn: $isOn)
    }
}

Rarità: Comune Difficoltà: Media


14. Come crei una lista in SwiftUI?

Risposta: Usa la vista List per visualizzare raccolte scorrevoli di dati.

struct ContentView: View {
    let fruits = ["Apple", "Banana", "Orange", "Grape"]
    
    var body: some View {
        List(fruits, id: \.self) { fruit in
            Text(fruit)
        }
    }
}

// Con modello personalizzato
struct Task: Identifiable {
    let id = UUID()
    let title: String
}

struct TaskListView: View {
    let tasks = [
        Task(title: "Buy groceries"),
        Task(title: "Walk the dog")
    ]
    
    var body: some View {
        List(tasks) { task in
            Text(task.title)
        }
    }
}

Rarità: Comune Difficoltà: Facile


15. Cosa sono i View Modifiers in SwiftUI?

Risposta: I modificatori di vista sono metodi che creano una nuova vista con proprietà modificate. Sono concatenabili.

  • Modificatori Comuni: .padding(), .background(), .foregroundColor(), .font(), .frame()
  • L'Ordine Conta: I modificatori vengono applicati in sequenza
Text("Hello, World!")
    .font(.title)
    .foregroundColor(.blue)
    .padding()
    .background(Color.yellow)
    .cornerRadius(10)
    .shadow(radius: 5)

// Modificatore personalizzato
struct PrimaryButtonStyle: ViewModifier {
    func body(content: Content) -> some View {
        content
            .padding()
            .background(Color.blue)
            .foregroundColor(.white)
            .cornerRadius(8)
    }
}

extension View {
    func primaryButton() -> some View {
        modifier(PrimaryButtonStyle())
    }
}

Rarità: Comune Difficoltà: Facile


Dati e Networking (5 Domande)

16. Come esegui una richiesta di rete in iOS?

Risposta: Usa URLSession. In Swift moderno, una risposta junior può mostrare async/await e ricordare che codebase più vecchie usano ancora completion handler. Una risposta più solida valida anche la risposta HTTP, gestisce gli errori, decodifica modelli Codable e aggiorna la UI sul main actor.

struct User: Codable {
    let id: Int
    let name: String
}

func fetchUser(id: Int) async throws -> User {
    let url = URL(string: "https://api.example.com/users/\(id)")!
    let (data, response) = try await URLSession.shared.data(from: url)

    guard let http = response as? HTTPURLResponse, (200...299).contains(http.statusCode) else {
        throw URLError(.badServerResponse)
    }

    return try JSONDecoder().decode(User.self, from: data)
}

Rarità: Molto Comune Difficoltà: Media


17. Cos'è Codable in Swift?

Risposta: Codable è un alias di tipo per i protocolli Encodable & Decodable. Consente una facile conversione tra tipi Swift e rappresentazioni esterne come JSON.

  • Sintesi Automatica: Swift genera automaticamente il codice di codifica/decodifica se tutte le proprietà sono Codable.
  • Chiavi Personalizzate: Usa l'enum CodingKeys per mappare nomi di proprietà diversi.
struct User: Codable {
    let id: Int
    let name: String
    let email: String
}

// Decodifica JSON
let json = """
{
    "id": 1,
    "name": "John Doe",
    "email": "[email protected]"
}
""".data(using: .utf8)!

let decoder = JSONDecoder()
let user = try decoder.decode(User.self, from: json)

// Codifica in JSON
let encoder = JSONEncoder()
encoder.outputFormatting = .prettyPrinted
let jsonData = try encoder.encode(user)

Rarità: Molto Comune Difficoltà: Facile


18. Come rendi persistenti i dati localmente in iOS?

Risposta: Molteplici opzioni per la persistenza dei dati locale:

  • UserDefaults: Semplice storage chiave-valore per piccole quantità di dati (impostazioni, preferenze)
  • File System: Salva i file nella directory Documents o Cache
  • Core Data: Framework di Apple per la gestione e la persistenza del grafo di oggetti
  • SQLite: Database relazionale
  • Keychain: Storage sicuro per dati sensibili (password, token)
// UserDefaults
UserDefaults.standard.set("John", forKey: "username")
let username = UserDefaults.standard.string(forKey: "username")

// File System
let documentsPath = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)[0]
let filePath = documentsPath.appendingPathComponent("data.json")

// Scrivi
try data.write(to: filePath)

// Leggi
let loadedData = try Data(contentsOf: filePath)

Rarità: Molto Comune Difficoltà: Facile


19. Qual è la differenza tra operazioni sincrone e asincrone?

Risposta: Un’operazione sincrona blocca il thread corrente finché non termina. Sul main thread è pericolosa perché l’app può smettere di rispondere. Un’operazione asincrona avvia il lavoro e lascia continuare il thread; il risultato arriva dopo tramite async/await, un completion handler, un delegate o un publisher.

Nei colloqui iOS, collega la risposta alla reattività della UI: chiamate di rete, lavoro su file e calcoli pesanti non devono bloccare il main thread, e le modifiche alla UI devono essere eseguite sul main actor.

func loadProfile() async {
    do {
        let user = try await fetchUser(id: 42)
        await MainActor.run {
            self.nameLabel.text = user.name
        }
    } catch {
        await MainActor.run {
            self.errorLabel.text = "Could not load profile"
        }
    }
}

Rarità: Comune Difficoltà: Facile


20. Che cos’è Grand Central Dispatch (GCD)?

Risposta: GCD è l’API di basso livello di Apple per schedulare lavoro su dispatch queue. La main queue si usa per la UI, mentre le global queue possono eseguire lavoro in background con diverse priorità di quality of service.

In Swift moderno, spesso userai prima Swift concurrency (async/await, Task e actors), perché rende il codice asincrono più leggibile. GCD resta importante perché molti progetti UIKit, codebase più vecchie e API con callback usano DispatchQueue.main.async o code in background.

DispatchQueue.global(qos: .userInitiated).async {
    let image = renderThumbnail()

    DispatchQueue.main.async {
        self.imageView.image = image
    }
}

Rarità: Comune Difficoltà: Media


Newsletter subscription

Consigli di carriera settimanali che funzionano davvero

Ricevi le ultime idee direttamente nella tua casella di posta

Distinguiti dai Reclutatori e Ottieni il Lavoro dei Tuoi Sogni

Unisciti a migliaia di persone che hanno trasformato la loro carriera con curriculum potenziati dall'IA che superano l'ATS e impressionano i responsabili delle assunzioni.

Inizia a creare ora

Condividi questo post

Riduci il Tempo di Scrittura del Curriculum del 90%

La persona in cerca di lavoro media impiega più di 3 ore per formattare un curriculum. La nostra IA lo fa in meno di 15 minuti, portandoti alla fase di candidatura 12 volte più velocemente.