Reject scans that don’t match what a ScanField expects, before they reach your data.
ZipZapKit hasn’t been released yet. The API names on this page are provisional and may change.
Validation checks each scan against rules you set and rejects the ones that fail. A rejected scan never reaches your binding, so your app only ever sees values it can use.Use it when a wrong scan is likely or costly: labels with several barcodes on them, numbers that people sometimes type by hand, or fields where a bad value causes trouble later.
Every scan goes through the same steps, in this order:
Barcode type. Is this one of the types the field accepts? See Barcode types.
Normalization. The value is converted to the form you asked for. See Normalization.
Validation. Each rule checks the normalized value.
Your binding. If every rule passes, the value is written.
Because validation runs after normalization, write your rules for the form you store. If you normalize to 14-digit GTINs, validate 14 digits.Barcode types and validation answer different questions. Barcode types decide which barcodes are read at all. Validation decides whether what was read is acceptable.
Recalculates the check digit and rejects the value if it doesn’t match. It applies to GTINs (UPC-A, UPC-E, EAN-13, EAN-8, ITF-14) and passes other values through.A scanner already verifies the check digit of a UPC or EAN while reading it, so this rule matters most for values that didn’t come from one: numbers typed by hand, and GTINs carried inside another barcode type such as a QR code. The algorithm is explained in Check digits.
Rejects values that aren’t the given length, or within the given range.Fix the length whenever you can for types with no check digit, such as Interleaved 2 of 5. It’s the main defense against a partial read, where the scanner sees only part of the barcode and reports a shorter, valid-looking number.
Rejects values that aren’t well-formed GS1 data: unknown Application Identifiers, fields of the wrong length, invalid dates, bad check digits inside a field. See GS1 Application Identifiers.
Return true to accept the scan. The closure receives the normalized value and the barcode type. Use it for formats the built-in rules can’t express, such as a number with your own checksum.
Check the form of a value, not its meaning. A rule should answer “could this be an asset tag?”, not “is this the asset I expected?”.Rules run on every barcode the scanner sees, so they have to be fast: no network or database calls. And a rejection only tells the user “not accepted”. To check a scan against an order or a database, do it in your own code after the value is written, where you can say what’s wrong. See Checking for the right value.
A rejected scan isn’t written, so the field keeps its previous value. The user is told the barcode was recognized but not accepted, in a way that depends on how they’re scanning: see Behavior by source.To respond yourself as well, add a handler:
ScanField("Product barcode", text: $gtin) .validate(.checkDigit) .onRejectedScan { scan, rule in message = "That barcode isn't a valid product number." }
The handler receives the rejected scan and the rule it failed.
The barcode is marked in the camera view as recognized but not accepted, and the camera keeps looking. On a label with several barcodes, it settles on the one that passes.
ZipZap hardware
The scanner ignores the barcode. If the user keeps aiming at it, the scanner signals the rejection with a beep, a flash and a vibration.
Other HID scanners
The scanner types the value and the field validates it when the scanner sends Enter. The barcode type isn’t known, so rules run on the value alone.
Validation tells you a value is well formed. It can’t tell you the user scanned the right thing: a valid barcode for the wrong product passes every rule.Check that in your own code, once the value is in your binding. There you can use your app’s data and tell the user exactly what’s wrong:
ScanField("Item", text: $gtin) .barcodeTypes(.retailProduct) .validate(.checkDigit) .onChange(of: gtin) { _, scanned in if !order.contains(scanned) { message = "That item isn't in this order." } }