Overview

This document provides an overview of the ShinobiForms control. It describes the features of the control and its associated concepts.

ShinobiForms provides a quick and effective way to capture your user’s data in an iOS application. Once your user has entered their data into the fields in a form, the control allows you to access that data.

If you simply want to get up and running, follow the Quick Start Guide, alternatively, for a more detailed description of how forms work and the features they present, head over to the ShinobiForms Control Overview.

Quick Start Guide

Introduction

This is a brief introduction to using the ShinobiForms component. This quick start guide will walk you through a series of simple steps introducing the key features of the library; forms, sections, fields, and validation.

At the end of this guide you will have created the following form:

Getting Started Image

Installation

ShinobiForms ships with an installer to make it easier to get started. To run the installer, open the zipped file which you downloaded from ShinobiControls and run ‘install.pkg’. When it opens up, it should look as below.

Installer Image

The easiest way to install the ShinobiForms framework is to run the ‘install.pkg’ file. This will install the framework into Xcode for you, along with the framework documentation. This means you can add the framework to your project in the same way as you would any of the frameworks which are shipped with Xcode.

If you don’t want to run the installer, the framework is also contained within the ShinobiForms folder in the disk image. Regardless of whether you ran the installer, you should copy this folder onto your machine to save the samples and documentation. Drag the ShinobiForms folder onto the Desktop icon in the disk image. This will copy the folder onto your desktop.

The ‘ShinobiForms’ folder contains:

  • A copy of the framework.
  • A copy of the documentation for the framework.
  • A set of samples to demonstrate getting started with ShinobiForms.
  • An uninstall script for uninstalling the ShinobiForms framework & documentation from Xcode.
  • A README file with setup steps.
  • A change log stating the changes made in each release.
  • A copy of the ShinobiForms Standard License.
  • A text file containing the version number of the framework.

Creating a Simple Form

Start up Xcode and create a new project via File / New / Single View Application

Within your newly created project add a reference to the ShinobiForms framework. If you’ve installed ShinobiForms using our installer, you can add this in the same way as you would any of the standard Apple frameworks. Select your project target, and switch to the Build Phases tab. Open the Link Binary With Libraries section, click the Plus button, find the entry for ShinobiForms.framework, and click Add.

If you have just copied the framework onto your machine, the easiest way to add it to your project is to locate the ShinobiForms.framework and drag it directly into your project.

ShinobiForms makes use of a few other frameworks, so add the following as well:

  • Security.framework (Trial Version only)
  • QuartzCore.framework
  • libxml.2.2.dylib

You can follow along with GettingStarted.xcodeproj in the Samples/ folder of your framework download. The first step is getting your view controller ready to be able to access the ShinobiForm classes. Open up the ViewController.m file and add the following import statement.

#import <ShinobiForms/ShinobiForms.h>

Further down the same file add a class extension and a property for the form.

@interface ViewController ()

@property (nonatomic, strong) ShinobiForm *form;

@end

If you’ve downloaded a trial version of ShinobiForms you will have been issued with a trial license key. Add the key that was supplied to your viewDidLoad method like so.

//Trial users: Add your license key here!
//[ShinobiForms setLicenseKey:@""];

You’re now ready to start building your forms model. Now we’re going to create some field models with titles.

// Create some field models.
SFormTextField *name = [[SFormTextField alloc] initWithTitle:@"Name"];
SFormDateField *birthDate = [[SFormDateField alloc] initWithTitle:@"Birth Date"];
SFormPickerField *pickerField = [[SFormPickerField alloc] initWithTitle:@"Picker"];

You’ll need to do some further set up for some field types. Let’s add some choices to the picker field.

pickerField.options = @[@[@"A", @"B", @"C"]];

Now we’ve created our fields, we need to create a section model to put them in.

// Create a section model.
SFormSection *section = [[SFormSection alloc] initWithFields:@[name, birthDate, pickerField]];
section.title = @"My Form";

The next step is to create a form model to add the sections to.

// Create the form model.
self.form = [ShinobiForm new];
self.form.sections = @[section];

Now we’ve got our model set up, we need to generate our view. You could do this yourself, but the easiest way is to use one of our view builders. Since we’re building the form, let’s use our form view builder.

// Build the views.
SFormView *formView = [[SFormFormViewBuilder new] buildViewFromModel:self.form];

At this point we’ve now got our form view, section view, and field views. These have been automatically generated and sized for us. All you need to do now is position the form and add it to your view hierarchy!

formView.center = self.view.center;
[self.view addSubview:formView];

If you run the sample application you should now see something like the image shown at the beginning of this quick start guide. As your users interact with your field views, your model objects will be updated to contain those values. If you modify your model object, your views will be updated to represent them - the model stays in sync with your views! (Note that the model will not update to programatic modification of your views.)

You’ve now learned how to set up a simple ShinobiForm, congratulations! If you got stuck at all, take a look at the corresponding sample - GettingStarted.xcodeproj.

ShinobiForms Control Overview

Models

Form model objects can exist independently of a view, although model objects will need a view to represent them if you are to collect data from the users of your application (see Views). The fact that model objects can exist without views means you can store them, create them, and pull information from them without caring about the view hierarchy.

ShinobiForm

A ShinobiForm is a model object of a form. A form is a collection of fields that are conceptually linked composing a single unit of input for submission. The model needs to be represented by a view so it can be added to your view hierarchy for user interaction (see SFormView). A form has multiple SFormSection model objects that represent groupings of SFormFields within the form.

Adding Sections

You can add sections to a ShinobiForm by assigning an NSArray of SFormSection objects to the sections property.

ShinobiForm *form = [ShinobiForm new];

SFormSection *section = [SFormSection new];
form.sections = @[section];

Submitting Data from The Form

A ShinobiForm supports submitting its data to a delegate. To do this, set the delegate property on the form which can be an instance of any class which adopts the SFormDelegate protocol. Then, call the submit method on the form. This method will validate the form and then notify the delegate.

To add a submit button to your form see the section Adding a Submit Button.

SFormSection

An SFormSection is a model object of a section. A section is a collection of fields that are grouped and laid out separately to fields in other sections. The model needs to be represented by a view so it can be added to your view hierarchy for user interaction (see SFormSectionView).

Adding Fields

You can add fields to an SFormSection by assigning an NSArray of SFormField objects to the fields property.

SFormField *field = [SFormTextField new];
section.fields = @[field];

SFormField

An SFormField is a model object of a field. The model needs to be represented by a view so it can be added to your view hierarchy for user interaction (see SFormFieldView).

A field object holds a value of a specific type. This type depends on the type of field you are using - check the specific fields documentation for further information. Setting the value on a field will cause the validators contained with the field’s validators array to validate the field. This will in turn update the field’s isValid status. Any failing validators will be added to the currentlyFailingValidators array providing an easy way to grab error messages from the failing validators.

For a list of the available field model subclasses see the documentation for SFormField.

Validation

The SFormField class has a validators property which holds objects conforming to the appropriate validator protocol (SFormTextFieldValidator, SFormNumberFieldValidator, SFormChoiceFieldValidator etc.). These objects are responsible for validating the contents of a field’s value property. An SFormField will validate whenever the value property changes.

You can specify the type of validation you want on your field by assigning validators like so:

SFormTextField *field = [SFormTextField new];
field.validators = @[[SFormTextFieldNotEmptyValidator new]];

We provide a number of validators out of the box which can be initialized either directly as shown above, or through our validation utility classes.

field.validators = @[[SFormTextFieldValidators notEmpty]];

We have created a code sample which demonstrates this: ValidatingTextField.xcodeproj

You can create validators by directly instantiating the validator classes but we recommend using the validator utility class for each field type. These validators provide a ‘menu’ of our provided validators, as well as convenience methods for common validation use cases. Below is a list of the validation utility classes.

  • SFormTextFieldValidators: A validation utility class for SFormTextFields.
  • SFormNumberFieldValidators: A validation utility class for SFormNumberFields.
  • SFormDateFieldValidators: A validation utility class for SFormDateFields.
  • SFormBooleanFieldValidators: A validation utility class for SFormBooleanFields.
  • SFormPickerFieldValidators: A validation utility class for SFormPickerFields.
  • SFormChoiceFieldValidators A validation utility class for SFormChoiceFields.
Comparison Validators

You may find you need to validate a field by comparing it to the value of another field. For example, a ‘maximum’ field where the value must be larger than a ‘minimum’ field. To make these validation cases easier, we’ve provided validators that do just this.

To validate a field’s input against another field, simply create the validator by providing the field to compare against and the required result, then add the validator to the field.

SFormNumberField *minPriceField = ...;
SFormNumberField *maxPriceField = ...;

SFormNumberFieldComparisonValidator *lessThanMax = [SFormNumberFieldValidators compareWithNumberField:maxPriceField validResult:SFormComparisonRuleLessThanOrEqual];
minPriceField.validators = @[lessThanMax];

SFormNumberFieldComparisonValidator *greaterThanMin = [SFormNumberFieldValidators compareWithNumberField:minPriceField validResult:SFormComparisonRuleGreaterThanOrEqual];
maxPriceField.validators = @[greaterThanMin];

The SFormNumberFieldValidators utility class provides a much easier way to validate a minimum and a maximum field - the above code snippet can be simplified down to the following.

SFormNumberField *minPriceField = ...;
SFormNumberField *maxPriceField = ...;
self.minMaxGroup = [SFormNumberValidators minMaxGroupForMinNumberField:minPriceField maxNumberField:maxField];
Validation Groups

The SFormFieldGroup can be used to ensure that when one field updates other fields also re-validate - useful when one field is validating against the contents of another. (We use field groups internally to implement minimum & maximum field validation as seen on the validator utilities.) If one field in the validation group validates, every field in the group will also validate.

To set up a validation group all you need to do is create one, add some fields.

SFormFieldValidationGroup *validationGroup = [SFormFieldValidationGroup new];
validationGroup.fields = @[field1, field2];
self.myValidationGroup = @[validationGroup];

Note that you need to keep a strong reference to your validation groups.

Views

Without a view representing model objects, your forms won’t be able to collect data from your users. The view is what your application’s users will be interacting with.

Each model object has a subclass of UIView to visually represent it.

These views observe the model objects and ensure that they keep in sync when the model updates. They also make sure that they keep the model up to date when your users enter data into the views. (Note that the model will not update to reflect programatic changes on the view.)

Connecting the View and Model

The easiest way to set up a form is to use one of our provided view builders (see View Builders). However, if you have a view you have created, you can easily connect it up with your model object by assigning the model object to the view and adding an appropriate converter (see Converters). The view takes care of all observation and synchronizing.

SFormTextField *model = [SFormTextField new];
SFormTextFieldView *view = [SFormTextFieldView new];

// Add a converter to the view.
...

view.model = model;

Note that when assigning a model to a view, the model will synchronize with any values already set on the model, as well as any values set on the model from then onwards. For example, the two following code snippets are functionally equivalent.

// Snippet 1
SFormTextField *model = [SFormTextField new];
SFormTextFieldView *view = [SFormTextFieldView];

// Add a converter to the view.
...

view.model = model;

model.value = @"Hello World!"; // Field updates to display the model's new value.

// Snippet 2
SFormTextField *model = [SFormTextField new];
model.value = @"Hello World!";

SFormTextFieldView *view = [SFormTextFieldView];

// Add a converter to the view.
...

view.model = model; // Field updates to display the model's value.

View Builders

View builders provide a simple way to build a view hierarchy for any of your model objects. All you need to do is provide your model to one of these builders and it will spit out a ready to use view hierarchy. You will have already seen this in the Creating a Simple Form section. An example of building a form view for a form model is given below.

SFormView *formView = [[SFormFormViewBuilder new] buildViewFromModel:self.form];

There are three view builders provided for you, SFormFormViewBuilder, SFormSectionViewBuilder, and SFormFieldViewBuilder for building form, section and field views respectively.

When providing a model object to one of the previously mentioned view builders, the view builder will carry out three steps.

  1. The view builder will create a suitable view for the provided model object.
  2. The view builder will assign the provided model object to the view as described in Connecting the View and Model.
  3. The produced view will be sized to fit by calling sizeToFit on the view.

Warning: In the case of the SFormViewBuilder and the SFormSectionBuilder the views will be built recursively downwards. For example, passing a form model through an SFormViewBuilder will create views for the form model’s section model, and the section model’s field models.

SFormView

An SFormView is a visual representation of a ShinobiForm model object. The SFormView observes changes to the ShinobiForm object assigned to its model property.

For a list of properties the SFormView observes on its model, check the SFormView documentation.

Warning: Modifying the sections array will cause any section views on the form to be replaced.

Adding a Submit Button

To add a submit button to your form, all you need to do is set a UIButton as your SFormView’s submitButton property. It will automatically have the parent form added as a target, calling submit on touch up inside. We also provide a UIButton subclass, SFormSubmitButton, which is styled to match our default form style.

SFormSubmitButton *button = [SFormSubmitButton new];
[button sizeToFit];
formView.submitButton = button;

For advice on positioning the submit button see Layout.

For an example of using a submit button in a form, take a look at the SubmitForm.xcodeproj sample project.

SFormSectionView

An SFormSectionView is a visual representation of a SFormSection model object. The SFormSectionView observes changes to the SFormSection object assigned to its model property.

For a list of properties the SFormSectionView observes on its model, check the SFormSectionView documentation.

Warning: Modifying the fields array will cause any section views on the form to be replaced.

SFormFieldView

An SFormFieldView is a visual representation of a SFormField model object. There are a number of SFormFieldView subclasses provided to represent different field model object subclasses. The SFormFieldView is a UIView subclass that is made up of the following components:

  • label: This is a UILabel which can be used as the title of your field.
  • requiredLabel: A UILabel can be used to indicate to the user that the field is required.
  • inputElement: A UIView allowing data entry.
  • errorLabel: A UILabel that can display an error message upon failed validation.

For a list of the available view subclasses see the SFormFieldView documentation.

Disabling a Field

There may be times when you want a field to be disabled. To do this you can simply set the field view’s enabled property to NO.

fieldView.enabled = NO;

This will change the field to look inactive, giving it a faded appearance as well as disallowing user input.

Observation

The SFormFieldView observes changes to the SFormField object assigned to its model property. For example, SFormFieldView observes the value property on it’s assigned model. Updating the model’s value property will update the value displayed in the field as shown below.

SFormTextField *model = [SFormTextField new];
SFormTextFieldView *view = [SFormTextFieldView new];
view.model = model;

model.value = @"Hello World!";

The above snippet will change the text value of the field view’s UITextField to “Hello World!”.

There are a few properties that the field view observes, check the SFormFieldView’s documentation (and the documentation of any SFormFieldView subclasses you are using) for a full list.

Converters

The type of a field view’s input can differ from the type or format of it’s model’s value property - this is where the converter plays its role. A converter is responsible for converting between these two differing types or formats and is an object conforming to SFormFieldModelViewConverter.

An example of where the type is the same, but the format may differ is an SFormTextFieldView with an underlying SFormNumberField. In this case the user may input any characters offered by the pop-up keyboard into the inputElement of the SFormTextFieldView, but the SFormNumberField needs to receive an NSString for its value property that only contains numbers or valid grouping/decimal separators. This requirement is often described as an input mask, and can be achieved with a converter.

To set up a view without using the builders you need to first create your own view.

SFormTextFieldView *view = [SFormTextFieldView new];

Then we connect up the model as described in Connecting the View and Model.

view.model = [SFormNumberField new];

You then need to assign an appropriate converter. In this example we’ll need a converter that masks non-numeric characters in an NSString on the view side and passes this to the model side. You could write your own, by implementing a class that conforms to SFormFieldModelViewConverter, but we’re going to use the provided SFormNumberFieldConverter which already has this functionality.

view.converter = [SFormNumberFieldConverter new];

You now have a field view that takes text input of any format, a converter which strips non-numeric characters (aside from grouping/decimal separators), and a model that will receive only numeric strings from the converter.

The SFormFieldModelViewConverter defines two methods:

  • viewValueFromModel: - this method is responsible for converting from the model’s type/format to the type/format needed by the view.
  • modelValueFromView: - this method is responsible for converting from the view’s type/format to the type/format needed by the model.

By implementing these two methods you have ultimate power over view/model compatibility. Using different converters, or even converters you’ve written yourself, you can create new field combinations using the model objects and views we provide out of the box - you can even combine them with your own custom views!

We have created a code sample which demonstrates a converter: Converter.xcodeproj

Layout

All view objects (SFormView, SFormSectionView and SFormFieldView) have a layout property containing a layout object.

  • SFormView’s layout object is an object conforming to SFormLayout.
  • SFormSectionViews layout object is an object conforming to SFormSectionLayout.
  • SFormFieldView’s layout object is an object conforming to SFormFieldLayout.

This layout is delegated to for the view’s layoutSubviews and sizeToFit implementations.

Assigning an object to this layout property provides an easy way to change the layout of a view without subclassing. To change a view’s layout you can create your own concrete implementation of the relevant protocol and assign it to the view’s layout. For information on our out of the box layouts, please see the layout protocols for a list of concrete implementations (SFormLayout, SFormSectionLayout and SFormFieldLayout).

It is possible to write your own layout behaviors by creating classes that conform to the above layout protocols. These objects can them be assigned to the relevant view’s layout property.

ShinobiForm *formView = [ShinobiForm new];
formView.layout = [MyTotallyAwesomeLayout new];

There are two other alternatives ways to lay out a field.

  1. Subclass the relevant view and implement layoutSubviews.
  2. Nil the view’s layout object and set the frames of the view’s subview yourself. Here’s an example sizing a field this way:
SFormTextFieldView *view = [SFormTextFieldView new];
view.layout = nil;

view.frame = CGRectMake(0, 0, 300, 50);
view.inputElement.frame = CGRectMake(0, 0, 200, 50);

Adding the Form to a Scroll View

Adding the form to the scroll view is done exactly the same as you would with any other view. However, ShinobiForms provides a useful class that increases the usability of a form in a scroll view, SFormScrollViewManager. The SFormScrollView manager modifies a given scroll view in response to keyboard events. All you need to do is set up your scroll view manager like so.

self.scrollViewManager = [[SFormScrollViewManager alloc] initWithScrollView:myScrollView];

When the keyboard appears, the correct amount of padding will be added to the scroll view’s content inset so that your users can scroll to the bottom of your scroll view to see fields that would otherwise be covered by the keyboard.

For an example of using the scroll view manager, take a look at the FormInScrollView.xcodeproj sample project.

Toolbar

You can add a toolbar above the keyboard whilst editing a SFormField by assigning a UIView conforming to the SFormNavigationToolbar protocol to your form view’s keyboardToolbar property. By default this toolbar will be an instance of SFormToolbar which provides two buttons on the toolbar that allow users to easily navigate between fields.

When fields are added to the form they will have their inputAccessoryView set to this the value of keyboardToolbar. If you’d like to remove the toolbar you can simply assign nil to the keyboardToolbar property.

formView.keyboardToolbar = nil;

As mentioned previously, the default keyboard toolbar is an SFormToolbar. This toolbar will provide two buttons, a previous button (a minimal arrow pointing to the left) and a next button (a similar arrow pointing to the right). These arrows allow users to navigate to the field previous or next to the currently selected field. If you are at the very first or last field, the previous or next button will be disabled respectively.

Creating a Custom Toolbar

The SFormToolbar subclasses UIToolbar, for basic customization you can simply modify or add UIBarButtonItem objects in the toolbar’s items array. If you’re looking for heavier customization and you don’t want to hook into our built in navigation functionality then you can directly replace the keyboardToolbar property on your form. However, if you’re hoping to keep our navigation functionality then you’ll need to understand a little bit about how our toolbar communicates with the form and vice versa.

SFormToolbar conforms to the SFormNavigationToolbar protocol. To hook into our navigation you’ll need to implement the two methods defined by this protocol.

  • The method setFieldNavigator:.
  • The method fieldView:becameFirstResponderHasPrevious:hasNext:.

The fieldNavigator gives you an object you can send messages to, telling the form to focus on a new field. As you can see, the object passed into the method setFieldNavigator: conforms to the protocol SFormNavigator which defines three methods, focusPreviousField, focusNextField (these methods will do nothing if there is no previous/next field) and dismissField.

When a new field view becomes first responder, the toolbar will have the method fieldView:becameFirstResponderHasPrevious:hasNext: called on it. This method tells the toolbar what field was selected, and whether there are any fields previous or next to it. For example, we could display information specific to the selected field somewhere on our toolbar, or enable/disable the next and previous buttons as we get to the beginning/end of the form.

Creating a ShinobiForm with XML

It is possible to write the model part of your form in XML. This can then be parsed by an SFormXMLParser to produce a ShinobiForm at runtime.

To create ShinobiForms from XML you will need to first add the ShinobiForms_XSD.xml file to your application’s bundle as per Adding ShinobiForms Resources.

It is not currently possible to write the view part of your form in XML, but the SFormFormViewBuilder can help you to easily generate a view hierarchy to represent the ShinobiForm output from the parsing of XML. See View Builders for more information on this.

See the FormFromXML.xcodeproj sample for an example of how a form can be constructed from XML.

Writing XML Forms

The best way to ensure you are writing XML that our API can parse is to adhere to the XSD (XML Schema Definition) file (ShinobiForms_XSD.xml) that is embedded in our ShinobiForms.framework.

See the FormMeta.xml file in the FormFromXML.xcodeproj sample for an example of a valid XML file that can be parsed to a ShinobiForm.

XML Elements

The following is a list of XML elements and the ShinobiForm objects they map to:

  • Forms
    • form : ShinobiForm
  • Sections:
    • section: SFormSection
  • Fields
    • textField : SFormTextField
    • emailField : SFormEmailField
    • numberField : SFormNumberField
    • rangedNumberField : SFormRangedNumberField
    • choiceField : SFormChoiceField
    • booleanField : SFormBooleanField
    • dateField : SFormDateField
    • pickerField : SFormPickerField
  • Validators
    • textEmailValidator : SFormTextFieldEmailValidator
    • textNumericValidator : SFormTextFieldNumericValidator
    • textRegexValidator" : SFormTextFieldRegexValidator
    • textNotEmptyValidator : SFormTextFieldNotEmptyValidator
    • textComparisonValidator : SFormTextFieldComparisonValidator
    • pickerNotEmptyValidator : SFormPickerFieldNotEmptyValidator
    • dateComparisonValidator : SFormDateFieldComparisonValidator
    • dateNotEmptyValidator : SFormDateFieldNotEmptyValidator
    • choiceComparisonValidator : SFormChoiceFieldComparisonValidator
    • choiceNotEmptyValidator : SFormChoiceFieldNotEmptyValidator
    • booleanComparisonValidator : SFormBooleanFieldComparisonValidator
    • booleanTrueValidator : SFormBooleanFieldTrueValidator
    • booleanFalseValidator : SFormBooleanFieldFalseValidator
    • rangedNumberComparisonValidator : SFormRangedNumberFieldComparisonValidator

Parsing XML Forms

Our API handles XML as NSStrings. You can parse your xml via the following:

NSString *xml = //get your xml as an NSString
ShinobiForm *form = [[SFormXMLParser new]formFromXml:xml
                                         xmlEncoding:xmlEncoding
                                                 xsd:nil
                                         xsdEncoding:0];  

Validating XML Forms

As seen in the Parsing XML Forms code example, the method for producing a ShinobiForm from XML (formFromXml:xmlEncoding:xsd:xsdEncoding:) has an XSD parameter. This is used to validate that the XML meets the schema definition before parsing begins. Passing nil for this parameter results in us falling back to using the ShinobiForms_XSD.xml file in your application’s main bundle.

It is possible to validate XML without then parsing it into a ShinobiForm:

NSString *xml = //get your xml as an NSString
BOOL isValid = [[SFormXMLParser new]isValidXml:xml
                                   xmlEncoding:xmlEncoding
                                           xsd:nil
                                   xsdEncoding:0];

Localization

ShinobiForms uses a strings file to allow you to localize the strings used within your forms. The form looks for a strings file named ShinobiForms.strings within your application’s main bundle. You can add this to your main bundle by following Adding ShinobiForms Resources. Once this is done you should then be able to add extra strings for the locales you wish to support.

Adding ShinobiForms Resources

You can add ShinobiForms resources to your application’s bundle. You can do this by dragging the resource/file, found within your ShinobiForms.framework bundle, into your project’s navigator.