Le Regole dell'Esame di Teoria Patente B nel 2026

L'esame di teoria per la patente B presso la Motorizzazione Civile prevede:

  • 30 domande VERO/FALSO estratte casualmente dal database ministeriale ufficiale.
  • Tempo a disposizione: 20 minuti.
  • Numero massimo di errori consentiti: 3 errori (con 4 o più errori l'esame è respinto).

Sviluppo Tecnico: Come Creare un'App Quiz Patente in SwiftUI

Ecco l'architettura completa per un motore di quiz in SwiftUI con gestione dello stato delle risposte e timer con countdown animato:

import SwiftUI

struct QuizQuestion: Identifiable {
    let id = UUID()
    let text: String
    let imageName: String?
    let isCorrect: Bool
    var selectedAnswer: Bool? = nil
}

@Observable
final class PatenteQuizEngine {
    var questions: [QuizQuestion] = []
    var currentIndex: Int = 0
    var timeRemaining: Int = 1200 // 20 minuti in secondi
    var isExamFinished: Bool = false
    
    var errorsCount: Int {
        questions.filter { q in
            guard let selected = q.selectedAnswer else { return false }
            return selected != q.isCorrect
        }.count
    }
    
    var isPassed: Bool {
        errorsCount <= 3
    }
    
    func answerCurrent(with answer: Bool) {
        guard currentIndex < questions.count else { return }
        questions[currentIndex].selectedAnswer = answer
        if currentIndex < questions.count - 1 {
            currentIndex += 1
        } else {
            finishExam()
        }
    }
    
    func finishExam() {
        isExamFinished = true
    }
}

struct PatenteQuizView: View {
    @State private var engine = PatenteQuizEngine()
    
    var body: some View {
        VStack(spacing: 20) {
            // Header con Timer ed Errori
            HStack {
                Text("Errori: \(engine.errorsCount)/3")
                    .font(.headline)
                    .foregroundStyle(engine.errorsCount > 3 ? .red : .green)
                Spacer()
                Text(timeString(from: engine.timeRemaining))
                    .font(.title3.monospacedDigit())
                    .fontWeight(.bold)
            }
            .padding()
            
            if !engine.isExamFinished && engine.currentIndex < engine.questions.count {
                let q = engine.questions[engine.currentIndex]
                
                VStack(alignment: .leading, spacing: 16) {
                    Text("Domanda \(engine.currentIndex + 1) di \(engine.questions.count)")
                        .font(.caption)
                        .foregroundStyle(.secondary)
                    
                    Text(q.text)
                        .font(.title3)
                        .fontWeight(.semibold)
                        .padding(.vertical)
                    
                    HStack(spacing: 16) {
                        Button(action: { engine.answerCurrent(with: true) }) {
                            Text("VERO")
                                .font(.headline)
                                .frame(maxWidth: .infinity)
                                .padding()
                                .background(Color.green.opacity(0.15))
                                .foregroundStyle(.green)
                                .clipShape(RoundedRectangle(cornerRadius: 12))
                        }
                        
                        Button(action: { engine.answerCurrent(with: false) }) {
                            Text("FALSO")
                                .font(.headline)
                                .frame(maxWidth: .infinity)
                                .padding()
                                .background(Color.red.opacity(0.15))
                                .foregroundStyle(.red)
                                .clipShape(RoundedRectangle(cornerRadius: 12))
                        }
                    }
                }
                .padding()
                .background(Color(.secondarySystemBackground))
                .clipShape(RoundedRectangle(cornerRadius: 16))
            } else {
                VStack(spacing: 16) {
                    Image(systemName: engine.isPassed ? "checkmark.seal.fill" : "xmark.octagon.fill")
                        .font(.system(size: 64))
                        .foregroundStyle(engine.isPassed ? .green : .red)
                    
                    Text(engine.isPassed ? "PROMOSSO! 🎉" : "BOCCIATO 😭")
                        .font(.title)
                        .fontWeight(.bold)
                    
                    Text("Hai commesso \(engine.errorsCount) errori (limite massimo: 3)")
                        .foregroundStyle(.secondary)
                }
                .padding()
            }
            
            Spacer()
        }
        .padding()
    }
    
    private func timeString(from totalSeconds: Int) -> String {
        let min = totalSeconds / 60
        let sec = totalSeconds % 60
        return String(format: "%02d:%02d", min, sec)
    }
}

Vuoi creare la tua App o hai bisogno di consulenza personalizzata?

Sviluppo app iOS native in SwiftUI, piattaforme backend scalabili e offro supporto strategico per monetizzazione e progetti internazionali.

Diallo Yunus

Diallo Yunus

Indie iOS & macOS Developer, Fondatore di DialloDev e autore di 11+ app pubblicate su Apple App Store. Aiuto startup, creator e aziende a costruire prodotti digitali di successo.