feat: prepare Ikokuko 0.2.0 release - #2
Merged
Merged
Conversation
eosobande
commented
Aug 2, 2026
eosobande
left a comment
Contributor
Author
There was a problem hiding this comment.
Reviewed the 0.2.0 changes at 4e46dae.
The review found one lifecycle edge case. Replacing FormState at the same composition position could retain the previous ValidationEffect instances when their explicit keys remained equal. This could leave the old DisposableEffect active and skip validation for the new form.
Commit 4e46dae now keys the form content subtree by FormState. Compose therefore disposes the old form effects and creates fresh validation effects for the new form. The added regression test verifies both old-form disposal and new-form validation.
Local verification passed:
./gradlew :ikokuko:allTests./gradlew :ikokuko:checkgit diff --check
No unresolved material findings remain from this review.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
This PR prepares Ikokuko 0.2.0.
The release updates the public form API from lessons proved in Yetunde's
core:ui:formmodule. It keeps application UI and product-specific validation outside the library.The release includes breaking API cleanup because Ikokuko is still below 1.0. It also adds migration guidance, stronger behavior tests, runnable samples, publication metadata, and a release workflow for Android and iOS.
Motivation
The 0.1.0 API had several limitations:
Form behavior
Field validity
field.isValidis strict. It isfalsewhenever the field has a stored validation or external error.field.errorreturns the stored error without applying UI visibility rules.field.shouldDisplayErroris true only when all these conditions are true:Form validity and submission
FormState.isValidfollows the form's error-reporting state:This behavior prevents initial reactive validation from disabling UI that uses
enabled = isValid.submit()performs these operations in order:onSubmitwhen valid, oronInvalidwhen invalid.After an invalid submit, stored errors disable submit UI. The UI becomes valid again as field changes clear all stored errors.
Applications can pass
shouldShowErrors = truewhen they need stored errors to affect form validity immediately.Error ownership
Validation and external errors share one active error slot for each field name.
The latest write wins. A later validation result can replace an external error, and a later external write can replace a validation error.
Reset clears values, errors, dirty state, and error visibility. It also restarts validation for values that are equal to their prior initial values.
Cross-field validation
Validators can declare the fields that they read:
ValidationScopeexposes read-only field values. It does not expose submit, reset, error mutation, dirty-state mutation, or lifecycle operations.ValidationEffectobserves:FieldEqualsValidatordeclares its compared field as a dependency. A change to that field revalidates the target field.Validator changes
Added
FieldEqualsValidatorCheckedValidatorSelectionRangeValidatorSelectionRangeValidatorsupports minimum, maximum, exact, and unbounded selection counts. A null maximum means that the upper bound is not limited.Simplified
errorMessageas its first constructor argument.toIntOrNull().Regexvalues.Validator.validatereceives a read-onlyValidationScope.Removed
EqualsValidatorNotEqualsValidatorEmailValidatorPhoneNumberValidatorApplications can use
MatchPatternValidatorwith application-owned email and phone patterns. Applications can implement custom validators when they need numeric types other than string-backed integers.Field and state API changes
Field<T>now requiresT : Any.Fieldremains keyed by its name.Field.isDirtyis writable.markAsDirty()is removed.Field.Int,Field.Long, andField.Doubleare removed.ValidationEffectandFormFielduseinitialValueinstead ofdefault.submit(onInvalid)uses a non-null no-op callback by default.rememberSaveableFormState()is added.FormState.saver(valuesSaver)supports custom field-value serialization.The default saver supports field values accepted by the platform save registry. Applications must provide a custom saver for unsupported value types.
Field identity requirement
Each field name must be unique within one form.
Fieldequality uses only the field name. Two fields with the same name share one stored value even when their generic types differ. Reading that value through an incompatible field type can fail at runtime.ValidationEffectdoes not reject duplicate names because it cannot reliably distinguish a second declaration from normal recomposition.Validator equality requirement
The validator list remains a
ValidationEffectkey.Equivalent built-in validator instances compare structurally because they are data classes. Recreating equivalent built-in validators during recomposition does not restart validation.
Custom validators that store lambdas can compare by lambda identity. Recreating those validators can restart validation without a field value change and can replace an external error. Applications should use structurally stable custom validators or remember lambda-backed instances.
Samples and supported targets
Runnable applications now live under
samples/::samples:composeAppsamples/iosAppThe published and documented targets for 0.2.0 are:
Desktop, JavaScript, and Wasm are not part of the 0.2.0 support promise.
Publication and workflow
The release workflow follows the Gbeewa release structure.
It:
release-MAJOR.MINOR.PATCHtag.origin/main.VERSION_NAMEto Gradle.macos-15.macos-15-intelbefore publication.The workflow uses:
actions/checkout@v5actions/setup-java@v5gradle/actions/setup-gradle@v5Breaking-change migration
Field<T?>Field<T>and represent optional input with a non-null application value.Field.Int,Field.Long, orField.DoubleField<T>(name)when a non-text numeric value is required.default = ...initialValue = ....field.markAsDirty()field.isDirty = true.submit(onInvalid)field.errorfor rendering decisionsfield.shouldDisplayErrorfor styling and message visibility.FormState.isValidremains true until error reporting is enabled.Validator<T>withValidationScope.Regexconstructor argumentEmailValidatororPhoneNumberValidatorMatchPatternValidatorwith an application-owned pattern.SelectionRangeValidator.FieldEqualsValidatorfor field equality or implement a custom dependency-aware validator.See
README.mdandCHANGELOG.mdfor the complete public migration notes.Test coverage
The release adds or updates tests for:
Local verification
The complete clean release gate passed:
Result:
iosX64Testas expected.Additional verification passed:
RECOMMEND_ACCEPT.Actual Intel
iosX64Testexecution remains owned by themacos-15-intelrelease job.Release process
This PR does not tag, push a release tag, or publish an artifact.
After this PR is approved and merged:
main.release-0.2.0tag on that commit.Checklist
release-0.2.0tag created and pushed.Need help on this PR? Tag
@codesmith-botwith what you need. Autofix is disabled.