“Es geht nicht.”
Wenn du schon mal eine iOS-App supportet hast, kennst du dieses frustrierend vage Nutzerfeedback. Keine Schritte zum Reproduzieren, keine Fehlermeldung, kein Kontext – nur das gefürchtete “geht nicht”, das dich mit mehr Fragen als Antworten zurücklässt.
Selbst die detailorientiertesten Nutzer wissen selten, welche Informationen du zur Diagnose brauchst. Und wenn sie doch versuchen zu helfen, fehlt ihnen oft das technische Wissen, um die richtigen Details zu liefern. Diese Diskrepanz erzeugt eine frustrierende Erfahrung für alle Beteiligten.
In diesem Beitrag stelle ich zwei praktische Ansätze vor, die ich in ErrorKit implementiert habe, um diese Lücke zu schließen: einen einfachen Feedback-Button, der automatisch Diagnose-Logs sammelt, und einen strukturierten Ansatz für Error-Analytics, der dir hilft, Muster zu erkennen – auch ohne direkte Nutzerberichte.
Das Problem des fehlenden Kontexts
Wenn Nutzer auf Probleme stoßen, erschweren mehrere Herausforderungen die Diagnose:
Sie wissen nicht, welche Informationen du brauchst
Sie können nicht einfach auf System-Logs zugreifen
Sie können sich nur schwer an die genauen Schritte erinnern und diese formulieren
Komplexe Probleme können mehrere Komponenten betreffen
Sporadische Probleme sind schwer auf Abruf reproduzierbar
Ohne richtigen Kontext wird Debugging zum Ratespiel. Du verbringst womöglich Stunden mit dem Reproduzieren eines Problems, das mit den richtigen Informationen in Minuten gelöst wäre.
Lösung 1: Feedback-Button mit angehängten Logs
Die erste Lösung macht es Nutzern extrem einfach, dir vollständige Informationen zu schicken. ErrorKit bietet einen SwiftUI-Modifier, der einen Mail-Composer mit automatischer Log-Sammlung hinzufügt:
struct ContentView: View {
@State private var showMailComposer = false
var body: some View {
VStack {
// Dein App-Inhalt
Button("Report a Problem") {
showMailComposer = true
}
.mailComposer(
isPresented: $showMailComposer,
recipient: "[email protected]",
subject: "YourApp Bug Report",
messageBody: """
Please describe what happened:
----------------------------------
Device: \(UIDevice.current.model)
iOS: \(UIDevice.current.systemVersion)
App version: \(Bundle.main.infoDictionary?["CFBundleShortVersionString"] as? String ?? "Unknown")
""",
attachments: [
try? ErrorKit.logAttachment(ofLast: .minutes(30))
]
)
}
}
}Das erstellt einen einfachen “Problem melden”-Button, der:
Einen vorausgefüllten E-Mail-Composer öffnet
Geräte- und App-Informationen enthält
Automatisch aktuelle System-Logs anhängt
Platz für den Nutzer bietet, das Problem zu beschreiben
Der Log-Anhang ist hier das Geheimrezept. Wenn der Nutzer diesen Button nach einem Problem antippt, bekommst du ein umfassendes Bild davon, was in und um deine App passiert ist, als das Problem auftrat.
Apples Unified Logging System nutzen
ErrorKit verwendet Apples Unified Logging System (OSLog/Logger), um Diagnose-Informationen zu sammeln. Falls du noch kein strukturiertes Logging nutzt, hier eine kurze Einführung:
import OSLog
// Logger erstellen
let logger = Logger()
// oder mit Subsystem und Kategorie
let networkLogger = Logger(subsystem: "com.yourapp", category: "networking")
// Auf passenden Ebenen loggen
logger.debug("Detailed connection info") // Entwickler-Debugging
logger.info("User tapped submit button") // Allgemeine Informationen
logger.notice("Profile successfully loaded") // Wichtige Ereignisse
logger.error("Failed to load user data") // Fehler, die behoben werden sollten
logger.fault("Database corruption detected") // Systemausfälle
// Werte formatieren und Datenschutz steuern
logger.info("User \(userId, privacy: .private) logged in from \(ipAddress, privacy: .public)")Das Unified Logging System bietet mehrere Vorteile gegenüber print()-Anweisungen:
Log-Level zum Filtern von Informationen
Datenschutzkontrollen für sensible Daten
Effiziente Performance mit minimalem Overhead
Persistenz über App-Neustarts hinweg
Umfassende Log-Sammlung
Ein zentraler Vorteil von ErrorKits Ansatz ist, dass nicht nur die Logs deiner App erfasst werden, sondern auch relevante Logs von:
Third-Party-Frameworks, die Apples Unified Logging System nutzen
Systemkomponenten, mit denen deine App interagiert (Netzwerk, Dateisystem usw.)
Hintergrundprozessen, die mit der Funktionalität deiner App zusammenhängen
Das gibt dir ein vollständiges Bild davon, was in und um deine App passiert ist, als das Problem auftrat – nicht nur die Logs, die du explizit hinzugefügt hast.
Log-Sammlung steuern
Du kannst die Log-Sammlung anpassen, um Detail und Datenschutz auszubalancieren:
// Logs der letzten 30 Minuten ab Notice-Level sammeln (Standard)
try ErrorKit.logAttachment(ofLast: .minutes(30), minLevel: .notice)
// Logs der letzten Stunde ab Error-Level sammeln (weniger ausführlich)
try ErrorKit.logAttachment(ofLast: .hours(1), minLevel: .error)
// Logs der letzten 5 Minuten ab Debug-Level sammeln (sehr detailliert)
try ErrorKit.logAttachment(ofLast: .minutes(5), minLevel: .debug)Der minLevel-Parameter filtert Logs nach Wichtigkeit:
.debug: Alle Logs (sehr ausführlich).info: Informative Logs und darüber.notice: Bemerkenswerte Ereignisse (Standard).error: Nur Fehler und Ausfälle.fault: Nur kritische Fehler
Das gibt dir Kontrolle darüber, wie viel Information du sammelst, während du trotzdem den nötigen Kontext für die Diagnose erhältst.
Alternative Methoden für mehr Kontrolle
Wenn du mehr Kontrolle über die Log-Verarbeitung brauchst, bietet ErrorKit zusätzliche Ansätze:
Log-Daten direkt abrufen
Um Logs an dein eigenes Backend zu senden oder sie in der App zu verarbeiten, verwende loggedData:
let logData = try ErrorKit.loggedData(
ofLast: .minutes(10),
minLevel: .notice
)
// Die Daten mit deinem eigenen Reporting-System verwenden
analyticsService.sendLogs(data: logData)In eine temporäre Datei exportieren
Um Logs über andere Mechanismen zu teilen, verwende exportLogFile:
let logFileURL = try ErrorKit.exportLogFile(
ofLast: .hours(1),
minLevel: .error
)
// Die Log-Datei teilen
let activityVC = UIActivityViewController(
activityItems: [logFileURL],
applicationActivities: nil
)
present(activityVC, animated: true)Lösung 2: Smarte Error-Analytics mit Gruppierungs-IDs
Während der Feedback-Button Nutzern hilft, bemerkte Probleme zu melden, bleiben viele Probleme ungemeldet. Nutzer stoßen vielleicht auf einen Fehler, zucken die Schultern und versuchen es erneut – ohne dir je davon zu erzählen. Genau hier kommen Error-Analytics ins Spiel.
ErrorKit bietet Tools, um Fehler automatisch zu tracken und intelligent zu gruppieren:
func handleError(_ error: Error) {
// Eine stabile ID abrufen, die dynamische Parameter ignoriert
let groupID = ErrorKit.groupingID(for: error)
// Die vollständige Fehlerketten-Beschreibung abrufen
let errorDetails = ErrorKit.errorChainDescription(for: error)
// An dein Analytics-System senden
Analytics.track(
event: "error_occurred",
properties: [
"error_group": groupID,
"error_details": errorDetails,
"user_id": currentUser.id
]
)
// Dem Nutzer eine passende UI anzeigen
showErrorAlert(message: ErrorKit.userFriendlyMessage(for: error))
}Beispiel einer globalen Fehlerbehandlungsfunktion für deine App.
Die Magie steckt hier in der groupingID(for:)-Funktion. Sie generiert einen stabilen Bezeichner basierend auf der Typstruktur des Fehlers und den Enum-Cases, wobei dynamische Parameter und lokalisierte Meldungen ignoriert werden.
Das bedeutet, dass Fehler mit der gleichen Ursache dieselbe Gruppierungs-ID haben, auch wenn konkrete Details (wie Dateipfade oder Nutzer-IDs) unterschiedlich sind:
// Beide erzeugen dieselbe groupID: "3f9d2a"
ProfileError
└─ DatabaseError
└─ FileError.notFound(path: "/Users/john/data.db")
ProfileError
└─ DatabaseError
└─ FileError.notFound(path: "/Users/jane/backup.db")Dieser Ansatz bietet mehrere Vorteile:
Häufige Probleme identifizieren: Sieh, welche Fehler am häufigsten auftreten
Fixes priorisieren: Konzentriere dich zuerst auf Probleme mit großer Auswirkung
Behebung nachverfolgen: Beobachte, ob Fehlerraten nach Fixes sinken
Neue Probleme erkennen: Identifiziere schnell neue Fehlermuster nach Releases
Mit Nutzersegmenten korrelieren: Sieh, ob bestimmte Fehler spezifische Nutzer betreffen
Beide Ansätze für maximale Einblicke kombinieren
Ein wirkungsvoller Ansatz ist es, automatische Analytics mit nutzerinitiiertem Feedback zu kombinieren, also etwa so:
func handleError(_ error: Error) {
// Immer für Analytics tracken
trackErrorAnalytics(error)
// Bei schwerwiegenden oder unerwarteten Fehlern zum Feedback auffordern
if isSerious(error) {
showErrorAlert(
message: ErrorKit.userFriendlyMessage(for: error),
feedbackOption: true
)
} else {
// Bei kleineren Problemen einfach eine Meldung anzeigen
showErrorAlert(message: ErrorKit.userFriendlyMessage(for: error))
}
}
func showErrorAlert(message: String, feedbackOption: Bool = false) {
// Implementierung eines Alerts, der optional einen
// "Feedback senden"-Button enthält, der den Mail-Composer mit Logs öffnet
}Das ergibt ein umfassendes System, in dem:
Alle Fehler für Analytics getrackt werden und dir breite Muster liefern
Schwerwiegende Fehler Nutzer zu detailliertem Feedback mit Logs auffordern
Nutzer jederzeit Feedback für Probleme initiieren können, die du womöglich nicht trackst
Best Practices für Logging
Um den Wert der Log-Sammlung zu maximieren, beachte diese Best Practices:
1. Logs mit Kontext strukturieren
Liefere genug Kontext in deinen Logs, um zu verstehen, was passiert ist:
// Statt:
Logger().error("Failed to load")
// Verwende:
Logger().error("Failed to load document \(documentId): \(ErrorKit.errorChainDescription(for: error))")2. Passende Log-Level wählen
Setze Log-Level strategisch ein, um die Ausführlichkeit zu steuern:
.debugfür Entwicklerdetails, die nur während der Entwicklung gebraucht werden.infozum Nachverfolgen des normalen App-Ablaufs.noticefür wichtige Ereignisse, die Nutzer interessieren würden.errorfür Probleme, die behoben werden müssen, aber die Kernfunktionalität nicht verhindern.faultfür kritische Probleme, die die Kernfunktionalität beeinträchtigen
3. Sensible Informationen schützen
Verwende Privacy-Modifier, um Nutzerdaten zu schützen:
Logger().info("Processing payment for user \(userId, privacy: .private)")4. Wichtige Nutzeraktionen loggen
Erstelle Breadcrumbs der Nutzeraktivität, um den Weg zu Fehlern nachzuvollziehen:
Logger().notice("User navigated to profile screen")
Logger().info("User tapped edit button")
Logger().notice("User saved profile changes")5. Start und Abschluss wichtiger Operationen loggen
Klammere bedeutende Operationen ein, um unvollständige Aufgaben zu identifizieren:
Logger().notice("Starting data sync")
// ... Sync-Implementierung
Logger().notice("Completed data sync")Die Auswirkung auf Support und Entwicklung
Die Implementierung dieser Tools kann sowohl die Nutzererfahrung als auch die Entwicklungsabläufe transformieren:
Für Nutzer:
Vereinfachtes Melden: Feedback mit einem einzigen Tipp absenden
Keine technischen Fragen: Frustrierendes Hin-und-her-Kommunizieren vermeiden
Schnellere Lösung: Probleme können schneller diagnostiziert und behoben werden
Bessere Erfahrung: Zeigt Nutzern, dass ihre Probleme ernst genommen werden
Für Entwickler:
Vollständiger Kontext: Sieh genau, was passiert ist, als Probleme auftraten
Reduzierter Support-Aufwand: Weniger Zeit für Nachfragen nach zusätzlichen Informationen
Bessere Reproduktion: Zuverlässigere Reproduktionsschritte basierend auf Log-Daten
Effizientes Debugging: Muster in Fehlerberichten schnell erkennen
Datengetriebene Prioritäten: Zuerst die häufigsten Probleme beheben
Fazit
ErrorKits Ansatz überbrückt die frustrierende Lücke zwischen einem Nutzer, der “es geht nicht” sagt, und dem tatsächlichen Wissen, was passiert ist. Ich habe festgestellt, dass automatische Log-Sammlung kombiniert mit smarter Error-Analytics eine Feedback-Schleife erzeugt, die tatsächlich funktioniert.
Wirklich mächtig wird es, wenn du detaillierte Logs bekommst, sobald Nutzer sich entscheiden, ein Problem zu melden, und gleichzeitig die Probleme auffängst, die sie nie erwähnen. Dieser duale Ansatz hat grundlegend verändert, wie ich Probleme in meinen Apps verstehe und behebe. Wenn du es satt hast, Probleme im Blindflug zu debuggen, enthält ErrorKit all diese Logging-Tools und Verbesserungen für die Fehlerbehandlung – Werkzeuge, die ich gebaut habe, weil ich sie selbst brauchte:
Wie gehst du mit Nutzerfeedback und Error-Reporting um? Hast du andere effektive Techniken gefunden, die wirklich helfen? Schreib mir auf den sozialen Kanälen (Links unten)!

