Objective-C workflow for iOS apps

This article reviews a simple, lightweight Objective-C workflow component for iOS applications. The source code and demo app can be downloaded from GitHub and freely used under the permissive MIT license.

Repository: https://github.com/ijoshsmith/iOS-Workflow

Workflow as a conceptual tool

Workflow diagrams are often used to investigate and document complex problems. They consist of boxes connected by arrows. A box can represent various things, depending on its shape, but for our purposes here it represents a decision or a work item.

Decision boxes have two or more arrows pointing away from them, each arrow representing a distinct result for whatever decision was made. A work item box, on the other hand, has no more than one arrow pointing away from it. That arrow shows what happens after the task is completed. If the box does not have an arrow pointing at another box, it is a final node in the workflow. A workflow can have more than one final node, if necessary.

Daunting problems can be decomposed, rationalized, discussed, and solved using this modest set of tools. However, solving a problem in the abstract is not the same as implementing that solution in code. Converting the solution depicted by a workflow diagram into robust code that can deal with the realities of software, such as asynchronous Web service calls that might fail, is no small job.

Workflow as a design pattern

I have learned a simple truth through experience. If the solution to a problem is complicated enough that you need a workflow diagram to understand it, you should structure that solution in code as a workflow, if possible. The conceptual tools used to create and understand the solution to a difficult problem can, and should, be leveraged when implementing and troubleshooting that solution in source code.

The biggest benefit of structuring your solution as a workflow is that the plumbing needed to coordinate disparate, asynchronous decisions and work items does not interfere with the code that solves a complex problem. Domain logic is complicated enough by itself! Another major advantage of having direct correspondence between a workflow diagram and class files is that the diagram turns into a map of your code base, which can be very helpful (especially if you add the diagram file to your Xcode project).

After using this approach in two large iOS projects, both of which worked out very well, I decided to create a component that can be reused in any iOS app to model code as a workflow consisting of decisions and work items.

Introducing JASWorkflow

I set out to create a lightweight, yet useful, workflow implementation. Nothing too fancy, just the bare essentials for what I believe is needed by most apps that benefit from using a workflow to solve a complicated problem. Here is a list of features in JASWorkflow:

  • A workflow node can represent a binary decision (yes/no) or a work item.
  • When executed, a node can immediately return its final result, or indicate that its result is “pending” and report the real result later. This is useful for async processing, such as calling a Web service.
  • You can provide the workflow with a completion block that will be invoked when it finishes executing its nodes.
  • You can explain to a workflow how to translate the result of a final node into a semantically relevant outcome value, which is passed to your completion block.
  • Your completion block and every executed node runs on the same thread from which your code started executing the workflow.
  • A workflow object is self-preserving. It prevents itself from being deallocated while executing, so that your code does not need to hold a strong reference to the workflow object until it completes.
  • An executing workflow supports cancellation.
  • A completed workflow supports reuse (e.g. reset and executed again).
  • Nodes can share data with each other via the workflow’s userInfo property.

The rest of this article shows how to put JASWorkflow to use.

Workflow example

For the sake of clarity I present a simplistic workflow example. In reality, the trivial problem solved here would most likely not beckon to be implemented as a workflow, but that is irrelevant for our purposes. It merely provides context for a discussion about implementing a workflow. Without further ado, I give you the Health Workflow:

The workflow diagram above, created with the Grafio app on my iPad, shows the steps needed to determine if a person is healthy. First the test subject is asked if he is willing to exercise. If the answer is No, he is asked if he is injured. An uninjured person who is unwilling to exercise is deemed unhealthy. An injured person’s health status cannot be determined because an injury is a valid reason for not exercising. If the test subject says he is willing to exercise, and then proves it by lifting weights and doing sit-ups, he is considered healthy. I’m not a doctor, but I play one on the Internet!

The demo iPhone app, which is part of the source code download available on GitHub, has a button and label for each possible outcome.

The goal is to execute a Health Workflow when the user taps a button. The workflow must simulate predefined decisions made by the test subject, and then display the workflow’s outcome value next to the button. This allows us to verify that our code is functioning properly for every route through the workflow.

Programmatic workflow representation

Our goal it so convert the workflow diagram seen at the top of the previous section into Objective-C classes, enums, and so forth. Let’s begin that process by creating a diagram equivalent to the one above, but this time each box is labelled with a programmatic identifier.

Three rounded boxes at the bottom represent the possible outcomes of a completed workflow, labelled with enum value names. All other boxes are labelled with node class names. Next let’s turn our attention to the outcome values.

Defining outcomes

All three outcomes for the Health Workflow are listed in the DEMOOutcome enum. I also included a helper function that is used when displaying an outcome to the user.

A workflow’s outcome values do not need to be expressed as an enum. Strings, or any other kind of object, would be fine. Later on I show how these values are associated with nodes, so that a workflow can translate a final node’s result to some meaningful outcome value.

Implementing nodes

The real brains of a workflow is in its nodes. Each discrete decision and item of work should be encapsulated in a subclass of JASDecision or JASWorkItem. Both of those classes are direct subclasses of JASWorkflowNode, as shown in the abridged class declarations below:

Note that JASWorkflowNode does not have any awareness of other nodes. Its two subclasses introduce properties that enable nodes to be linked together. The names of those properties are closely tied to values in the JASWorkflowNodeResult enum seen previously. For example, JASDecision’s yes property references the next node to execute if the decision node’s result is JASWorkflowNodeYes.

Below is the node that represents the decision of whether or not the test subject is willing to exercise. The mockResult property is needed for testing purposes, since this node does not actually know how to do anything aside from return a mocked result value.

The implementation of this decision node is seen below.

Keep in mind, in a real app each node would add value to a workflow, not simply return an externally determined result. I won’t bother showing the DEMODecisionIsInjured class, since it is very similar to the code seen above. Instead, let’s now see how a work item node is declared:

This node does not immediately produce its final value. The node returns ‘Pending’ from the execute method, and two seconds later reports back to the workflow a result of ‘Complete’. During those two seconds the workflow sleeps, allowing the thread on which it runs to do other work. This delayed completion emulates a node that must wait for a Web service to respond, or any other asynchronous event needed by iOS apps.

The DEMOWorkItemLiftWeights class is very similar to this, so I won’t bore you with it.

Executing a workflow

Now that we have defined outcomes and nodes it is time to compose them into a workflow. Before diving into the code that creates and executes a workflow, first let’s review an abridged declaration of the JASWorkflow class. The complete class declaration is full of informative, yet verbose, API documentation.

In a previous section I showed the user interface for the demo app, which displays a button and label for each of the three possible Health Workflow outcomes. Here is the action method for a button that tests which outcome is produced if the test subject will not exercise because he is injured (which, I remind you, should be ‘Inconclusive’):

That method calls a helper method responsible for creating a workflow with predefined decision values, executing the workflow, and updating a label with the result.

The first thing the above method does is call into yet another helper method to create a JASWorkflow and return, via an out parameter, the first node to execute. We will visit that method shortly, but for now turn your focus to the use of the startExecutingWithNode:completion: method.

The first point of interest is that a workflow can start executing at any node, there is no arbitrary enforcement of a “first” node. This increases the flexibility of a workflow, since there may be several viable entry points, determined at run-time.

Although the code shown above does not make it explicit, the startExecutingWithNode:completion: method is a non-blocking call. In other words, the flow of execution does not wait until the workflow completes before moving past a call to that method. When the workflow eventually finishes executing nodes, it will invoke your completion handler block.

The completion handler block accepts three parameters. The first is named outcome and points to a semantically relevant value based on the result of the final node to execute, or nil if no such value exists. In the next method I explain more about this very useful and powerful feature.

The second parameter to the completion handler block is named finalNode and it points to the last node to execute before the workflow completed. If the workflow encountered an error, this node’s error property will point to an NSError object.

The last parameter is named cancelled and it will be YES if the workflow was cancelled via the cancel method. If the workflow was cancelled, the outcome and finalNode arguments will be nil.

Next up is the method that creates and configures a workflow and its nodes. This is where the magic happens.

I annotated each part of that method with a letter to assist with explaining how it works.

Part A is equivalent to drawing boxes in a workflow diagram. This is where the four workflow nodes are created. Note that a node can be passed data via its initializer, such as the initWithCount: method for the sit-ups node, or via properties, accessor methods, etc. Alternatively, nodes can access and share data by using the workflow’s userInfo property, which exposes a mutable dictionary.

Part B uses Key-Value Coding to set up mock return values for both decision nodes. I use KVC instead of a direct property assignment because those variables are typed as JASDecision pointers, and that class does not declare the mockResult property (both of its subclasses declare that property).

Part C is equivalent to drawing arrows between boxes in a workflow diagram. This is where the order in which nodes execute is determined.

Part D creates an instance of JASWorkflow that manages four nodes. The workflow object owns (i.e. retains) the nodes passed into its initializer, and only those nodes. All other pointers to nodes are weak, meaning that a node not passed into the workflow’s initializer will be immediately deallocated unless your code explicitly keeps a strong reference to it. All nodes used in a workflow should be passed to its initializer.

Part E shows how to map the result of a final node to an outcome value. There are three uses of the ifFinalNode:producesResult:evaluateTo: method, one for each value of the DEMOOutcome enum. This is how the workflow knows what value to pass for the outcome parameter of your completion handler block. When a node produces a result, such as JASWorkflowNodeResultNo, for which it does not have a node to execute next, the workflow is complete. At that time, the workflow tries to translate the [finalNode, result] combination into an outcome value. It can only produce an outcome value if your code supplied one via the ifFinalNode:producesResult:evaluateTo: method.

Part F points the rootNode out parameter at the first node to execute, which in this demo is the node that asks if the test subject is willing to exercise.

Summary

I have had nothing but success when applying this approach to implementing complex, asynchronous aspects of production iOS apps. If you are the kind of person who finds workflow diagrams useful, like I do, be sure to consider giving it try next time you’re faced with a gnarly problem whose solution can be clearly expressed with boxes and arrows.

All of the code reviewed here, and more, is available on GitHub.

Happy coding!

Posted in iOS 6, Objective-C | 2 Comments

Slides from iOS for .NET Devs presentation

Recently I spoke at the Southeast Valley .NET User Group, in Arizona, about iOS programming from a .NET developer’s perspective. I really enjoyed speaking to the group and they had a bunch of good questions for me. Sharing my experiences in the “other world” is an interesting challenge!

Josh speaking about iOS

I went into detail about how native iOS programming compares to .NET programming. The presentation briefly covered many of the topics that my book ‘iOS Programming for .NET Developers‘ goes into in great detail. For example, I spent some time comparing Objective-C to C# and showing how to translate between them.

Josh talking about Objective-C

I exported my slide deck to a PDF, which can be downloaded from this blog post.

iOS Programming for .NET Devs Slides

Enjoy!

Posted in iOS Programming for .NET Developers | Leave a comment

Deciding between native iOS and Xamarin (MonoTouch)

An old friend of mine, who I worked with back when I was a .NET developer, emailed me recently. He was looking for some advice on which platform to use for creating iOS software, mostly focusing on native versus Xamarin (MonoTouch). My answer does not delve into specifics about Xamarin, because it’s been years since I used their platform in earnest. It does, however, express my opinion on native vs. non-native. He left out other options, such as PhoneGap, perhaps because he simply was not yet aware that they exist.

Below is his email, followed by my reply. I don’t expect this to settle the matter once and for all (where’s the fun in that?!) but perhaps it will give others a new perspective on the topic.

His Question

Since I have started playing around with iOS, two questions that came to my mind and I thought I should ask you.
To be or not to be native iOS?
Xamarin or not to Xamarin?
My real purpose is

  • to create some productivity apps for handheld devices and
  • to create some re-usable control libraries.

It will be great if you can share your expertise on this :-)

My Reply

Your question is a good one, which I’ve discussed with many people. So far my answer is broken down into three considerations:

  1. Reach – Is getting the same codebase onto as many devices as possible a high priority, or are you more interested in keeping the app on one operating system for the first launch (in order to create the best app possible – no compromises)?
  2. User Experience – Are you willing to make sacrifices in the user experience to use a non-native layer (ex. building HTML-based UI via PhoneGap)?
  3. Budget – Can you afford to build a separate app for each platform? If so, that’s probably the best route since you won’t need to rely on lowest common denominator solutions that bring their own bugs and limitations to the table.

In your particular case, the re-usable control libraries should be native because I doubt that such performance-sensitive, low-level rendering code would be reusable via Xamarin’s platform across different OS’s. You can always wrap your native libs with their runtime wrapping API, which I read about a while back, and use them from Xamarin apps if necessary.

Regarding the productivity apps, I don’t know enough details about your situation to say anything definite. Using Xamarin for those apps might make sense, and you will probably have a temporary advantage of avoiding the learning curve associated with native iOS programming (note: I wrote temporary). Truth be told, learning native iOS programming isn’t all that difficult, it just takes time and a little brain rewiring. Also, when working with Xamarin, or PhoneGap, you often end up dealing with Objective-C code anyways (plug-ins, StackOverflow examples, etc).

I have a bias toward native since it is inherently better than any 3rd party layer, and I’m not interested in dealing with the bugs of Apple and another company as well. Unfortunately, technical preferences don’t usually drive business decisions!

Posted in MonoTouch, Objective-C | 4 Comments

Save 20% on iOS Programming for .NET Developers this month

If you buy a paperback copy of iOS Programming for .NET Developers this month (October, 2012) be sure to use this coupon code to save 20% on your purchase: OCTBOOKS12

Visit Lulu.com to pick up your copy and get started learning one of the most in-demand skills on the market today.

 

Posted in iOS Programming for .NET Developers | 5 Comments

Determine the size of a View by inspecting its NIB

I was exploring the UICollectionView API tonight and stumbled upon an old problem. Both UITableView and UICollectionView ask their delegate for size information about the cells they display. When creating cells in separate XIB files, which I normally do, this means that the delegate must somehow know how big the root View is in that XIB. The easiest way to do that is evil: hard-code the cell’s size in the delegate. Duplication! Yuck!! No more!!!

It dawned on me that this entire problem can be avoided by doing a one-time lookup of the View’s natural size, as it exists in the XIB file (and, at run-time, in the view’s NIB). I posted a Gist on GitHub that shows my simple solution:

Happy coding!

Posted in Interface Builder, Tips and Tricks, UICollectionView, UITableView | 1 Comment

Brief overview of Auto Layout in iOS 6

This blog post examines the basics of Auto Layout in iOS 6 and suggests resources for learning more about the topic.

Introduction to Auto Layout

Starting with iOS 6 Apple has given developers a powerful new way to create user interfaces that intelligently adapt to layout changes. This SDK addition, known as Auto Layout, enables a developer to express spatial relationships between views, leaving UIKit to update each view’s frame as needed to honor them. Relationships are expressed via layout constraints, which are objects of type NSLayoutConstraint, that can be added to a view by passing the addConstraint: or addConstraints: messages to it.

Auto Layout replaces the “springs and struts” autosizing layout mechanism used in the past. All new XIB and storyboard files have Auto Layout enabled by default. It is possible to port old views to use Auto Layout because it is smart enough to translate autosizing information into layout constraints.

The fact that Auto Layout was released at the same time as iPhone 5 is no coincidence. With iPhone 5’s screen being 4 inches tall, instead of the traditional 3.5 inches, there is a more pressing need to create adaptable user interfaces. If the rumored mini iPad is ever released, this concern will be even more pervasive for iOS developers. However, for simple views the old autosizing model works fine. For example, an app I have been developing at work relies heavily on UITableView and was created using autosizing, for running on iOS 5 and iPhone 4. When we tested the app out on iOS 6 and iPhone 5 simulator, there were almost no UI layout problems. For more complicated and custom user interfaces, however, the outcome probably won’t be as pain-free.

Auto Layout is also very helpful for supporting internationalization. Not only does it make the work of accommodating variable length display text easier, it also intrinsically supports right-to-left languages, such as Hebrew and Arabic. In the Auto Layout API the terms “leading” and “trailing” are used to generically refer to the start and end of a line, respectively. For left-to-right languages, such as English, leading means left and trailing means right. For right-to-left languages, it’s reversed. By relying on these generic concepts built into the API, it frees you, the application developer, from needing to take such locale-specific issues into account when building views.

How to use Auto Layout

Interface Builder (IB) now has support for working with layout constraints in an iOS app. It allows you to create and configure layout constraints on views without having to write a line of code. The screenshot below shows how constraints are now part of the document outline, and how the selected constraint is highlighted with a white border in the visual designer.

There is an ASCII-art inspired language named Visual Format Language (VFL) that can be used to ideographically express relationships between views. VFL text is written in code files, not IB, and acts as a facade over the cumbersome underlying API. Similar to the layout constraints support in IB, this language supports a subset of the functionality available in the API.

Most of the time the support in IB and VFL will cover your needs. However, the full power of layout constraints is available for situations that need it. Code can be written that creates individual NSLayoutConstraint objects and specifies all parameters required to create a single constraint, including things that are not available in other formats, such as a multiplier that allows a constraint to operate on a percentage of a value. The example below demonstrates this.

Cardinal rule of Auto Layout

There is something fundamental about Auto Layout that you must know in order to use it properly:

Constraints must provide enough information to determine one, and only one, size and location for a view.

If you don’t provide enough constraints, Auto Layout cannot accurately determine a view’s frame. If you provide too many constraints, such that multiple frames are possible for the same view, Auto Layout cannot accurately determine a view’s frame. If you violate the cardinal rule, either your app will crash or a large amount of helpful troubleshooting information will be logged to the Output window in Xcode.

Recommended Resources

There is far too much to say about Auto Layout for this introductory blog post. My goal here is to give a newcomer enough information about the topic so that it makes sense. If you have been working with iOS for a while, I’m sure you will see why it’s such an important topic to learn about. I suggest the following resources for an in-depth treatment of the subject:

The free tutorial on Ray Wenderlich’s site Beginning Auto Layout in iOS 6: Part 1/2

Both chapters about Auto Layout in Ray Wenderlich’s excellent book ‘iOS 6 by Tutorials

Apple’s documentation: Cocoa Auto Layout Guide

All three presentations about Auto Layout at WWDC 2012

I published a simple demo project that shows how to work with Auto Layout in code on GitHub.

Posted in Auto Layout, iOS 6 | Tagged , , | 2 Comments

Check out the iOS 6 Feast

Ray Wenderlich, whose iOS programming tutorials have helped countless aspiring iOS developers learn the trade, is now hosting a gigantic feast of iOS 6 goodness. He has dubbed it the iOS 6 Feast because there is course after course of delicious knowledge being served. His team has been hard at work preparing tutorials, books, and sample code to help make it easier for the rest of us to quickly learn about the new features in iOS 6.

The last course of Ray’s feast is a growing list of iOS-related products being given away for free. I decided to contribute a free copy of my book iOS Programming for .NET Developers.

Yesterday I bought, and have since been devouring, one of his team’s new books titled iOS 6 by Tutorials. It is an unbelievably large and comprehensive book about all of the great new features that iOS devs must know about to remain relevant and tasty in the job market. ;)

Posted in Books, iOS Programming for .NET Developers | 1 Comment