Repository navigation
Form validation, gestures and a contrast checker, merged from three of the author's packages
that are being archived: SwiftUI-Forms-Kit, SwiftUI-Gesture-Library and iOSUIComponents. The
code was rewritten for this package's style and tests rather than copied. No existing API
changed in a breaking way.
Added
- Validation:
Validatorwithrequired,minLength,maxLength,email,url,phone,
digits,matches,equals,range,atLeast,atMost,notEmpty,count,custom
and the combinatorsand,orandall;ValidationResult;ValidatedValue, which shows
an error only after an edit or a submit;AsyncValidation, a debounced check that ignores
answers for outdated values. FormSchema,FormValueandSchemaForm: forms described in JSON, checked on decoding
(duplicate ids, choices without options, impossible limits, invalid regular expressions,
more fields, patterns or options than the size limits allow) and rendered with the package's
fields. Every regular expression, in a schema or inValidator.matches, runs on
NSRegularExpressionwith a 20 ms limit per match, a 1,000 UTF-16 code unit input limit and a
100 ms budget pererrors(in:)call.StepFlowandMultiStepFormfor forms split into steps.- Gestures:
.onSwipe,.onSwipeSequence,.onEdgeSwipe,.transformable(pinch, rotate,
drag and double-tap reset),.boundedDrag,ShapeDrawingPadandUnistrokeRecognizer
(the $1 recognizer with $N's handling of straight lines), with the testable rules
SwipeDirection,SwipeSequenceMatcher,EdgeSwipe,TransformStateandDragBounds.
Each gesture has an accessibility action. ColorContrast: WCAG 2 contrast ratio and AA/AAA level for SwiftUI colors and hex codes.FileUploadField.UploadedFile.url, publicname,sizeandtype, the
FileUploadField.Rejectionreasons and anonRejectedcallback.- Example app screens for the validated form, the JSON form and the gestures; a
-sample
launch argument that opens one of them;scripts/capture-example-screens.sh, which captures
them on a simulator for the README; and the DocC articles "Validating Forms" and "Adding
Gestures".
Changed
OTPField,SecurePINFieldandPinInputkeep only ASCII digits (or ASCII letters and
digits forPinInput(inputType: .alphanumeric)) and filter pasted text before cutting it
to length, so"123-456"fills all six cells.CreditCardInput's cardholder field prompts for "Name on card" instead of showing the sample
name "John Doe".
Fixed
FileUploadFieldadded made-up demo files instead of opening a file picker. It now opens
the system file importer, accepts dropped files and enforcesallowedTypes,maxFileSize
andmaxFiles.PinInputwith.numericinput undid its own length limit, so pasted text longer than
lengthstayed in the binding andonCompletenever ran;OTPFieldandSecurePINField
could callonCompletetwice for one paste.PinInput(isSecure: true)masked the boxes but kept the PIN in a plain text field, where
keyboard suggestions and VoiceOver could still reach it. It now uses a secure field, as
SecurePINFielddoes.- NaN, infinite or very large values no longer stop the app in
ProgressCard,
ProgressRingChart,CountdownTimer,Stopwatch,ProgressTimer,DurationPicker,
ReorderableList, the toast, snackbar and banner durations,LoadingButtonand
VerticalMarqueeText. A zero or negative interval no longer makes a repeating animation
spin. - Zero or negative counts no longer stop the app in
DotLoader,SkeletonView,
PageIndicator,ScrollingPageIndicator,CheckboxGroup,TimeSlotPicker,StatGrid,
OTPField,SecurePINField,PinInputandPasscodeInput. - A
maxRatingbelow 1 inRatingInputandRatingDisplay, and amaxItemsormaxVisible
below 3 inBreadcrumbViewandDropdownBreadcrumb, no longer stop the app; they are raised
to those minimums. - A negative
maxVisibleinAvatarGroupormaxPointsinLiveSparklineChartno longer
stops the app; it is treated as 0. ReadingTimeTextwith awordsPerMinuteof 0 divided by zero; the speed is now at least 1.- A
stepthat is zero, negative, NaN, infinite or larger than the range no longer stops the
app inSliderFieldandSliderRatingInput, includingSliderRatingInput(range: 5...5)with
its default step; the slider then moves without steps. The same happens for a range that is
not finite or holds more than 10,000 steps, such as0...1e15with a step of 1, which tried
to allocate memory for every step. The pill and gradientSliderFieldstyles count steps
from the lower bound and stay inside the range. PageIndicator's.numbersand.progressstyles stopped the app for acurrentPageof
Int.max; the page number shown is now kept between 0 and the page count.StepperButtonstopped the app when a step passedInt.maxorInt.min, for example a
VoiceOver increment on a range ending atInt.max; such a step now leaves the value
unchanged. A negativestepno longer swaps what the two buttons do.TexturedGlassCarddrew one noise dot per ten square points without limit; it now draws at
most 4,000.