SwiftUI-Formularvalidierung, die sich nativ anfühlt: Fokus und Fehlermeldungen
SwiftUI-Formularvalidierung, die sich nativ anfühlt: Fokus verwalten, Inline-Fehler zur richtigen Zeit anzeigen und Servermeldungen klar darstellen, ohne Nutzer zu nerven.

Wie sich „nativ wirkende“ Validierung in SwiftUI anfühlt
Ein nativ wirkendes iOS-Formular ist ruhig. Es widerspricht dem Nutzer nicht beim Tippen. Es gibt klare Rückmeldungen, wenn es wichtig ist, und macht den Nutzer nicht auf die Suche nach dem Fehler.
Die Hauptanforderung ist Vorhersehbarkeit. Dieselben Aktionen sollten immer zur gleichen Art von Rückmeldung führen. Wenn ein Feld ungültig ist, sollte das Formular das konsistent an der gleichen Stelle zeigen, mit dem gleichen Tonfall und einer klaren nächsten Handlung.
Die meisten Formulare brauchen drei Arten von Regeln:
- Feldregeln: Ist dieser einzelne Wert gültig (leer, Format, Länge)?
- Feldübergreifende Regeln: Stimmen Werte überein oder hängen sie voneinander ab (Passwort und Passwort bestätigen)?
- Serverregeln: Akzeptiert das Backend es (E-Mail bereits verwendet, Einladung erforderlich)?
Das Timing ist wichtiger als ein cleverer Wortlaut. Gute Validierung wartet auf einen sinnvollen Moment und spricht dann einmal klar. Ein praktischer Rhythmus sieht so aus:
- Bleibe still, während der Nutzer tippt, besonders bei Formatregeln.
- Zeige Rückmeldung, nachdem ein Feld verlassen wurde oder der Nutzer auf „Senden" tippt.
- Halte Fehler sichtbar, bis sie behoben sind, und entferne sie dann sofort.
Validierung sollte still sein, solange der Nutzer noch die Antwort formt, zum Beispiel beim Eingeben einer E-Mail oder eines Passworts. Einen Fehler beim ersten Zeichen zu zeigen, fühlt sich wie Nörgeln an, selbst wenn es technisch korrekt ist.
Validierung sollte sichtbar werden, wenn der Nutzer signalisiert, dass er fertig ist: Fokus verschiebt sich weg oder er versucht zu senden. Das ist der Moment, in dem er Hilfe will, und dann kannst du ihn gezielt zum Feld führen, das Aufmerksamkeit braucht.
Wenn das Timing stimmt, wird alles andere einfacher. Inline-Nachrichten bleiben kurz, Fokusbewegungen wirken hilfreich, und serverseitige Fehler fühlen sich wie normale Rückmeldungen statt Strafen an.
Ein einfaches Validierungszustandsmodell aufsetzen
Ein nativ wirkendes Formular beginnt mit einer klaren Trennung: der getippte Text des Nutzers ist nicht dasselbe wie die Meinung der App über diesen Text. Wenn du sie mischst, zeigst du entweder Fehler zu früh oder verlierst Servernachrichten, wenn die UI aktualisiert wird.
Ein einfacher Ansatz ist, jedem Feld einen eigenen Zustand mit vier Teilen zu geben: den aktuellen Wert, ob der Nutzer damit interagiert hat, den lokalen (auf dem Gerät berechneten) Fehler und den Serverfehler (falls vorhanden). Dann kann die UI entscheiden, was gezeigt wird, basierend auf „touched" und „submitted", anstatt auf jeden Tastendruck zu reagieren.
struct FieldState {
var value: String = ""
var touched: Bool = false
var localError: String? = nil
var serverError: String? = nil
// One source of truth for what the UI displays
func displayedError(submitted: Bool) -> String? {
guard touched || submitted else { return nil }
return localError ?? serverError
}
}
struct FormState {
var submitted: Bool = false
var email = FieldState()
var password = FieldState()
}
Ein paar kleine Regeln halten das vorhersehbar:
- Halte lokale und Serverfehler getrennt. Lokale Regeln (wie „erforderlich" oder „ungültige E-Mail") sollten eine Servermeldung wie „E-Mail bereits vergeben" nicht überschreiben.
- Leere
serverError, wenn der Nutzer das Feld erneut bearbeitet, damit er nicht auf einer alten Meldung hängen bleibt. - Setze
touched = truenur, wenn der Nutzer das Feld verlässt (oder wenn du entscheidest, dass er versucht hat zu interagieren), nicht beim ersten getippten Zeichen.
Mit diesem Setup kann deine View frei an value binden. Validierung aktualisiert localError, und deine API-Schicht setzt serverError, ohne dass sie sich gegenseitig behindern.
Fokussteuerung, die führt, nicht nörgelt
Gute SwiftUI-Validierung sollte sich so anfühlen, als würde die Systemtastatur dem Nutzer helfen, eine Aufgabe zu beenden, nicht als würde die App ihn tadeln. Fokus ist ein großer Teil davon.
Ein einfaches Muster ist, den Fokus als eine einzige Quelle der Wahrheit mit @FocusState zu behandeln. Definiere ein Enum für deine Felder, binde jedes Feld daran und springe weiter, wenn der Nutzer die Tastatur-Schaltfläche tippt.
enum Field: Hashable { case email, password, confirm }
@FocusState private var focused: Field?
TextField("Email", text: $email)
.textContentType(.emailAddress)
.keyboardType(.emailAddress)
.textInputAutocapitalization(.never)
.submitLabel(.next)
.focused($focused, equals: .email)
.onSubmit { focused = .password }
SecureField("Password", text: $password)
.submitLabel(.next)
.focused($focused, equals: .password)
.onSubmit { focused = .confirm }
Was dieses Verhalten nativ wirken lässt, ist Zurückhaltung. Bewege den Fokus nur auf klare Nutzeraktionen: Next, Done oder die primäre Schaltfläche. Beim Absenden fokussiere das erste ungültige Feld (und scrolle bei Bedarf dorthin). Stehle nicht den Fokus, während der Nutzer tippt, selbst wenn der Wert momentan ungültig ist. Sei außerdem konsistent mit den Tastaturlabels: Next für Zwischenfelder, Done fürs letzte Feld.
Ein gängiges Beispiel ist die Registrierung. Der Nutzer tippt „Account erstellen". Du validierst einmal, zeigst Fehler und setzt dann den Fokus auf das erste fehlerhafte Feld (oft die E-Mail). Wenn er sich gerade im Passwortfeld befindet und noch tippt, springe ihn nicht mitten im Tippen zurück zur E-Mail. Dieses kleine Detail ist oft der Unterschied zwischen „poliertem iOS-Formular" und „nervigem Formular".
Inline-Fehler, die zur richtigen Zeit erscheinen
Inline-Fehler sollten sich wie ein leiser Hinweis anfühlen, nicht wie ein Tadel. Der größte Unterschied zwischen „nativ" und „nervig" ist, wann du die Nachricht zeigst.
Timing-Regeln
Wenn ein Fehler schon beim ersten Tipp angezeigt wird, unterbricht das. Eine bessere Regel ist: Warte, bis der Nutzer eine faire Chance hatte, das Feld fertigzustellen.
Gute Momente, um einen Inline-Fehler zu zeigen:
- Nachdem das Feld den Fokus verloren hat
- Nachdem der Nutzer auf Senden getippt hat
- Nach einer kurzen Pause beim Tippen (nur für offensichtliche Prüfungen, wie E-Mail-Format)
Ein verlässlicher Ansatz ist, eine Nachricht nur dann zu zeigen, wenn das Feld touched ist oder ein Submit versucht wurde. Ein frisches Formular bleibt ruhig, aber der Nutzer bekommt klare Hinweise, sobald er interagiert.
Layout und Stil
Nichts wirkt weniger iOS-ähnlich als ein Layout, das springt, wenn ein Fehler erscheint. Reserve Platz für die Nachricht oder animiere ihr Erscheinen, damit sie nicht abrupt das nächste Feld nach unten schiebt.
Halte Fehlermeldungen kurz und konkret, mit einer Handlung pro Nachricht. „Passwort muss mindestens 8 Zeichen haben" ist umsetzbar. „Ungültige Eingabe" ist es nicht.
Beim Styling strebe Subtilität und Konsistenz an. Eine kleine Schrift unter dem Feld (wie footnote), eine einheitliche Fehlerfarbe und eine dezente Hervorhebung des Feldes lesen sich meist besser als starke Hintergründe. Lege die Nachricht ab, sobald der Wert wieder gültig ist.
Ein realistisches Beispiel: Zeige bei einem Anmeldeformular nicht „E-Mail ungültig", während der Nutzer noch name@ tippt. Zeige es, nachdem er das Feld verlässt oder nach einer kurzen Pause, und entferne es, sobald die Adresse gültig wird.
Lokaler Validierungsablauf: Tippen, Feld verlassen, Absenden
Ein guter lokaler Ablauf hat drei Geschwindigkeiten: sanfte Hinweise beim Tippen, strengere Prüfungen beim Verlassen eines Feldes und vollständige Regeln beim Absenden. Dieser Rhythmus sorgt dafür, dass Validierung nativ wirkt.
Während der Nutzer tippt, halte die Validierung leicht und unaufdringlich. Denke „ist das offensichtlich unmöglich?“ statt „ist das perfekt?" Für ein E-Mail-Feld prüfst du vielleicht nur, dass es ein @ enthält und keine Leerzeichen. Beim Passwort könntest du nach Beginn der Eingabe eine kleine Hilfe wie „8+ Zeichen" anzeigen, aber vermeide rote Fehler beim ersten Tastendruck.
Wenn der Nutzer ein Feld verlässt, führe strengere Einzel-Feld-Regeln aus und zeige bei Bedarf Inline-Fehler. Hier gehören „Erforderlich" und „Ungültiges Format" hin. Es ist auch ein guter Moment, Leerzeichen zu trimmen und Eingaben zu normalisieren (z. B. E-Mail kleinschreiben), damit der Nutzer sieht, was gesendet wird.
Beim Absenden validiere alles noch einmal, einschließlich feldübergreifender Regeln, die du vorher nicht prüfen konntest. Das klassische Beispiel sind Passwort und Passwort bestätigen. Scheitert das, setze den Fokus auf das Feld, das korrigiert werden muss, und zeige dort eine klare Nachricht.
Behandle die Absenden-Schaltfläche mit Bedacht. Halte sie aktiv, während der Nutzer noch das Formular ausfüllt. Deaktiviere sie nur, wenn Tippen sinnlos wäre (z. B. während bereits ein Absendevorgang läuft). Wenn du sie deaktivierst, zeige dennoch, was zu beheben ist.
Während des Absendevorgangs zeige einen klaren Ladezustand. Ersetze das Schaltflächen-Label durch einen ProgressView, verhindere Doppeltaps und halte das Formular sichtbar, damit Nutzer verstehen, was geschieht. Dauert die Anfrage länger als eine Sekunde, reduziert ein kurzer Labeltext wie „Account wird erstellt..." die Unsicherheit, ohne zusätzliche Unruhe zu stiften.
Serverseitige Validierung, ohne Nutzer zu frustrieren
Serverseitige Prüfungen sind die endgültige Instanz, selbst wenn deine lokalen Regeln stark sind. Ein Passwort kann deine Regeln bestehen, aber vom Server als zu häufig abgelehnt werden, oder eine E-Mail kann bereits vergeben sein.
Der größte UX-Gewinn ist, „deine Eingabe ist nicht akzeptabel" von „wir konnten den Server nicht erreichen" zu trennen. Wenn die Anfrage abläuft oder der Nutzer offline ist, markiere Felder nicht als ungültig. Zeige ein ruhiges Banner oder einen Alert wie „Verbindung fehlgeschlagen. Bitte erneut versuchen." und lass das Formular unverändert.
Wenn der Server Validierung fehlschlägt, behalte die Eingaben des Nutzers bei und weise auf die genauen Felder hin. Das Formular zu löschen, ein Passwort zu entfernen oder den Fokus wegzunehmen, lässt Nutzer bestraft fühlen.
Ein einfaches Muster ist, eine strukturierte Fehlerantwort in zwei Kategorien zu parsen: Feldfehler und Formularfehler. Aktualisiere dann deinen UI-Zustand, ohne die Textbindungen zu verändern.
struct ServerValidation: Decodable {
var fieldErrors: [String: String]
var formError: String?
}
// Map keys like "email" or "password" to your local field IDs.
Was sich meist nativ anfühlt:
- Platziere Feldmeldungen inline unter dem Feld und verwende die Formulierung des Servers, wenn sie klar ist.
- Springe den Fokus zum ersten fehlerhaften Feld nur nach einem Absendeversuch, nicht während des Tippens.
- Wenn der Server mehrere Probleme zurückgibt, zeige pro Feld die erste Meldung, um lesbar zu bleiben.
- Fallbacks wie „Etwas ist schiefgelaufen" vermeide, wenn du Felddetails hast.
Beispiel: Der Nutzer sendet ein Anmeldeformular und der Server antwortet „E-Mail bereits vergeben." Behalte die eingegebene E-Mail, zeige die Nachricht unter dem E-Mail-Feld und fokussiere dieses Feld. Ist der Server down, zeige eine einzelne Retry-Meldung und lass alle Felder unverändert.
Servermeldungen an der richtigen Stelle anzeigen
Serverfehler wirken „ungerecht", wenn sie in einem zufälligen Banner erscheinen. Platziere jede Nachricht so nah wie möglich beim Feld, das sie verursacht hat. Verwende eine allgemeine Meldung nur, wenn du sie wirklich keinem einzelnen Feld zuordnen kannst.
Beginne damit, die Fehler-Payload des Servers in deine SwiftUI-Feldidentifikatoren zu übersetzen. Das Backend könnte Schlüssel wie email, password oder profile.phone zurückgeben, während deine UI ein Enum wie Field.email und Field.password verwendet. Mache das Mapping einmal direkt nach der Antwort, damit der Rest deiner View konsistent bleibt.
Eine flexible Modellierung ist, serverFieldErrors: [Field: [String]] und serverFormErrors: [String] zu halten. Speichere Arrays, auch wenn du meist nur eine Nachricht zeigst. Wenn du eine Inline-Meldung anzeigst, wähle die hilfreichste Nachricht zuerst. Zum Beispiel ist „E-Mail bereits vergeben" hilfreicher als „Ungültige E-Mail", wenn beides auftaucht.
Mehrere Fehler pro Feld sind üblich, aber alle anzuzeigen ist laut. Meistens zeige nur die erste Nachricht inline und hebe die restlichen für eine Detailansicht auf, falls nötig.
Für Fehler, die keinem Feld zugeordnet sind (abgelaufene Sitzung, Rate-Limits, „Versuche es später erneut"), platziere sie in der Nähe des Absende-Buttons, damit der Nutzer sie beim Handeln sieht. Achte außerdem darauf, alte Fehler bei Erfolg zu löschen, damit die UI nicht „hängen bleibt".
Lösche schließlich Serverfehler, wenn der Nutzer das zugehörige Feld ändert. Praktisch entfernt ein onChange-Handler für email serverFieldErrors[.email], sodass die UI sofort widerspiegelt: „Okay, du behebst es."
Barrierefreiheit und Ton: kleine Entscheidungen, die nativ wirken
Gute Validierung dreht sich nicht nur um Logik. Es geht auch darum, wie sie gelesen, gehört und bei Dynamic Type, VoiceOver und in verschiedenen Sprachen funktioniert.
Fehler lesbar machen (nicht nur mit Farbe)
Geh davon aus, dass Text groß dargestellt werden kann. Verwende Dynamic Type-freundliche Stile (wie .font(.footnote) oder .font(.caption) ohne feste Größen) und lass Fehlermeldungen umbrechen. Halte Abstände konsistent, damit das Layout nicht zu stark springt, wenn ein Fehler erscheint.
Verlasse dich nicht nur auf rote Schrift. Füge ein klares Icon, ein Präfix wie „Fehler:" oder beides hinzu. Das hilft Menschen mit Farbsehschwäche und macht das Scannen schneller.
Ein schneller Prüfpunkt, der meist funktioniert:
- Verwende einen gut lesbaren Textstil, der mit Dynamic Type skaliert.
- Erlaube Umbruch und vermeide Kürzung bei Fehlermeldungen.
- Füge ein Icon oder ein Label wie „Fehler:" zusätzlich zur Farbe hinzu.
- Achte auf hohen Kontrast in Light Mode und Dark Mode.
VoiceOver die richtigen Informationen vorlesen lassen
Wenn ein Feld ungültig ist, sollte VoiceOver das Label, den aktuellen Wert und den Fehler zusammen vorlesen. Wenn der Fehler als separates Text unter dem Feld liegt, kann er übersprungen oder aus dem Kontext gerissen werden.
Zwei Muster helfen:
- Kombiniere das Feld und seinen Fehler in einem Accessibility-Element, sodass der Fehler beim Fokussieren des Feldes angesagt wird.
- Setze einen Accessibility-Hint oder -Wert, der die Fehlermeldung enthält (z. B. „Passwort, erforderlich, muss mindestens 8 Zeichen haben").
Der Ton ist ebenfalls wichtig. Schreibe Meldungen klar und leicht zu lokalisieren. Vermeide Slang, Witze und vage Formulierungen wie „Ups". Bevorzuge konkrete Anweisungen wie „E-Mail fehlt" oder „Passwort muss eine Zahl enthalten".
Beispiel: Ein Registrierungsformular mit lokalen und serverseitigen Regeln
Stell dir ein Registrierungsformular mit drei Feldern vor: E-Mail, Passwort und Passwort bestätigen. Das Ziel ist ein Formular, das leise bleibt, während der Nutzer tippt, und hilfsbereit wird, wenn er weiterkommen will.
Fokusreihenfolge (was die Return-Taste macht)
Mit SwiftUI FocusState sollte jeder Return-Tastendruck wie ein natürlicher Schritt wirken.
- Email Return: Fokus auf Password.
- Password Return: Fokus auf Confirm Password.
- Confirm Password Return: Tastatur ausblenden und Submit versuchen.
- Wenn Submit fehlschlägt: Fokus aufs erste Feld mit Aufmerksamkeitbedarf.
Dieser letzte Schritt ist wichtig. Wenn die E-Mail ungültig ist, geht der Fokus zurück zur E-Mail, nicht nur zu einer roten Meldung irgendwo.
Wann Fehler erscheinen
Eine einfache Regel hält die UI ruhig: Zeige Nachrichten nach dem Berühren eines Feldes (wenn der Nutzer es verlässt) oder nach einem Submit-Versuch.
- Email: Zeige „Gib eine gültige E-Mail ein" nach dem Verlassen des Feldes oder beim Submit.
- Password: Zeige Regeln (z. B. Mindestlänge) nach dem Verlassen oder beim Submit.
- Confirm Password: Zeige „Passwörter stimmen nicht überein" nach dem Verlassen oder beim Submit.
Jetzt die Serverseite. Angenommen, der Nutzer sendet und deine API gibt Folgendes zurück:
{
"errors": {
"email": "That email is already in use.",
"password": "Password is too weak. Try 10+ characters."
}
}
Was der Nutzer sieht: Die E-Mail zeigt die Servermeldung direkt darunter, und das Passwort zeigt seine Meldung unter dem Passwortfeld. Confirm Password bleibt ruhig, sofern es lokal nicht fehlschlägt.
Was er dann tut: Fokus landet auf der E-Mail (dem ersten Serverfehler). Er ändert die E-Mail, drückt Return, um zum Passwort zu springen, passt das Passwort an und sendet erneut. Weil die Meldungen inline sind und der Fokus absichtlich gesetzt wird, wirkt das Formular kooperativ statt tadelnd.
Häufige Fallen, die Validierung „un-iOS" wirken lassen
Ein Formular kann technisch korrekt und trotzdem falsch wirken. Die meisten „un-iOS"-Probleme liegen beim Timing: wann du einen Fehler zeigst, wann du den Fokus bewegst und wie du auf den Server reagierst.
Ein häufiger Fehler ist, zu früh zu sprechen. Wenn du einen Fehler beim ersten Tastendruck zeigst, fühlt sich der Nutzer beim Tippen gescholten. Bis das Feld berührt wurde (verlassen oder Submit versucht), zu warten, behebt das oft.
Asynchrone Serverantworten können den Fluss ebenfalls stören. Wenn eine Registrierung zurückkommt und du plötzlich den Fokus verschiebst, wirkt das willkürlich. Halte den Fokus dort, wo der Nutzer zuletzt war, und bewege ihn nur, wenn er Next tippt oder du einen Submit behandelst.
Eine weitere Falle ist, bei jeder Änderung alles zu löschen. Alle Fehler sofort zu entfernen, sobald ein Zeichen sich ändert, kann das echte Problem verbergen, besonders bei Servermeldungen. Leere nur den Fehler für das gerade bearbeitete Feld und behalte die restlichen, bis sie wirklich behoben sind.
Vermeide „stille" deaktivierte Submit-Buttons. Den Button dauerhaft zu deaktivieren, ohne zu erklären, was zu beheben ist, zwingt Nutzer zu raten. Wenn du deaktivierst, kombiniere es mit konkreten Hinweisen oder erlaube Absenden und leite dann zum ersten Problem.
Langsame Anfragen und doppelte Taps sind leicht zu übersehen. Wenn du keinen Fortschritt zeigst und Doppeltaps nicht verhinderst, tippen Nutzer zweimal, bekommen zwei Antworten und landen in verwirrenden Zuständen.
Eine kurze Checkliste:
- Verzögere Fehler bis Blur oder Submit, nicht bis zum ersten Zeichen.
- Bewege den Fokus nach einer Serverantwort nicht, es sei denn, der Nutzer hat es verlangt.
- Lösche Fehler feldweise, nicht alles auf einmal.
- Erkläre, warum Submit blockiert ist (oder erlaube Submit mit Anleitung).
- Zeige Laden an und ignoriere zusätzliche Taps während des Wartens.
Beispiel: Wenn der Server „E-Mail bereits vergeben" sagt (vielleicht von einem Backend, das du in AppMaster gebaut hast), behalte die Meldung unter der E-Mail, lass das Passwort unberührt und erlaube dem Nutzer, die E-Mail zu ändern, ohne das ganze Formular neu starten zu müssen.
Kurze Checkliste und nächste Schritte
Eine nativ wirkende Validierungserfahrung dreht sich hauptsächlich um Timing und Zurückhaltung. Du kannst strenge Regeln haben und den Bildschirm trotzdem ruhig wirken lassen.
Überprüfe vor dem Release:
- Validiere zur richtigen Zeit. Zeige keine Fehler beim ersten Tastendruck, außer es ist wirklich hilfreich.
- Bewege den Fokus mit Absicht. Beim Absenden zum ersten ungültigen Feld springen und deutlich machen, was falsch ist.
- Halte die Formulierungen kurz und konkret. Sage, was als Nächstes zu tun ist, nicht nur, was der Nutzer „falsch" gemacht hat.
- Respektiere Laden und Retry. Deaktiviere die Absende-Schaltfläche beim Senden und behalte eingegebene Werte bei, falls die Anfrage fehlschlägt.
- Behandle Serverfehler als Feldrückmeldung, wann immer möglich. Mappe Servercodes zu einem Feld und verwende eine Top-Meldung nur für echte globale Probleme.
Teste es dann wie ein echter Mensch. Halte ein kleines Telefon in einer Hand und versuche, das Formular mit dem Daumen auszufüllen. Schalte danach VoiceOver an und überprüfe, ob Fokusreihenfolge, Fehleransagen und Schaltflächenbeschriftungen noch Sinn ergeben.
Für Debugging und Support hilft es, Server-Validierungscodes (nicht rohe Meldungen) zusammen mit Bildschirm- und Feldnamen zu protokollieren. Wenn ein Nutzer sagt „ich kann mich nicht registrieren", siehst du schnell, ob es email_taken, weak_password oder ein Netzwerkausfall war.
Um das im ganzen Projekt konsistent zu halten, standardisiere dein Feldmodell (value, touched, local error, server error), die Fehlerplatzierung und die Fokusregeln. Wenn du native iOS-Formulare schneller bauen willst, ohne jede Seite von Hand zu coden, kann AppMaster (appmaster.io) SwiftUI-Apps zusammen mit Backend-Services generieren, was hilft, Client- und Server-Validierungsregeln besser abzustimmen.


