Skip to content

Archive

Tag: Application Design

This is quick tutorial showing how to get started with F* in Objective-C. F* is a proposed meta-pattern for application development (see here for related documents)

You can check out the sample project from SVN:

http://xp-dev.com/svn/harmony-framework/trunk/f-star/iOS/tutorial/FStarExample/

Setting up your project

  1. Open XCode.
  2. Go to File > New Project
  3. Select ‘Empty Application’.
  4. In Product Name, I input FStarExample.
  5. I put my projects under my experiments/ folder. XCode will create a folder for our project.

Arranging the sample project

I like to have ‘headroom’ in my projects. The initial project structure looks like this:

  • FStarExample (project node)
    • FStarExample (source location)
    • FStarExampleTests (for unit testing)
    • Frameworks (libraries required by the iOS SDK)
    • Products (build output)

I regroup all items, so that it looks like this:

  • FStarExample (project node)
    • source (custom group)
    • tests (previously, ‘FStarExampleTests’)
    • [implied]
      • FStarExample-Prefix.pch (digged up from ‘Supporting Files’ I just like to keep this one ‘visible’)
      • FStarExample (still the app delegate)
      • Products
      • Frameworks

Keep in mind that XCode groups are just a convenient way to… group files. For better and for worse, grouping is irrelevant to the underlying file structure.

Setting up an F* instance

Under source I create the following structure:

  • source
    • [m]
    • Facade.h
    • Dispatcher.h
    • Feature.h

The Facade will be used to instantiate all the features we use in our application. The Dispatcher provides the basis for gluing features together. Feature is the base class for all application features.

Note: XCode doesn’t encourage physical groupings (putting source files in separate folders – it is needless to say possible but doesn’t flow smoothly) so I’ll be content with XCode groups.

Creating a feature

To create a feature named foo, follow the steps:

  1. create a group named foo
  2. create a subclass of Feature, Foo inside group foo.
  3. foo overrides setup in Feature.
  4. import the feature class from Facade and add an entry to initFeatures.

The sample project illustrates the above steps.

Event handling

The dispatcher mediates communication between features via events. In the provided example:

  • Foo registers with the dispatcher to receive an event x. registration happens within the setup method. (While setup is running, nothing warrants that all features are already initialized so while it’s okay to register for events, it is not okay to dispatch.
  • After all features are setup, the start method is called on each feature, at which point Bar dispatches the event.
If you test the sample project (hold run button in top left until options appear), you can check the output in the log window.

Implementation notes

About event handling and the dispatcher
In my sample project, the dispatcher is not very scalable – as we go on and define more and more notifications, it will quickly become bloated. I will probably update the sample project a bit later to show how this can be fixed.
In practice this needs to be done with some care – when using F* defining new notifications is common enough that a templated approach quickly becomes desirable.
About the facade
In the facade each feature is bound to the applications in two ways:
  • We need to import the feature class
  • The feature class needs to be instantiated and added to the feature list.
Classes can be instantiated dynamically, which practically allows turning a feature off without having to change any code. Needless to say this comes at the cost of compile time safety.
The facade should be connected with FStarExampleAppDelegate. As the last call in application:didFinishLaunchingWithOptions: I instantiate the facade. A real application would need to provide access to UI resources (here, the window provided by the app delegate).

In Practice

When I use F*, I tend two follow a 3 step cycle.

1. induction

During the ‘inductive phase’, I try to focus exclusively on the problem I am solving. If I feel my feature requires external input (or a signal), I design notifications without worrying about the source of such notifications.

This approach becomes important over time because it allows solving the problem at hand without  burdening ourselves with the growing ‘mass of code’ that the project generates. For me this inductive phase is similar to ‘starting a new project’. Surely most programmers have noticed that they feel dynamic and fresh at the beginning of a new project?

2. testing

During the testing phase, notifications can be used to write acceptance tests – test the feature in isolation.

3. integration

During the integration phase, I make sure that the notifications the feature relies on are actually satisfied.

If you’re trying to write your first iPhone/iTouch applications or are struggling to put together a small to medium size application, this article may help.

This article assumes you have the ‘kind of understand’ feeling about MVC that most programmers get after digging up a little material on the topic.

A Basic Model View Controller (MVC) setup

In xcode, select new project > view based application. The following classes are created for a ‘Z’ project:

ZAppDelegate – Application Facade
ZViewController – Root controller

The only classes missing are a view class and a model class.

Basic view setup:

  • Create a ZView class if you need to customise the view provided by ZViewController.xib programmatically. Assign ZView as class of the root controller’s view from the property inspector in interface builder.
  • If you only want to gain access to UI elements added to the view, do the following:
  • 1 –  open ZViewController.xib and add your UI elements. In this example, I assume a label (UILabel class) was added.
    2 – add an IBOutlet field to ZViewController.h, like this :
    IBOutlet UILabel* myLabel;
    3 – In ZViewController.xib, notice that myLabel now appears in the file’s owner property inspector (click the blue arrow pointing right in prop inspector to view outlets). It has a little keyhole on the right.
    4 – From myLabel‘s keyhole, click-drag a connector to the actual label.

    Step 1 creates the actual UI element. Step 2 allows programmatic access to the created UI element. Finally, Steps 3 and 4 ensure that the field we have created maps to the actual UI element.

Basic Model Setup
Now suppose we created a ZModel class to hold our application data model. We probably want to gain access to the model from the controller. With a model driven view (view is mainly required to reflect model state), it is often convenient to keep a reference to the model from our view as well. This brings two questions:

1 – Where do we create the model? In other words, when should it be initialized?
2 – How do we provide the controller and view a reference to the model

These questions can be answered in various ways. For simple applications, here’s a recipe that works.

  • Instantiate your model class in ZAppDelegate::applicationDidFinishLaunching
  • Still in the same function, assign the model to the controller and view.

ZAppDelegate naturally owns a reference to the controller (viewController) and view (viewController.view). This makes it a convenient placeholder for our data model. Besides, even if we change the controller and/or view while the application is running, we can still access the model root. Finally, applicationDidFinishLaunching clearly holds the code used to add the view to the main window, so we are sure that the view binds the model before getting rendered the first time.

Processing user interaction

If your view is processing touches (touchesBegan etc…) it may be convenient to assign a backward reference from the view to the controller. Again, this setup can be effected from applicationDidFinishLaunching. We can then query the controller to perform high level actions when touches occur.
You can work around potential (compile-time) cyclical dependencies between the view and controller by only referring the controller using an
@class ZViewController
annotation before your interface declaration/class def. Although this may trigger a warning, you don’t need to import ZViewController to invoke methods on it as Objective C resolves method calls at runtime.

That’s it for now…

If your application is becoming complex, you may need to look into more developed MVC solutions – e.g. get inspiration from the PureMVC framework or maybe try my F* Meta-Pattern.