ObjectForm - a simple yet powerful library to build form for your class models.

Related tags

Form ObjectForm
Overview

ObjectForm

ObjectForm

A simple yet powerful library to build form for your class models.

Motivations

I found most form libraries for swift are too complicated to bootstrap a simple project. So I write ObjectForm to make it dead simple for building forms and binding model classes.

ObjectForm doesn't fight with you to write UIKit code. By design, it is simple to understand and extend. If you follow the example project carefully, you would find it easy to fit in your Swift project.

This project has no dependency of any other library.

Application

In my application (a personal finance app), I use ObjectForm to make forms for multiple classes as well as different variants for the same class, which saves me from writing duplicate code.

Features

  • Bind class model to form rows
  • Automatic keyboards types according to model types
  • Form rows are type safe
  • Fully customizable form validation rules
  • A list of built-in UITableViewCell to support multiple types
  • Work amazingly well with your UITableView code

Requirements

  • iOS >= 11.0

Usage

  1. Copy sources
  • Copy files under /Sources into your project.
  1. Carthage (Coming soon)
  2. Swift Package Manager

Available Rows & Cells

Rows

  • StringRow: Row to support string input, full keyboard
  • DoubleRow: Row to support number input, numeric keyboard
  • DateRow: Row to bind Date value

Cells

  • TextViewInputCell: text input cell
  • SelectInputCell: support selection, provided by CollectionPicker
  • TextViewVC: A view controller with UITextView to input long text
  • ButtonCell: Show a button in the form
  • TypedInputCell: Generic cell to support type binding
  • FormInputCell: The base class for all cells

Tutorials

You can follow ObjectFormExample in ObjectFormExample to learn how to build a simple form with a class model.

Binding Model to Form

class FruitFormData: NSObject, FormDataSource {
      // Bind your custom model
      typealias BindModel = Fruit
      var basicRows: [BaseRow] = []

      func numberOfSections() -> Int {...}
      func numberOfRows(at section: Int) -> Int {...}
      func row(at indexPath: IndexPath) -> BaseRow {...}

      self.bindModel = fruit

      basicRows.append(StringRow(title: "Name",
                                 icon: "",
                                 kvcKey: "name",
                                 value: fruit.name ?? "",
                                 placeholder: nil,
                                 validator: nil))

      // Row are type safe
      basicRows.append(DoubleRow(title: "Price",
                                 icon: "",
                                 kvcKey: "price",
                                 value: fruit.price,
                                 placeholder: "",
                                 validator: nil))

      // You can build as many rows as you want
      basicRows.append(TextViewRow(title: "Note",
                                   kvcKey: "note",
                                   value: fruit.note ?? "-"))

  }
}

Showing FormDataSource in a UITableView

class FruitFormVC: UIViewController {
  private let dataSource: FruitFormData
}

extension FruitFormVC: UITableViewDataSource {
    func numberOfSections(in tableView: UITableView) -> Int {
        return dataSource.numberOfSections()
    }

    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        dataSource.numberOfRows(at: section)
    }

    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {

        let row = dataSource.row(at: indexPath)
        row.baseCell.setup(row)
        row.baseCell.delegate = self
        return row.baseCell
    }
}

Listening to Cell Value Change

extension FruitFormVC: FormCellDelegate {
    func cellDidChangeValue(_ cell: UITableViewCell, value: Any?) {
        let indexPath = tableView.indexPath(for: cell)!
        _ = dataSource.updateItem(at: indexPath, value: value)
    }
}

Data Validation

By providing a validation block when building a row, you can provide any validaiton rules.

basicRows.append(StringRow(title: "Name",
                           icon: "",
                           kvcKey: "name",
                           value: fruit.name ?? "",
                           placeholder: nil,
                           validator: {
                           // Custom rules for row validation
                            return !(fruit.name?.isEmpty ?? true)

}))
@objc private func saveButtonTapped() {
    guard dataSource.validateData() else {
        tableView.reloadData()
        return
    }

    navigationController?.popViewController(animated: true)
}

Row Type Safety

Since a form row use key-value-coding to update its bind model, it is important to keep the row value type the same as the object's variable type. ObjectForm enforces type safe. Every row must implement the following method:

open override func isValueMatchRowType(value: Any) -> Bool

This is already implemented by built-in generic rows, for example, TypedRow and SelectRow.

Make Your Own Row

Making your own row and cell is easy. You have 2 options:

  1. Create concrete type using TypedRow
typealias StringRow = TypedRow<String>
  1. Subclass BaseRow
\(title ?? "")" } required init(title: String, kvcKey: String, value: String?) { self.cell = TextViewInputCell() super.init() self.title = title self.kvcKey = kvcKey self.value = value self.placeholder = nil } } ">
class TextViewRow: BaseRow {

    public override var baseCell: FormInputCell {
        return cell
    }

    public override var baseValue: CustomStringConvertible? {
        get { return value }
        set { value = newValue as? String }
    }

    var value: String?

    var cell: TextViewInputCell

    open override func isValueMatchRowType(value: Any) -> Bool {
        let t = type(of: value)
        return String.self == t
    }

    override var description: String {
        return " \(title ?? "")"
    }

    required init(title: String, kvcKey: String, value: String?) {
        self.cell = TextViewInputCell()

        super.init()

        self.title = title
        self.kvcKey = kvcKey
        self.value = value
        self.placeholder = nil
    }
}

Do not subclass built-in rows and cells (indeed it is not possible because they are not open classes), because they are subject for change.

Instead, use them as a template to create your own version.

Comments
  • Mark Row and Cell classes as open so that they can be subclassed

    Mark Row and Cell classes as open so that they can be subclassed

    The classes in question were marked as public, which is not a problematic when the package is imported directly into a project.

    When the package is installed using a package manager (SPM or other) these classes couldn't be subclassed because of the public access level. Marking them as open solves this problem.

    Fixes #13

    opened by RobertAudi 2
  • Row classes cannot be subclassed when installing with the Swift Package Manager

    Row classes cannot be subclassed when installing with the Swift Package Manager

    When trying to subclass a row class Xcode throws an error:

    class CustomRow: BaseRow { /* */ }
    // Error: Cannot inherit from non-open class 'BaseRow' outside of its defining module
    

    This is because the row classes are defined as public instead of open.

    The problem shouldn't exist when the source files are dragged into a project though.

    opened by RobertAudi 2
  • Re-structure files and support Swift Package Manager

    Re-structure files and support Swift Package Manager

    • Put ObjectForm's source to Sources/ObjectForm
    • Put example project to Examples/ObjectFormExample
    • Make ObjectForm a Swift package
    • Import ObjectForm through SPM in example project
    • Make types/properties/methods public in ObjectForm to run example project

    Related issue: #3

    opened by li-bei 1
  • [Prototype] DO NOT MERGE: Move BaseRow to protocol

    [Prototype] DO NOT MERGE: Move BaseRow to protocol

    Make this PR to play with new Apple APIs.

    Using Swift 5.7 new features, we now can migrate BaseRow from a base class to protocol with any keywords.

    image

    Please build this branch with Xcode 14 beta, because building with Xcode 13 will generate this error because it is new swift feature:

    image
    opened by haojianzong 1
  • Suggestion about FormDataSource

    Suggestion about FormDataSource

    I think FormDataSource should be a concrete class that implements UITableViewDataSource, just like what UITableViewDiffableDataSource does. In this way, developers can use FormDataSource directly:

    let fruit: Fruit
    let rows: [[BaseRow]]
    let dataSource = FormDataSource(model: fruit, rows: rows)
    self.tableView.dataSource = dataSource
    

    instead of typing lots of glue code, such as in example:

    extension FruitFormVC: UITableViewDataSource {
        func numberOfSections(in tableView: UITableView) -> Int {
            return dataSource.numberOfSections()
        }
    
        func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
            dataSource.numberOfRows(at: section)
        }
    
        func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
    
            let row = dataSource.row(at: indexPath)
            row.baseCell.setup(row)
            row.baseCell.delegate = self
            return row.baseCell
        }
    }
    
    opened by li-bei 3
Releases(1.3.1)
Owner
jakehao
Outlook is hiring @suzhou. Feel free to drop me an email for discussions: hi(at)jakehao.com
jakehao
Former is a fully customizable Swift library for easy creating UITableView based form.

Former is a fully customizable Swift library for easy creating UITableView based form. Submitting Issues Click HERE to get started with filing a bug r

Ryo Aoyama 1.3k Dec 27, 2022
Elegant iOS form builder in Swift

Made with ❤️ by XMARTLABS. This is the re-creation of XLForm in Swift. 简体中文 Overview Contents Requirements Usage How to create a Form Getting row valu

xmartlabs 11.6k Jan 1, 2023
Some cells to Form a Pod

CellsGao Example To run the example project, clone the repo, and run pod install from the Example directory first. Requirements Installation CellsGao

null 0 Nov 2, 2021
Custom Field component with validation for creating easier form-like UI from interface builder.

#YALField Custom Field component with validation for creating easier form-like UI from interface builder. ##Example Project To run the example project

Yalantis 476 Sep 1, 2022
Declarative form building framework for iOS

Formalist Swift framework for building forms on iOS Formalist is a Swift framework for building forms on iOS using a simple, declarative, and readable

Seed 159 May 25, 2022
iOS validation framework with form validation support

ATGValidator ATGValidator is a validation framework written to address most common issues faced while verifying user input data. You can use it to val

null 51 Oct 19, 2022
SherlockForms - An elegant SwiftUI Form builder to create a searchable Settings and DebugMenu screens for iOS

??️‍♂️ SherlockForms What one man can invent Settings UI, another can discover i

Yasuhiro Inami 98 Dec 27, 2022
APValidators - Codeless solution for form validation in iOS!

APValidators is a codeless solution for form validation. Just connect everything right in Interface Builder and you're done. Supports really complex and extendable forms by allowing to connect validators in tree.

Alty 131 Aug 16, 2022
XLForm is the most flexible and powerful iOS library to create dynamic table-view forms. Fully compatible with Swift & Obj-C.

XLForm By XMARTLABS. If you are working in Swift then you should have a look at Eureka, a complete re-design of XLForm in Swift and with more features

xmartlabs 5.8k Jan 6, 2023
Easily validate your Properties with Property Wrappers 👮

ValidatedPropertyKit enables you to easily validate your properties with the power of Property Wrappers. struct LoginView: View { @Validated(

Sven Tiigi 873 Dec 22, 2022
Simple ui check box

CheckBox Example To run the example project, clone the repo, and run pod install from the Example directory first.

Jamal Alaayq 4 Jan 8, 2020
Carbon🚴 A declarative library for building component-based user interfaces in UITableView and UICollectionView.

A declarative library for building component-based user interfaces in UITableView and UICollectionView. Declarative Component-Based Non-Destructive Pr

Ryo Aoyama 1.2k Jan 5, 2023
SwiftForms is a small and lightweight library written in Swift that allows you to easily create forms.

SwiftForms is a powerful and extremely flexible library written in Swift that allows to create forms by just defining them in a couple of lines. It also provides the ability to customize cells appearance, use custom cells and define your own selector controllers.

Miguel Ángel Ortuño 1.3k Dec 27, 2022
A rule-based validation library for Swift

SwiftValidator Swift Validator is a rule-based validation library for Swift. Core Concepts UITextField + [Rule] + (and optional error UILabel) go into

null 1.4k Dec 29, 2022
iOS Validation Library

Honour Validation library for iOS inspired by Respect/Validation. Validator.mustBe(Uppercase()).andMust(StartsWith("F")).validate("FOOBAR") ❗ If you w

Jean Pimentel 55 Jun 3, 2021
The most flexible and powerful way to build a form on iOS

The most flexible and powerful way to build a form on iOS. Form came out from our need to have a form that could share logic between our iOS apps and

HyperRedink 32 Aug 15, 2022
Decodable Simple and strict, yet powerful object mapping made possible by Swift 2's error handling.

Decodable Simple and strict, yet powerful object mapping made possible by Swift 2's error handling. Greatly inspired by Argo, but without a bizillion

Johannes Lund 1k Jul 15, 2022
A fast & simple, yet powerful & flexible logging framework for Mac and iOS

CocoaLumberjack CocoaLumberjack is a fast & simple, yet powerful & flexible logging framework for macOS, iOS, tvOS and watchOS. How to get started Fir

null 12.9k Jan 9, 2023
Simple yet powerful, open-source SpaceX launch tracker

SpaceX GO! Simple yet powerful, open-source SpaceX launch tracker About the project The purpose of this project is to develop the ultimate SpaceX expe

Jesús Rodríguez 786 Jan 2, 2023
A simple to use yet very powerful tool for the tech heads looking for daily dose of tech news

A simple to use yet very powerful tool for the tech heads looking for daily dose of tech news. This app is built using SwiftUI and also uses catalyst to run on MacOs as well.

Dishant Nagpal 1 Feb 27, 2022