iOS-UUIDs am Cloud-Mac strikt prüfen und normalisieren
Eine API liefert die UUID desselben Datensatzes plötzlich in Klein- statt in Großbuchstaben, und der Client legt einen zweiten Datensatz an. Meist liegt das nicht am Zufallszahlengenerator, sondern daran, dass ein String direkt als Primärschlüssel dient: Vergleiche, Cache und Persistenz verwenden jeweils eigene Regeln. Auf einem Cloud-Mac lässt sich diese Eingabegrenze zunächst mit einem eigenständigen Swift-Skript absichern. Anschließend können die Prüfungen in die Tests des iOS-Projekts übernommen werden, ohne einen Simulator zu starten.
Zuerst den Eingabevertrag festlegen
Dieser Artikel akzeptiert die standardisierte Textdarstellung mit 36 Bytes: Hexadezimalzeichen in fünf Gruppen mit 8, 4, 4, 4 und 12 Zeichen, getrennt durch ASCII-Bindestriche. Die Eingabe darf Groß- und Kleinbuchstaben enthalten; die Ausgabe erfolgt einheitlich in Kleinbuchstaben. Führende und nachgestellte Leerraumzeichen, geschweifte Klammern sowie Darstellungen ohne Bindestriche werden abgewiesen.
Damit werden andere Darstellungen nicht grundsätzlich für ungültig erklärt. Vielmehr wird für die Schnittstelle genau eine zulässige Eingabeform festgelegt. Akzeptiert eine ältere Schnittstelle weitere Formen, gehören diese in eine explizite Kompatibilitätsschicht. Der zentrale Parser sollte sie nicht stillschweigend korrigieren.
Außerdem sind zwei Dinge zu trennen: Syntaktisch gültig bedeutet nicht automatisch fachlich zulässig. Eine UUID, die ausschließlich aus Nullen besteht, ist strukturell gültig, kann aber als Bestell- oder Dokumentkennung verboten sein. Auch eine Einschränkung auf bestimmte UUID-Versionen muss im Schnittstellenvertrag stehen. Ein Wert darf nicht allein deshalb abgewiesen werden, weil das erste Zeichen der dritten Gruppe nicht einer bestimmten Ziffer entspricht.
Eine UUID ist eine Kennung, kein Zugriffsnachweis. Auch bei korrektem Format muss der Server prüfen, ob der aktuelle Benutzer den zugehörigen Datensatz lesen darf.
Einen ausführbaren, strikten Parser schreiben
Führen Sie auf dem Knoten xcrun --find swift aus, um zu prüfen, ob die aktuelle Toolchain Swift bereitstellt. Speichern Sie den folgenden Code als uuid-contract.swift. Er prüft zunächst die Byte-Struktur und übergibt die Eingabe erst danach an Foundation. So hängt die Validierung nicht davon ab, wie großzügig eine bestimmte Systemversion Eingaben auslegt.
import Foundation
enum IdentifierError: Error {
case invalidSyntax
case zeroNotAllowed
}
func parseIdentifier(
_ text: String,
allowZero: Bool = false
) throws -> UUID {
let bytes = Array(text.utf8)
let separators: Set<Int> = [8, 13, 18, 23]
guard bytes.count == 36 else {
throw IdentifierError.invalidSyntax
}
for (index, byte) in bytes.enumerated() {
if separators.contains(index) {
guard byte == 45 else {
throw IdentifierError.invalidSyntax
}
} else {
let isHex = (48...57).contains(byte)
|| (65...70).contains(byte)
|| (97...102).contains(byte)
guard isHex else {
throw IdentifierError.invalidSyntax
}
}
}
guard let value = UUID(uuidString: text) else {
throw IdentifierError.invalidSyntax
}
let zero = UUID(
uuid: (0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0)
)
guard allowZero || value != zero else {
throw IdentifierError.zeroNotAllowed
}
return value
}
func canonical(_ value: UUID) -> String {
value.uuidString.lowercased()
}
Die Prüfung von UTF-8-Bytes statt der Anzahl sichtbarer Zeichen schließt Zeichen voller Breite und Unicode-Symbole aus, die Bindestrichen nur ähnlich sehen. lowercased() verarbeitet hier ausschließlich die bereits validierte UUID-Ausgabe. Beliebige Benutzereingaben werden also nicht erst umgewandelt und anschließend ungeprüft akzeptiert.
Grenzfälle mit festen Testdaten prüfen
Ergänzen Sie am Ende derselben Datei die folgenden Tests. Die gültigen Beispiele prüfen Wertgleichheit, Normalisierung und Idempotenz. Die ungültigen Beispiele decken Leerraumzeichen, ersetzte Zeichen und Längenänderungen ab. Ersetzen Sie feste Negativbeispiele nicht durch zufällige UUIDs, sonst lassen sich Fehler schlechter reproduzieren.
let upper = "550E8400-E29B-41D4-A716-446655440000"
let lower = upper.lowercased()
let value = try parseIdentifier(upper)
precondition(value == (try parseIdentifier(lower)))
precondition(canonical(value) == lower)
let reparsed = try parseIdentifier(canonical(value))
precondition(canonical(reparsed) == lower)
let invalid = [
"",
" " + lower,
lower + "\n",
"{" + lower + "}",
lower.replacingOccurrences(of: "-", with: ""),
lower.replacingOccurrences(of: "-", with: "-"),
lower.replacingOccurrences(of: "5", with: "g"),
lower + "0"
]
for text in invalid {
do {
_ = try parseIdentifier(text)
fatalError("Invalid UUID accepted")
} catch IdentifierError.invalidSyntax {
}
}
let zero = "00000000-0000-0000-0000-000000000000"
do {
_ = try parseIdentifier(zero)
fatalError("Zero UUID accepted")
} catch IdentifierError.zeroNotAllowed {
}
_ = try parseIdentifier(zero, allowZero: true)
Führen Sie xcrun swift uuid-contract.swift aus. Bei Erfolg bleibt die Ausgabe leer; bei einem Fehler endet der Prozess mit einem Exit-Status ungleich null. Die leeren Catch-Blöcke bedeuten, dass der erwartete Fehler aufgetreten ist. Andere Fehler werden nicht verschluckt und lassen das Skript weiterhin fehlschlagen. Verwenden Sie nach der Übernahme in XCTest Assertions für konkrete Fehler, damit diese Unterscheidung erhalten bleibt.
An der Datengrenze normalisieren
Das Parsen gehört an die Stelle, an der Netzwerkantworten, Deep-Link-Parameter oder importierte Dateien in das Domänenmodell gelangen. Die Domänenschicht hält einen UUID-Wert vor. Erst beim Senden einer Anfrage oder beim Schreiben eines Textfelds wird canonical aufgerufen. UI- und Cache-Schicht sollten nicht unabhängig voneinander Strings ersetzen.
Besondere Vorsicht ist bei der Migration gespeicherter Daten geboten. Ordnen Sie die bisherigen Schlüssel zunächst in einer schreibgeschützten Kopie ihren kanonischen Formen zu und erfassen Sie alle Fälle, in denen mehrere alte Schlüssel dieselbe kanonische Form ergeben. Lösen Sie solche Konflikte nach den fachlichen Regeln zum Zusammenführen von Datensätzen, bevor Sie Fremdschlüssel und Eindeutigkeitsbedingungen aktualisieren. Eine pauschale Umwandlung in Kleinbuchstaben könnte zwei bestehende Datensätze zu einem zusammenfallen lassen.
UUID-Textsortierung ist keine zeitliche Sortierung
Die lexikografische Reihenfolge zufälliger UUIDs sagt nichts über die Erstellungsreihenfolge aus. Benötigt eine Liste eine stabile Sortierung, verwenden Sie ein explizites Feld für den Erstellungszeitpunkt. Bei gleichen Zeitstempeln dient die normalisierte Kennung als zweiter Sortierschlüssel. Auch bei einem UUID-Verfahren mit Zeitsemantik müssen Version und Sortierregeln gesondert festgelegt werden.
In den Abnahmeprozess auf dem Cloud-Mac einbinden
Halten Sie vor der Ausführung des Skripts auf einem ArmVNC-Knoten die Commit-Kennung sowie die Ausgaben von sw_vers und xcrun swift --version fest. Diese Angaben helfen, Unterschiede zwischen Toolchains nachzuvollziehen, und enthalten keine Geschäftsdaten. Verwenden Sie für Fehlerfälle feste Testwerte, statt echte Benutzerdatensätze zu archivieren.
Nach der Einbindung in die kontinuierliche Integration sollten mindestens diese vier Punkte geprüft werden:
- Die standardisierte Textdarstellung lässt sich parsen; unterschiedliche Groß- und Kleinschreibung ergibt denselben Wert.
- Leerraumzeichen, falsche Trennzeichen und nicht hexadezimale Zeichen müssen abgewiesen werden.
- Für die Null-UUID gilt eine explizite Regel; ihre Ablehnung darf nicht hinter einem allgemeinen Syntaxfehler verborgen bleiben.
- Vor der Migration alter Schlüssel werden Kollisionen geprüft; die Anzahl verknüpfter Datensätze bleibt vor und nach der Migration gleich.
Ergänzen Sie abschließend einen Integrationstest: Dieselbe Entität wird zweimal eingegeben, einmal mit einer UUID in Großbuchstaben und einmal in Kleinbuchstaben. Im Speicher darf weiterhin nur ein Datensatz vorhanden sein, und die Aktualisierung muss den ursprünglichen Datensatz betreffen. Der Parser sorgt für einen einheitlichen Eingang. Erst der Integrationstest belegt, dass Cache, Datenbank und fachliche Aktualisierungen diesen Vertrag tatsächlich einhalten.
Häufig gestellte Fragen
Sind UUIDs mit unterschiedlicher Großschreibung verschieden?
Nein. Vergleichen Sie geparste UUID-Werte und speichern Sie Textwerte einheitlich in der Standardform mit Kleinbuchstaben. Prüfen Sie vorhandene Schlüssel vor der Migration auf Kollisionen.
Muss eine UUID aus lauter Nullen immer abgelehnt werden?
Nein. Ihre Struktur ist gültig, doch der fachliche Vertrag darf sie als Entitätskennung ausschließen. Diese Regel muss explizit sein und für beide Einstellungen getestet werden.
Build-Aufgaben an einen exklusiven Cloud-Mac übergeben
Wählen Sie einen Apple-Silicon-Server und nutzen Sie ihn exklusiv – mit nativer macOS-Oberfläche und Kommandozeile. Modell, Laufzeit und Standort richten sich nach Ihrer Bestellbestätigung.