{"id":15055342,"url":"https://github.com/hollyoops/reactiveform","last_synced_at":"2025-10-15T07:48:22.817Z","repository":{"id":56921689,"uuid":"441053982","full_name":"hollyoops/ReactiveForm","owner":"hollyoops","description":"A simple and validatable form for Swift and SwiftUI","archived":false,"fork":false,"pushed_at":"2022-03-12T16:22:14.000Z","size":78,"stargazers_count":41,"open_issues_count":0,"forks_count":2,"subscribers_count":3,"default_branch":"main","last_synced_at":"2024-04-14T13:04:42.566Z","etag":null,"topics":["combine","form","reactive","swift5","swiftui","validation"],"latest_commit_sha":null,"homepage":"","language":"Swift","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/hollyoops.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2021-12-23T03:46:01.000Z","updated_at":"2023-12-10T05:33:52.000Z","dependencies_parsed_at":"2022-08-21T04:50:46.635Z","dependency_job_id":null,"html_url":"https://github.com/hollyoops/ReactiveForm","commit_stats":null,"previous_names":[],"tags_count":2,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/hollyoops%2FReactiveForm","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/hollyoops%2FReactiveForm/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/hollyoops%2FReactiveForm/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/hollyoops%2FReactiveForm/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/hollyoops","download_url":"https://codeload.github.com/hollyoops/ReactiveForm/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248154761,"owners_count":21056541,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["combine","form","reactive","swift5","swiftui","validation"],"created_at":"2024-09-24T21:41:02.101Z","updated_at":"2025-10-15T07:48:22.750Z","avatar_url":"https://github.com/hollyoops.png","language":"Swift","funding_links":[],"categories":[],"sub_categories":[],"readme":"# ReactiveForm\n\n[![Main workflow](https://github.com/hollyoops/ReactiveForm/workflows/Main/badge.svg)](https://github.com/hollyoops/ReactiveForm/actions/workflows/main.yml) [![codecov](https://codecov.io/gh/hollyoops/ReactiveForm/branch/main/graph/badge.svg?token=0X34NQ63HK)](https://codecov.io/gh/hollyoops/ReactiveForm)\n\nA flexible and extensible forms with easy-to-use validation. This library build for `SwiftUI` and inspired by [Reactive forms of Angular](https://angular.io/guide/reactive-forms).\n\n## Installation\n\n- [**Swift Package Manager**](https://swift.org/package-manager/)\n\n1. In Xcode, open your project and navigate to **File** → **Swift Packages** → **Add Package Dependency...**\n2. Paste the repository URL (`https://github.com/hollyoops/ReactiveForm.git`) and finish import\n\n- [**CocoaPods**](https://cocoapods.org) \n\nReactiveForm is available through CocoaPods. To install it, simply add the following code to your Podfile:\n\n```ruby\npod 'ReactiveForm'\n```\n\n## Usage\n\n### Define fields with property wrapper\n\n```swift\nimport SwiftUI\nimport ReactiveForm\n\nclass ProfileForm: ObservableForm {\n  @FormField(validators: [.required])\n  var name = \"\"\n  \n  @FormField(validators: [.required, .email])\n  var email = \"\"\n}\n\nstruct ContentView: View {\n  @StateObject var form = ProfileForm()\n\n  var body: some View {\n    Form {\n      TextField(\"Name\", text: $form.firstName)\n      if form.$name.isInValid {\n        Text(\"Please fill a name.\")\n          .foregroundColor(.red)\n      }\n      TextField(\"Email\", text: $form.email)\n      if form.$email.isInValid {\n        Text(\"Please fill a valid email\")\n          .foregroundColor(.red)\n      }\n    }\n  }\n}\n```\n\n### Customize validator\n\n```swift\nlet stringValidator = Validator { stringValue: String -\u003e Bool\n  // some check logic\n  return true\n}\n\nclass ProfileForm: ObservableForm {\n  @FormField(validators: [.required, stringValidator])\n  var name = \"\"\n}\n```\n\n### Creating a form model\n\nYou are not a big fan of property wrapper, you can build a form using ``ObservableForm`` and ``FormControl``.\n\n```swift\nclass ProfileForm: ObservableForm {\n  var name = FormControl(\"\", validators: [.required])\n  var email = FormControl(\"\", validators: [.required, .email])\n}\n```\n\n### Validating manually\n\nFor some case, you may need to manually call `validate` rather than validate on change\n\n\n```swift\nclass ProfileForm: ObservableForm {\n  @FormField(\"\", validators: [.required], type: .manually) \n  var name: String\n\n  @FormField(\"\", validators: [.email], type: .manually) \n  var email: String\n}\n\nstruct ContentView: View {\n  @StateObject var form = ProfileForm()\n\n  var body: some View {\n    Form {\n      TextField(\"Name\", text: $form.name.pendingValue)\n      if form.$name.errors[.required] {\n        Text(\"Please fill a name.\")\n          .foregroundColor(.red)\n      }\n      TextField(\"Email\", text: $form.email.pendingValue)\n      if form.$email.errors[.email] {\n        Text(\"Please fill a valid email.\")\n          .foregroundColor(.red)\n      }\n      Button(action: submit) {\n        Text(\"Submit\")\n      }\n    }\n  }\n\n  func submit() {\n    // You can call `validate` manually\n    form.validate()\n    if form.isValid {\n      print(form)\n    }\n  }\n}\n```\n\n### Use Dirty to check form status\n\nYou can use `isDirty` \u0026 `isPristine` to check where form is edited. \n\nOne possible scenario is when page appear you don't want to show error util form is edited.\n\n```swift\nclass SettingsForm: ObservableForm {\n  var name = FormControl(\"\", validators: [.required], type: .manually) \n\n  var email = FormControl(\"\", validators: [.email], type: .manually) \n}\n\nstruct ContentView: View {\n  @StateObject var form = SettingsForm()\n\n  var body: some View {\n    Form {\n      TextField(\"Name\", text: $form.name.value)\n      if form.name.isDirty \u0026\u0026 form.name.errors[.required] {\n        Text(\"Please fill a name.\")\n          .foregroundColor(.red)\n      }\n      TextField(\"Email\", text: $form.email.value)\n      if form.email.isDirty \u0026\u0026 form.email.errors[.email] {\n        Text(\"Please fill a valid email.\")\n          .foregroundColor(.red)\n      }\n      Button(action: submit) {\n        Text(\"Submit\")\n      }\n      .disabled(form.isInvalid)\n    }\n  }\n}\n```\n\n## Documentation\n\n[![Netlify Status](https://api.netlify.com/api/v1/badges/7593e5fb-308e-4a64-9947-8267a9f7a556/deploy-status)](https://app.netlify.com/sites/frosty-yonath-df6e87/deploys)\n\n[Docs for ReactiveForm](https://frosty-yonath-df6e87.netlify.app/documentation/).\n\n## License\n\n[MIT](LICENSE)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fhollyoops%2Freactiveform","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fhollyoops%2Freactiveform","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fhollyoops%2Freactiveform/lists"}