//
you're reading...

Programming

Tips for Architecting Dual-platform OS X / iOS Applications

iOS & OS XIn the course of developing Speech Timer 2 for iOS and OS X, I’ve learned a number of lessons when making an app to run in both platforms. In essence, you can share much of the non-UI code but there’s a way to share some of the UI code as well. Here are my hard-earned lessons that you can take for granted:

  • Separate the controller classes.
  • Refactor common controller logic and share it whenever possible.
  • OS X’s initialization order is different than iOS.
  • Have a single document class for both platforms.

Controller Classes

In Cocoa’s Model-View-Controller (MVC) architecture, controller classes are really part of the user interface component and not really attached to  model classes. Controller classes in iOS are derived from UIViewController and in OS X you’ll need to subclass NSWindowController  The logic for each are significantly different and you’ll probably want iOS to have its own set of classes that are totally separate from their OS X counterparts – I wouldn’t recommend using the same class to handle both and switch between superclasses using preprocessor macros (more on this later).

Don’t fall into the trap of thinking that NSViewController is an equivalent of UIViewController  It isn’t really. One of the big difference is that NSViewController doesn’t participate directly in UI preservation & restoration. On the Mac, UI restoration is heavily leaned towards NSWindowController and NSDocument – you’ll need to handle NSViewControlle  restoration as special cases. However in iOS this is done primarily via UIViewController

Crafting Out Controller Logic

When you’ll developing the app, you’ll probably want to do one platform first and have it into a 90% working state before working on the other one. That way you’ll be able to identify more clearly which parts of controller code that are common to both platforms. Then you’ll can start refactoring to pull out these into it’s own classes that are shared by your UIViewController and NSWindowController subclasses.

 

iOS & OS X dual-platform application architecture

The diagram above shows an example of how to architect these classes. On the iOS side, the iPad and iPhone gets their own view controller subclasses to handle their different UI paradigms. These view/window controllers are suffixed with “IOS” or “OSX” just to avoid naming clashes that will be useful when the two are contained within the same project – even though only one variant will be present for each build target. Moreover controller code that are identical to both iOS and OS X are refactored to their own “Logic” classes, which at runtime are owned by their respective controller classes.

Who owns the UI?

In iOS, the UI needs to be constructed first before the document – that is, UIViewController objects need to be present and showing something before you can get a live UIDocument subclass. That’s because iOS’ user interface is still single-tasking for the most part; applications take up the entire screen and nothing else is appears to be running. Hence your app needs to be responsive and display something upon startup. You’ll need to have a root view controller present at applicationDidFinishLaunching  This also means that if you use UIDocument with its asynchronous I/O, the document instance may not be ready when your UI is loaded.

Contrastingly OS X applications has the option to not show any UI before the document is ready. The menu bar will show up first, the app may open a document and then only show a window when the document is fully loaded. OS X’s multi-tasking UI doesn’t leave the user staring a blank screen while your application initializes and loads a document – the user probably have something else running in the meantime.

As a result, in iOS the UI object is “owned” by the application delegate. That is, the application delegate starts up the initial view controller and probably still has a reference to it – either directly or indirectly via the root UIWindow object. But in OS X, the document tend to own most window controllers and also likely responsible for instantiating it – that’s why NSDocument has a makeWindowControllers method.

Sharing the Document class

Is the document class a UI component or a data store component? Chances are it’s a kind of both. It handles top-level local I/O in both iOS and OS X but in OS X it is also responsible to instantiate and keep a reference to window controllers. Hence you’ll probably want to have one document class that is shared in both OS X and iOS, and use preprocessor statements to handle each platform’s specific requirements:

@interface FOOYourDocument :
#if TARGET_OS_IPHONE
UIDocument
#else
NSDocument
#endif
//  declare your custom methods here
@end

@implementation FOOYourDocument
// your overrides here

// OS X specific overridden methods
#if !TARGET_OS_IPHONE

-(void)makeWindowControllers
{
// instantiate your window controllers here.
}

#endif

@end

If you use Core Data, you can use BSManagedDocument on OS X in place of UIManagedDocument on iOS. Furthermore, if you use Core Data, your document class would be pretty lean since the bulk of the work will be done either by Core Data or your data model classes, that will be virtually identical on iOS and OS X.

That’s all for now folks. Take care! 



Do you enjoy this post? Enter your e-mail address below to receive articles like this one in your mailbox.

Free Updates!

Learn how to grow your indie business while keeping your day job.

Categories

Archives

Keep updated!

Don't miss out on new articles!
%d bloggers like this: