Ein Nutzer gibt in einem iOS-Simulator auf einem Cloud-Mac einen chinesischen Namen ein. Noch bevor er aus der Pinyin-Eingabe ein Schriftzeichen auswählt, meldet das Formular, dass nur chinesische Zeichen erlaubt seien. Direkt nach der Auswahl wird die Schaltfläche zum Absenden kurz deaktiviert. Meist liegt das nicht an einem fehlerhaften regulären Ausdruck: Die Anwendung behandelt Text während der IME-Komposition bereits als endgültigen Wert. Um den Fehler nachzustellen und zu beheben, muss zwischen „noch in Komposition“ und „bestätigt“ unterschieden werden. Allein die Eigenschaft text zu betrachten, reicht nicht aus.
Zuerst festlegen, welcher Zustand validiert wird
Pinyin-Eingaben für Chinesisch, die Kana-Konvertierung im Japanischen und die Zusammensetzung koreanischer Schriftzeichen durchlaufen jeweils eine Auswahl- oder Kompositionsphase. Bei einem UITextField, das UITextInput implementiert, zeigt ein nicht leerer markedTextRange an, dass gerade markierter Text vorhanden ist. Die Oberfläche darf diesen Text anzeigen. Sie sollte ihn aber weder als Grundlage für eine Formatfehlermeldung verwenden noch deshalb das Absenden freigeben oder ihn als endgültigen Wert speichern.
Ein Eingabeereignis bedeutet nicht, dass die Eingabe abgeschlossen ist. Text in Komposition gehört zum Bearbeitungsvorgang; fachliche Validierung muss sich auf den bestätigten Wert beziehen.
Zunächst braucht das Feld eine klare Regel: Während der Komposition werden alte Fehlermeldungen entfernt und die Formatprüfung ausgesetzt. Nach Abschluss der Komposition wird der aktuelle text erneut geprüft. Beim Absenden wird nochmals kontrolliert, ob die Komposition beendet ist, und der Wert erneut validiert. Dieser letzte Schritt ist nötig, weil Nutzer andernfalls über einen anderen Weg absenden könnten, während ein Kandidat noch nicht bestätigt ist. Ob ein leerer Wert zulässig ist und ob ein Name ausschließlich aus chinesischen Schriftzeichen bestehen muss, sind fachliche Regeln. Sie sollten getrennt von der Prüfung des Kompositionszustands bleiben.
Die Prüfung bei Feldereignissen zurückstellen
Das folgende Objekt entscheidet nur, wann die Validierungsfunktion aufgerufen wird. Die Beispielregel verlangt einen nicht leeren Wert, der ausschließlich chinesische Schriftzeichen enthält. In einer echten Anwendung ist die entsprechende isValid-Regel an die Anforderungen anzupassen; eine Namensregel gehört nicht unverändert in ein E-Mail- oder Adressfeld.
import UIKit
@MainActor
final class NameFieldValidator {
private(set) var message: String?
private(set) var canSubmit = false
func editingDidChange(_ field: UITextField) {
guard field.markedTextRange == nil else {
message = nil
canSubmit = false
return
}
evaluate(field.text ?? "")
}
func submit(_ field: UITextField) -> Bool {
guard field.markedTextRange == nil else {
canSubmit = false
return false
}
evaluate(field.text ?? "")
return canSubmit
}
private func evaluate(_ value: String) {
canSubmit = !value.isEmpty &&
value.unicodeScalars.allSatisfy {
(0x3400...0x9FFF).contains(Int($0.value))
}
message = canSubmit ? nil : "请填写中文姓名"
}
}
Im View-Controller wird .editingChanged mit editingDidChange(_:) verbunden. Anschließend werden Hinweis und Schaltfläche anhand von message und canSubmit aktualisiert. Ob Änderungen an markiertem Text ein Ereignis auslösen, hängt vom konkreten Eingabeweg ab. Die Bestätigung eines Kandidaten entspricht nicht zwangsläufig genau einem .editingChanged. Deshalb sollte der Feldzustand auch beim Ende der Bearbeitung und am Einstiegspunkt des Absendevorgangs erneut gelesen werden. Wird text programmatisch geändert, muss auch die Oberfläche ausdrücklich aktualisiert werden: Eine direkte Zuweisung ist nicht dasselbe wie eine Eingabe durch den Nutzer. Der Codepunktbereich im Beispiel dient nur zur Veranschaulichung des Ablaufs. Er deckt nicht alle Zeichen in Namen, erweiterten chinesischen Schriftzeichen oder Trennzeichen ab.
Zustandswechsel mit einem Unit-Test absichern
Im Unit-Test-Target der App wird ein echtes UITextField angelegt. Der Test prüft, dass während der Komposition kein Absenden möglich ist und erst nach der Bestätigung anhand des endgültigen Textes entschieden wird. UIKit-Objekte müssen auf dem Hauptthread verwendet werden; dieser Test benötigt keine Bildschirmtastatur.
import XCTest
import UIKit
@MainActor
final class NameFieldValidatorTests: XCTestCase {
func testMarkedTextIsNotSubmitted() {
let field = UITextField()
let validator = NameFieldValidator()
field.setMarkedText("zhong", selectedRange: NSRange(location: 5, length: 0))
validator.editingDidChange(field)
XCTAssertNotNil(field.markedTextRange)
XCTAssertFalse(validator.canSubmit)
XCTAssertFalse(validator.submit(field))
field.setMarkedText("中文", selectedRange: NSRange(location: 2, length: 0))
field.unmarkText()
validator.editingDidChange(field)
XCTAssertNil(field.markedTextRange)
XCTAssertEqual(field.text, "中文")
XCTAssertTrue(validator.submit(field))
}
}
Der getestete Typ muss für das Test-Target zugänglich sein. Danach kann der Test auf dem Cloud-Mac mit der Test-Aktion von Xcode ausgeführt werden. Für die Einbindung in CI zuerst das Scheme in Xcode freigeben und dann mit xcodebuild -showdestinations -scheme <方案名> -project <工程名>.xcodeproj verfügbare Simulatoren ermitteln. Das ausgewählte Ziel platform=iOS Simulator,id=<设备ID> wird an xcodebuild test übergeben. Die Geräte-ID muss vom jeweils verwendeten Rechner stammen; die ID eines anderen Rechners gehört nicht fest ins Skript.
Der Test prüft die Reaktion des Validators auf den Feldzustand, nicht die Reihenfolge, in der die Systemtastatur Ereignisse sendet. Schlägt eine Assertion für markedTextRange fehl, zuerst prüfen, ob das Testfeld markierten Text unterstützt und ob der Aufruf auf dem Hauptthread läuft. Die Assertion zum Kompositionszustand darf nicht entfernt werden, nur damit der Test erfolgreich ist.
Den tatsächlichen Eingabeweg im Simulator prüfen
Nach bestandenem Unit-Test den gewünschten iOS-Simulator über die grafische Oberfläche des Cloud-Mac öffnen. In den Systemeinstellungen die benötigte Eingabemethode aktivieren und sicherstellen, dass die aktuelle Tastatur tatsächlich auf sie umgeschaltet ist. Eine Einstellung, die Eingaben unmittelbar als Text einer physischen Tastatur übermittelt, deaktivieren. Danach das Namensfeld öffnen und Folgendes prüfen:
- Pinyin eingeben, ohne einen Kandidaten auszuwählen, und Fehlermeldung sowie Schaltfläche zum Absenden beobachten. Der unbestätigte Text darf nicht als gültiger Name behandelt werden.
- Einen Kandidaten mit chinesischen Schriftzeichen auswählen und prüfen, ob Hinweis, Schaltfläche und Feldwert zusammenpassen. Anschließend ein Zeichen löschen und kontrollieren, ob die Validierung dem endgültigen Wert folgt.
- Während der Komposition über die Return-Taste oder einen anderen Einstiegspunkt das Absenden versuchen. Nicht nur die Schaltfläche muss deaktiviert erscheinen; auch die Absendeprüfung selbst muss den Vorgang ablehnen.
- Die Prüfung mit anderen relevanten Systemversionen und Eingabemethoden wiederholen. Ein Automatisierungsskript, das nur fertige chinesische Schriftzeichen einfügt, umgeht gerade die entscheidende Kompositionsphase.
Falls nach der Auswahl eines Zeichens nicht erneut validiert wird, lassen sich vorübergehend die Zustände bei .editingChanged, beim Ende der Bearbeitung und am Einstiegspunkt des Absendevorgangs protokollieren. So können fehlende Ereignisse eingegrenzt werden. Debug-Ausgaben gehören nur zur lokalen Fehlersuche; Nutzereingaben dürfen nicht in dauerhafte Logs geschrieben werden.
Die Zuständigkeitsgrenzen richtig ziehen
In echten Formularen können auch automatisches Ausfüllen, Einfügen, Spracheingabe und programmatisch gesetzte Werte den Feldinhalt ändern. Diese Wege erzeugen nicht unbedingt markierten Text. Deshalb muss beim endgültigen Absenden eine eigenständige Prüfung stattfinden. Umgekehrt darf Text während der Bearbeitung nicht verworfen werden, nur weil markedTextRange nicht leer ist: Ausgesetzt wird lediglich die fachliche Validierung, die sonst zu Fehlurteilen führen würde. Die normale Anzeige der Eingabe bleibt erhalten.
Bei der Abnahme sind drei Fragen getrennt zu prüfen: Erscheint vor der Kandidatenauswahl fälschlich ein Fehler? Wird der Zustand nach der Bestätigung rechtzeitig aktualisiert? Greift die Prüfung auch beim Absenden ohne Klick auf die Schaltfläche? Nur wenn alle drei Punkte einzeln getestet werden, verdeckt ein Screenshot mit erfolgreich eingegebenen chinesischen Schriftzeichen nicht das auf dem Cloud-Mac reproduzierte Eingabeproblem.
Häufig gestellte Fragen
Wann sollte ein iOS-Formular IME-Eingaben validieren?
Nicht während markedTextRange gesetzt ist. Prüfen Sie den endgültigen Feldwert nach Abschluss der Komposition und erneut beim Absenden.
Reicht ein XCTest ohne Prüfung im Simulator?
Nein. XCTest deckt die Validierungsregel ab; Kandidatenauswahl und Ereignisfolge müssen Sie mit der Zielversion von iOS im Simulator prüfen.
Richten Sie einen Cloud-Mac für Ihre nächste Aufgabe ein
Wählen Sie einen Mac mini passend zum Arbeitsspeicherbedarf oder Build-Aufkommen. Drei Modellklassen sind tage-, wochen-, monats- oder quartalsweise buchbar. Die aktuelle Verfügbarkeit der Knoten sehen Sie in Echtzeit in der Konsole.