Skip to content

hollyoops/ReactiveForm

Repository files navigation

ReactiveForm

Main workflow codecov

A flexible and extensible forms with easy-to-use validation. This library build for SwiftUI and inspired by Reactive forms of Angular.

Installation

  1. In Xcode, open your project and navigate to FileSwift PackagesAdd Package Dependency...
  2. Paste the repository URL (https://github.com/hollyoops/ReactiveForm.git) and finish import

ReactiveForm is available through CocoaPods. To install it, simply add the following code to your Podfile:

pod 'ReactiveForm'

Usage

Define fields with property wrapper

import SwiftUI
import ReactiveForm

class ProfileForm: ObservableForm {
  @FormField(validators: [.required])
  var name = ""
  
  @FormField(validators: [.required, .email])
  var email = ""
}

struct ContentView: View {
  @StateObject var form = ProfileForm()

  var body: some View {
    Form {
      TextField("Name", text: $form.firstName)
      if form.$name.isInValid {
        Text("Please fill a name.")
          .foregroundColor(.red)
      }
      TextField("Email", text: $form.email)
      if form.$email.isInValid {
        Text("Please fill a valid email")
          .foregroundColor(.red)
      }
    }
  }
}

Customize validator

let stringValidator = Validator { stringValue: String -> Bool
  // some check logic
  return true
}

class ProfileForm: ObservableForm {
  @FormField(validators: [.required, stringValidator])
  var name = ""
}

Creating a form model

You are not a big fan of property wrapper, you can build a form using ObservableForm and FormControl.

class ProfileForm: ObservableForm {
  var name = FormControl("", validators: [.required])
  var email = FormControl("", validators: [.required, .email])
}

Validating manually

For some case, you may need to manually call validate rather than validate on change

class ProfileForm: ObservableForm {
  @FormField("", validators: [.required], type: .manually) 
  var name: String

  @FormField("", validators: [.email], type: .manually) 
  var email: String
}

struct ContentView: View {
  @StateObject var form = ProfileForm()

  var body: some View {
    Form {
      TextField("Name", text: $form.name.pendingValue)
      if form.$name.errors[.required] {
        Text("Please fill a name.")
          .foregroundColor(.red)
      }
      TextField("Email", text: $form.email.pendingValue)
      if form.$email.errors[.email] {
        Text("Please fill a valid email.")
          .foregroundColor(.red)
      }
      Button(action: submit) {
        Text("Submit")
      }
    }
  }

  func submit() {
    // You can call `validate` manually
    form.validate()
    if form.isValid {
      print(form)
    }
  }
}

Use Dirty to check form status

You can use isDirty & isPristine to check where form is edited.

One possible scenario is when page appear you don't want to show error util form is edited.

class SettingsForm: ObservableForm {
  var name = FormControl("", validators: [.required], type: .manually) 

  var email = FormControl("", validators: [.email], type: .manually) 
}

struct ContentView: View {
  @StateObject var form = SettingsForm()

  var body: some View {
    Form {
      TextField("Name", text: $form.name.value)
      if form.name.isDirty && form.name.errors[.required] {
        Text("Please fill a name.")
          .foregroundColor(.red)
      }
      TextField("Email", text: $form.email.value)
      if form.email.isDirty && form.email.errors[.email] {
        Text("Please fill a valid email.")
          .foregroundColor(.red)
      }
      Button(action: submit) {
        Text("Submit")
      }
      .disabled(form.isInvalid)
    }
  }
}

Documentation

Netlify Status

Docs for ReactiveForm.

License

MIT