{"id":27076155,"url":"https://github.com/didisouzacosta/formidable","last_synced_at":"2025-07-30T10:37:01.563Z","repository":{"id":274459073,"uuid":"922973796","full_name":"didisouzacosta/Formidable","owner":"didisouzacosta","description":"The Formidable protocol is designed for objects that manage forms composed of multiple FormField components. By conforming to this protocol, you can leverage built-in functionality to validate, reset, and check the validity of all form fields at once.","archived":false,"fork":false,"pushed_at":"2025-03-28T13:39:54.000Z","size":9347,"stargazers_count":12,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-03-28T14:22:59.190Z","etag":null,"topics":["field","form","spm","swift","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/didisouzacosta.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,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2025-01-27T12:30:53.000Z","updated_at":"2025-03-28T13:39:58.000Z","dependencies_parsed_at":"2025-01-27T13:43:33.677Z","dependency_job_id":"1923b679-f8d5-488d-9b20-56199e3e7c10","html_url":"https://github.com/didisouzacosta/Formidable","commit_stats":null,"previous_names":["didisouzacosta/formidable"],"tags_count":1,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/didisouzacosta%2FFormidable","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/didisouzacosta%2FFormidable/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/didisouzacosta%2FFormidable/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/didisouzacosta%2FFormidable/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/didisouzacosta","download_url":"https://codeload.github.com/didisouzacosta/Formidable/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":247419635,"owners_count":20936009,"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":["field","form","spm","swift","swiftui","validation"],"created_at":"2025-04-06T00:28:51.223Z","updated_at":"2025-04-06T00:28:52.043Z","avatar_url":"https://github.com/didisouzacosta.png","language":"Swift","funding_links":[],"categories":[],"sub_categories":[],"readme":"\u003cp align=\"center\"\u003e\n  \u003cimage src=\"app-icon.png\" width=\"200\" /\u003e\n\u003c/p\u003e\n\n\u003ch1 align=\"center\"\u003eFormidable\u003c/h1\u003e\n\n### What is Formidable?\n\n`Formidable` is a protocol designed for objects that manage forms composed of multiple `FormField` components. It provides built-in functionality for validating, resetting, and checking the validity of form fields.\n\nhttps://github.com/user-attachments/assets/2aab4a21-b7ff-4526-a73b-7bba02a7f070\n\n---\n\n### Requirements\n\n| Platform                                             | Minimum Swift Version | \n| ---------------------------------------------------- | --------------------- |\n| iOS 17.0+ / macOS 15.0+ / tvOS 17.0+ | Swift 5 / Xcode 15.0\n\n#### Instalation\n\n### Swift Package Manager\n\n---\n\n\n```swift\ndependencies: [\n    .package(url: \"https://github.com/didisouzacosta/Formidable\", .upToNextMajor(from: \"1.0.0\"))\n]\n```\n\n---\n\n### Key Features\n- **Validation**: The `validate()` method checks all fields and throws an error if any fail validation.\n- **Reset**: The `reset()` method restores all fields to their original values.\n- **Validity Check**: The `isValid` computed property determines whether all fields are valid.\n- **Error Aggregation**: The `errors` computed property collects validation errors for easy handling.\n- **Enable State**: The `isEnabled` property checks whether all fields are enabled.\n\nBy adopting `Formidable`, you can create structured forms with reliable state management and validation.\n\n---\n\n### FormField\n\nThe `FormField` class represents a form field, managing its value, validation rules, and change tracking.\n\n### Public Properties\n- **`isHidden`**: Hides the field from the UI (default: `false`).\n- **`isDisabled`**: Disables editing (default: `false`).\n- **`rules`**: An array of validation rules (`FormFieldRule` conforming types).\n- **`transform`**: An optional closure that modifies the value before retrieval.\n- **`valueChanged`**: A closure triggered when the value changes.\n- **`showErrors`**: Controls whether validation errors should be displayed.\n- **`originalValue`**: Stores the initial value for reset purposes.\n- **`value`**: Holds the current field value, applying transformations if set.\n\n---\n\n### Validation Rules\n\nValidation rules define the conditions a field must meet to be valid. Common examples include:\n\n- **`GreaterThanRule`**: Ensures a field's value is greater than a specified number.\n- **`LessThanRule`**: Ensures a field's value is less than a specified number.\n- **`RequiredRule`**: Ensures a field is not empty.\n- **`EqualRule`**: Ensures a field matches a specific value.\n\nFor instance, you can require an age field to be greater than 18.\n\n---\n\n### Example Usage\n\n```swift\nimport SwiftUI\nimport Formidable\n\nenum ValidationError: LocalizedError {\n    case isRequired\n    case ageTooLow\n\n    var errorDescription: String? {\n        switch self {\n        case .isRequired: return \"This field cannot be left empty.\"\n        case .ageTooLow: return \"You need to be of legal age.\"\n        }\n    }\n}\n\n@Observable\nfinal class UserForm: Formidable {\n    \n    // MARK: - Public Properties\n    \n    var nameField: FormField\u003cString\u003e\n    var ageField: FormField\u003cInt\u003e\n    \n    // MARK: - Initialization\n    \n    init(_ name: String, age: Int) {\n        nameField = FormField(name)\n        ageField = FormField(age)\n        \n        defer {\n            setupRules()\n        }\n    }\n    \n    // MARK: - Public Methods\n    \n    func submit() throws -\u003e (name: String, age: Int) {\n        try validate()\n        return (name: nameField.value, age: ageField.value)\n    }\n    \n    // MARK: - Private Methods\n    \n    private func setupRules() {\n        nameField.rules = [RequiredRule(ValidationError.isRequired)]\n        ageField.rules = [GreaterThanRule(18, error: ValidationError.ageTooLow)]\n    }\n}\n\nstruct UserFormView: View {\n    \n    @State private var form = UserForm(\"\", age: 0)\n    \n    var body: some View {\n        NavigationStack {\n            Form {\n                Section {\n                    TextField(\"Name\", text: $form.nameField.value)\n                        .field($form.nameField)\n                    \n                    Picker(\"Age\", selection: $form.ageField.value) {\n                        ForEach([10, 18, 36], id: \\.self) { age in\n                            Text(\"\\(age)\")\n                        }\n                    }\n                    .field($form.ageField)\n                }\n            }\n            .navigationTitle(\"User\")\n            .toolbar {\n                ToolbarItemGroup {\n                    Button(action: reset) {\n                        Text(\"Reset\")\n                    }\n                    .disabled(form.isDisabled)\n                    \n                    Button(action: save) {\n                        Text(\"Save\")\n                    }\n                }\n            }\n            .onAppear {\n                UITextField.appearance().clearButtonMode = .whileEditing\n            }\n        }\n    }\n    \n    // MARK: - Private Methods\n    \n    private func reset() {\n        form.reset()\n    }\n    \n    private func save() {\n        do {\n            let data = try form.submit()\n            print(data)\n        } catch {\n            print(error)\n        }\n    }\n}\n\n#Preview {\n    UserFormView()\n}\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdidisouzacosta%2Fformidable","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fdidisouzacosta%2Fformidable","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdidisouzacosta%2Fformidable/lists"}