La Console in Xcode è uno strumento di debug per lo sviluppo iOS che visualizza l'output di NSLog, print, os_log e i log di crash dell'app in tempo reale. Secondo Apple Unified Logging, a partire da iOS 10 Apple raccomanda di usare os_log invece di NSLog per la raccolta centralizzata dei messaggi tramite Unified Logging System. La Console combina l'output del debugger e i messaggi di sistema in un'unica finestra di Debug Area, accessibile in qualsiasi momento dello sviluppo.
Punti chiave
La Console fa parte del Debug Area in Xcode, situata nel pannello inferiore dell'editor (View → Debug Area → Activate Console, scorciatoia Cmd + Shift + Y). La Console mostra tutto l'output di testo dell'applicazione in esecuzione: messaggi da NSLog, os_log, print, avvisi di runtime e dump automatici di eccezioni quando l'app si blocca.
La Console funziona sia nel simulatore che su un dispositivo fisico. Nel simulatore, i messaggi arrivano istantaneamente attraverso un pipe locale; su un dispositivo, arrivano tramite connessione USB con un ritardo di 1–3 frame. Per le app di produzione, la Console sul dispositivo non è disponibile — gli sviluppatori si affidano a Crashlytics o Unified Logging con raccolta remota tramite log collect.
A differenza dell'app di sistema Console.app su Mac, la finestra Console in Xcode mostra solo i log dell'applicazione corrente in esecuzione (con capacità di filtraggio). Console.app raccoglie i log di tutti i processi su Mac, inclusi i simulatori iOS. Tuttavia, per eseguire il debug di app iOS, gli sviluppatori usano la Console integrata di Xcode grazie alla sua integrazione con il debugger LLDB.
Tre API principali sono disponibili per lo sviluppatore iOS per l'output nella Console: NSLog (obsoleto), os_log (raccomandato) e print (solo Swift). Ognuna ha le proprie caratteristiche in termini di prestazioni, formattazione e compatibilità con Unified Logging System.
NSLog è una funzione di Foundation, disponibile in Objective-C e Swift. NSLog visualizza un messaggio con timestamp, nome del processo e PID. Svantaggi: NSLog scrive nel buffer di sistema in modo sincrono, bloccando il thread corrente durante la scrittura. Con chiamate frequenti (ad esempio, in un ciclo), NSLog crea un ritardo notevole. Apple non raccomanda NSLog per nuovi progetti, ma rimane compatibile con codice legacy e librerie di terze parti.
os_log è un'API di os.framework, introdotta in iOS 10. os_log è asincrono: il messaggio viene accodato e scritto nel buffer senza bloccare il thread chiamante. Secondo la WWDC 2016, os_log è 50 volte più veloce di NSLog in scenari di carico elevato. os_log supporta anche il controllo dinamico: i messaggi di livello DEBUG vengono raccolti solo nei build Debug, e in Release vengono ignorati senza overhead.
print() è il metodo di output più semplice in Swift. print scrive in stdout (output standard), che Xcode reindirizza alla Console. print non aggiunge metadati (ora, livello), ma supporta il buffering di stdout. Per il debug rapido, print è uno strumento comodo, ma per la registrazione permanente è inferiore a os_log in funzionalità e controllo.
import os.log
// NSLog — obsoleto, bloccante
NSLog("Application started")
// os_log — raccomandato, asincrono
let log = OSLog(
subsystem: "com.myapp",
category: "lifecycle"
)
os_log("Application started", log: log)
// print — output Swift veloce
print("Application started")
Unified Logging System (ULS) è l'infrastruttura di logging completa di Apple, introdotta in iOS 10 e macOS Sierra. ULS raccoglie i messaggi da tutti i processi di sistema in un unico archivio con capacità di accesso remoto tramite lo strumento da riga di comando log su Mac. Gli sviluppatori usano os_log per scrivere in ULS e la Console per leggere.
Ogni OSLog è identificato da una coppia di subsystem (ad esempio, com.myapp.network) e category (ad esempio, http, websocket). Il sottosistema è il dominio dell'applicazione (un'app può avere più sottosistemi per diversi moduli). La categoria è un componente all'interno del sottosistema. La combinazione subsystem + category consente un filtraggio flessibile dei log nella Console e in log collect.
| Livello | OSLogType | Visualizzazione Console | Raccolta in Release |
|---|---|---|---|
| Default | .default | Sempre | Sì |
| Info | .info | Con interfaccia os_log attivata | Sì |
| Debug | .debug | Solo build Debug | No |
| Error | .error | Sempre con etichetta rossa | Sì |
| Fault | .fault | Sempre con etichetta viola | Sì |
Il comando log collect su Mac raccoglie i log archiviati da un dispositivo iOS collegato in un file .logarchive. Questo file può essere aperto in Console.app su Mac per un'analisi dettagliata, inclusi messaggi os_log, crash log e diagnostica di sistema. Per abilitare la raccolta sul dispositivo, è necessario attivare la Modalità Sviluppatore e collegare il dispositivo tramite USB.
Il lavoro pratico con la Console include tre scenari principali: logging attivo durante lo sviluppo, analisi dei crash log dopo un blocco e diagnostica remota tramite .logarchive. Ogni scenario ha un set ottimale di strumenti e impostazioni.
Si raccomanda di creare un OSLog separato per ogni modulo dell'applicazione con livelli: debug (debug dettagliato), info (transizioni di stato chiave), error (eccezioni e guasti). Nella Console di Xcode, attivare il filtro per sottosistema dell'applicazione per escludere i messaggi di sistema che creano rumore e distraggono dalla logica dell'app.
Quando l'app si blocca, Xcode ferma automaticamente l'esecuzione e mostra il thread in cui si è verificato il blocco, con un stack trace completo nella Console. La prima riga del crash log contiene il tipo di eccezione (NSException, EXC_BAD_ACCESS) e il motivo. Studiare lo stack trace dal basso verso l'alto: l'ultimo metodo chiamato è la posizione del blocco. Per gli indirizzi crittografati (in Release), è necessaria la symbolication tramite dSYM.
// Esempio di configurazione modulare OSLog
extension OSLog {
static let uiLifecycle = OSLog(
subsystem: "com.myapp.ui",
category: "lifecycle"
)
static let network = OSLog(
subsystem: "com.myapp.network",
category: "http"
)
static let database = OSLog(
subsystem: "com.myapp.data",
category: "core-data"
)
}
// Utilizzo con livelli
os_log("View did load", log: .uiLifecycle, type: .debug)
os_log("HTTP 200 received", log: .network, type: .info)
os_log("Failed to save: \(error.localizedDescription)",
log: .database, type: .error)
La Console di Xcode supporta diverse funzionalità avanzate che vanno oltre il semplice logging. I log dei breakpoint consentono di inviare messaggi alla Console senza fermare l'esecuzione, e i comandi LLDB in Debugger Command offrono il controllo completo sulla formattazione dell'output.
È possibile configurare un breakpoint per visualizzare un messaggio nella Console e continuare automaticamente l'esecuzione. Posizionare un breakpoint sulla riga desiderata, fare clic destro → Edit Breakpoint → aggiungere Debugger Command: “po self” o “expr @import UIKit” + Debugger Command: “po self.view”. Selezionare Automatically continue after evaluating. Dopo l'avvio, il breakpoint visualizzerà il risultato del comando nella Console ogni volta che la riga viene raggiunta, senza interrompere il thread.
La Console di Xcode supporta l'esecuzione di comandi LLDB arbitrari mentre si è fermi su un breakpoint. po (print object) visualizza la descrizione di un oggetto, p (print) visualizza valori primitivi, ed expr esegue espressioni Swift/ObjC. Per output formattato, utilizzare p/CGRectGetWidth. L'output LLDB appare nella Console immediatamente dopo aver raggiunto il breakpoint.
func processUserData(user: User) {
// Breakpoint qui con Debugger Command:
// po "User name: \(user.name)"
// expr user.age = 30
print("Processing user: \(user.name)")
}
// Esempio di logging personalizzato con sequenza
func trackMethodCall(
file: String = #file,
function: String = #function
) {
os_log("[\(function)] called",
log: .uiLifecycle, type: .debug)
}
La Console di Xcode è strettamente integrata con Instruments — lo strumento di profilazione di Xcode. Quando si esegue l'app tramite Product → Profile con il modello Logging, tutti i messaggi os_log vengono registrati nella traccia Instruments con timestamp. Ciò consente di visualizzare simultaneamente log, prestazioni ed eventi di sistema su un'unica timeline, fondamentale per diagnosticare race condition e regressioni delle prestazioni.
Domande frequenti
NSLog è sincrono, blocca il thread e visualizza sempre il messaggio. os_log è asincrono, 50 volte più veloce in scenari di carico elevato, supporta le categorie e disattiva dinamicamente i livelli di debug nei build Release senza perdita di prestazioni.
Verificare il livello di logging: per impostazione predefinita, la Console mostra solo default e superiore. Per vedere info e debug, aprire il menu os_log nella Console di Xcode e selezionare Include Info Messages e Include Debug Messages nelle impostazioni dello schema (Edit Scheme → Run → Arguments → OS_ACTIVITY_MODE = debug).
Selezionare i messaggi desiderati nella Console, copiarli (Cmd + C) e incollarli in un qualsiasi editor di testo. Per un dump completo, utilizzare il comando terminale: sudo log collect --device --output /tmp/app_logs.logarchive — salva tutti i log dal dispositivo iOS in un formato strutturato.
os_log di tipo .default e .error funzionano in Release per impostazione predefinita. Per .info e .debug in Release, è necessario aggiungere l'argomento di avvio -OSLogPreferencesApp “$(PRODUCT_BUNDLE_IDENTIFIER):debug” nello schema Xcode. Senza questo argomento, i messaggi di debug non vengono raccolti in Release, risparmiando risorse del dispositivo.
Aprire Window → Organizer → Crashes in Xcode. L'organizer mostra tutti i crash log raccolti dai dispositivi dei tester, raggruppati per tipo di eccezione. La symbolication richiede un file .dSYM del build in cui si è verificato il blocco — Xcode lo trova automaticamente se un archivio è disponibile.
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