Skip to main content
The validation system checks required fields, validates data formats, and provides detailed feedback about validation status.

How Required Field Validation Works

Joyfill automatically validates required fields based on:
  • Field requirement: Fields marked as required = true
  • Field visibility: Hidden fields are always filtered out of the validation output
  • Field values: Required fields must have non-empty values to be valid
Basic Usage
let documentEditor = DocumentEditor(document: document)

// Validate all fields
let validationResult = documentEditor.validate()

if validationResult.status == .valid {
    print("Form is complete and valid")
    // Proceed with submission
} else {
    print("Form has validation errors")
    // Show errors to user
}
Path-scoped validation (validate(path:)) Use a path string when you only need validation for the whole document, one page, one field, or—on table and collection fields—a single row or single cell. Rules match validate(), scoped to the path depth. The result is a ComponentValidity: .page, .field, .row, .cell, or .notFound.
  • "" (or whitespace-only) — same as validate(); .page(Validation).
  • pageId — fields on that page only; .page(Validation).
  • pageId/fieldPositionId — that field when the first segment is the page that owns the position; .field(FieldValidity). If the page does not match, falls back to page-scoped validation for pageId.
  • pageId/fieldPositionId/rowIdrow-level validation for table/collection fields; .row(RowValidity). Use the row’s id from the field value. If the row is missing or not applicable, .notFound.
  • pageId/fieldPositionId/rowId/columnIdcell-level validation for a column in that row; .cell(CellValidity). If the column or row is missing, .notFound.
Surrounding whitespace around the path and next to / is ignored. For .field, you can use the fieldValidity helper on ComponentValidity when convenient.
// Field (any type)
let fieldResult = documentEditor.validate(path: "\(pageId)/\(fieldPositionId)")
if case .field(let fieldValidity) = fieldResult {
    // fieldValidity.status, fieldValidity.rowValidities (tables/collections), etc.
}

// Row (table or collection)
let rowPath = "\(pageId)/\(fieldPositionId)/\(rowId)"
let rowResult = documentEditor.validate(path: rowPath)
if case .row(let rowValidity) = rowResult {
    // Row-level status and rowValidity.cellValidities
}

// Cell (table or collection column in a row)
let cellPath = "\(pageId)/\(fieldPositionId)/\(rowId)/\(columnId)"
let cellResult = documentEditor.validate(path: cellPath)
if case .cell(let cellValidity) = cellResult {
    // Cell-level validation for that column
}
Key Points
  • Call validate() before form submission
  • Check status for overall validation result
  • Use fieldValidities to get specific field errors
  • Required fields must have non-empty values
  • Hidden fields are always filtered out of the validation output (conditional logic and hiddenViews)
  • Page hidden: all of its fields are valid
  • Table/Collection: validate their required columns per row. Each FieldValidity includes rowValidities with row-level and cell-level results (RowValidity and CellValidity)
  • Non-required table/collection fields still validate rows against required columns
  • Navigate to invalid fields: use fieldValidity.pageId and fieldValidity.fieldPositionId with goto() to navigate directly to invalid fields
  • Path-scoped checks: use validate(path:) after a single edit, for one page submit, or with the same path shapes as goto() — including pageId/fieldPositionId/rowId and pageId/fieldPositionId/rowId/columnId for table/collection row and cell validation