SDK Documentation

Start building patches with Objective-C today.

Overview

The Form Patch SDK allows you to create custom patches for use in Form. Custom patches allow one to extend Form to support features that are not supported by the patches supplied with the tool.

Prerequisites

Before getting started with the Form Patch SDK, it’s important that you have experience using Form, and also understand how to code in Objective-C. This document does not cover how to use Form or how to write Objective-C code.

What is a patch?

Patches work very similarly to a function. Each patch takes in some input values, performs an operation on them, and then produces its output values.

Patches can do anything from simulating a spring to communicating with a backend over the network. Form comes bundled with patches that cover the common case, however, if you need more specialized patches, or want to reuse code from an existing iOS project, the patch SDK is a great way to extend the tool.

Installing the SDK

Install Xcode

If you don’t have Xcode already installed, grab a copy from the Mac App Store.

Install the patch SDK

The patch SDK consists of an Xcode Template that can be used to produce a formplugin bundle. To install the SDK, open up the Form SDK Installer package on your Mac and follow the steps to complete.

Using the Form Plugin Xcode template

Creating a new Xcode project

Once the SDK is installed, a Form template should appear in Xcode under File > New > New Project.

Select “Form Plugin” to create a new Form Plugin Xcode project.

Upon loading, you should end up with something like this:

Building the project

Before we dive into making our plugin, let’s configure the viewer and make sure everything builds properly.

Linking the plugin to the viewer

In order to test out your plugin, the Form template will build a copy of the Form Viewer with your patch pre-installed. However, before building the viewer, you need to make sure it knows about the plugin and other Form frameworks. To do that, click on the project in the left sidebar:

From here, select the viewer target at the bottom of the Targets list. If you do not see a targets list, click the icon in the top bar that says: “General    Capabilities    Info    Build Settings...”

From here, click “Build Phases”.

To add your Form plugin, click "Link Binary with Libraries". Click the + button, and then add "<Project>PerformPlugin.framework".

Make sure, Performer.framework and RelativeInterface.framework are also included here. If not, drag them from the sidebar under <Project>PerformerPlugin » Frameworks into the "Link Binary with Libraries" section.

In addition to this, we need to make sure the viewer copies over each framework. Go to the "Copy Files" phase at the bottom. Click the + button and scroll to the bottom of the list. Add "Performer.framework", "RelativeInterface.framework" and "<Project>PerformPlugin.framework".

Once finished, your project should look like this:

Try it out by building the viewer target for your device. Once the viewer is up, switch back to the aggregate build target and hit Build & Run.

Your plugin will be automatically copied into the Form mac app. From here, you can try it out by dropping down your patch!

Anatomy of a patch

A patch is actually made up of two classes in Form, the node and the patch. The node is a class that runs inside of Form on the Mac and the patch is the class that runs on the device. Just about every object in Form has a Mac and iOS pair. Each class starts with either FMR or PMR, which are Former (Mac) and Performer (iOS) respectively.

FMRNode

FMRNode is the baseclass used when creating a node for your patch. It is responsible for defining ports on the patch and generally houses any logic that needs to happen in the editor.

PMRPatch

PMRPatch is the baseclass used on the device. It is automatically created by Form when the node is created. The patch class is the meat. It is what actually does the processing.

Let’s start by going through our fresh plugin created by the Xcode Template:

First, we'll look at the node. The default template provides a node that creates an input port and an output port.

Each port is represented by an object. The most common are FMRPrimitiveInputPort and FMRPrimitiveOutputPort. These ports are used for passing a Boolean, Index (int), Number (float), Angle, String or Color value.

There are two other methods in the node. + (NSString)defaultName and + (NSString)processClassName. The first returns the default name Form should use when creating a new instance of the patch. The second outlines the patch class on the device that corresponds to this node.

Now let's take a look at the patch starting with the header.

For each port defined in the node. There's a corresponding port defined as a property of the patch. Notice, the port classes are exactly the same, except they start with a PMR prefix instead of FMR. This is to note that we're using the device (Performer) version of these classes.

The patch implementation is simple and consists of just one method. - (void)processPatchWithContext:(PMRProcessContext *)context. This method is called by Form's runtime to tell the patch that it needs to process its input variables and update its outputs.

If your port variable names match the uniqueKey passed in the node, Form will populate them automatically. Ports can also be retrieved via the - (id)portForUniqueKey:(NSString *)uniqueKey method.

Here we can see the patch reads a boolean value form its onOffInput port. If the value is YES, then we create a random color and set it in the colorOutput port.

Once the node and patch are defined. You can begin testing your patch in Form. To do so, build the Viewer target for your device, then build the aggregate target to build and install your plugin in Form.

Once you've installed your plugin in Form. Drop it down in a new document and connect the color output to a Color View. Flip the On/Off switch on via the sidebar and connect the document to your device. You should now see a color view drawing with the random color generated by your custom patch!

This was just a brief introduction to creating custom patches in Form. If at any point you get stuck with an API, use Xcode's to "Jump to Definition" (Right click on the method / class name) to get to the API's header file. All public headers include detailed documentation.

Distributing patches

Once you’ve finished your node / patch. Build the project by going to Product > Archive. This will open up a directory with the .formplugin and perform plugin files. Anyone can simply double click this file and Form will open and install the plugin. Note however, they will need to download the Form Viewer Xcode Project in order to use the plugin on the device.

View patches

In order to create a custom view. Subclass the FMRViewNode and PMRViewPatch classes. You can easily create a custom RIView subclass and provide it in the PMRViewPatch subclass. Take a look at our Cube and WebView sample projects for examples on creating custom view patches.

Building the examples

To build the examples, open the Xcode project for the example. Then plug-in your phone to the computer to build a custom version of the Form Viewer.

Next, select the Form Viewer target in the target dropdown. Then select the phone you would like to build to and run Form Viewer.

Once Form Viewer is running on your device, build the plugin to run in Form. In the target dropdown, select the build target for Mac. Once that runs, go to Form, connect to your device, and dropdown the patch you were trying to build. This should now appear on your device.

Using Third-Party Patches

Add the .formplugin file by opening it in Form.

Add the .framework file to the Custom Patch Viewer Xcode project by dragging it into the Xcode sidebar.

Then select the project, go to 'Build Phases' and expand the 'Copy Files' tab. Locate the .framework in sidebar and drag it into the list of frameworks. Now build and run the Custom Patch Viewer on the device. In Form, you can now use the custom patch and have it run in the Custom Patch Viewer.

Distributing Custom Prototypes

To distribute prototypes through TestFlight, you'll need to replace the "Performer.framework" and "RelativeInterface.framework" with the ARM-only versions.

Start by opening the Xcode project and selecting the two frameworks from the sidebar. Then right-click on them and select "Show in Finder".

Locate the ARM-only versions of the frameworks that you downloaded earlier and replace them with the frameworks you opened through Xcode earlier.

Now that the frameworks are replaced, the project is now ready to be distributed. Follow these steps to configure TestFlight for project distribution.

Loading Custom Form Documents

Open up the "CustomPatchViewer" example for the Form 1.4 SDK. Drag your Form document file into the sidebar to add it to the project.

Paste this inside of AppDelegate.m anywhere between @implementation AppDelegate and @end:

// Launch a Form document from a URL

- (void)openDocumentAtURL:(NSURL *)url { NSError *error = nil;
PMRDocumentViewController *vc = [[PMRDocumentViewController alloc] initWithURL:url error:&error];
if (vc == nil) {
NSLog(@"Failed to create document view controller: %@", error);
return;
}

vc.delegate = (id)_connectionManager;
vc.options = PMRDocumentOptionsNone;

// If the current presented doc is server connected, we need to disconnect so the host knows

if ([_presentationCoordinator.documentViewController isConnectedWithHost]) {
[_connectionManager restartServer];
}

[_presentationCoordinator openDocumentWithViewController:vc animate:YES handler:nil];
}

Then, paste this at the bottom of -application:didFinishLaunching: just before "return YES;". Replace "HelloWorld" with the name of your Form document file. Make sure to leave off the extension.

// Create a file URL that points to the document on disk and then launch the document.

NSString *path = [[NSBundle mainBundle] pathForResource:@"HelloWorld" ofType:@"form"];

NSURL *fileURL = [NSURL fileURLWithPath:path];

[self openDocumentAtURL:fileURL];

Build & Run. The document will load automatically on launch. Your AppDelegate.m file should look like this: