As with any good project layout, you should separate out your UI from your non-UI components. That doesn't just mean on disk layout (though that makes sense as well) but rather using a MVC approach, whereby your controllers (C) know about the Models (M) and the UI (V) is rendered separately.
You can use Key-Value Observing (aka KVO) to set up your models, such that when they fire, it sends around a notification to any registered listeners to update. If you are using XIB to generate your user interfaces, then this is what happens when you bind an object to your display widget(s).
So you can end up with separate XIBs for your iPhone, Mac OS and (later) iPad - though if you resize things correctly, you can have the same XIB for the iPhone and iPad.
Lastly, there are often some cases where you need to inject logic into your models (for example, adding an image to return from a method). In this case, the iPhone and Mac OS have different Image classes. To do this, you can create the following:
MyModel.m // contains the data, no UI
MyModel+UIImage.m // contains a category for adding the UIImage
MyModel+NSImage.m // contains a category for adding the NSImage
The category looks like this:
@interface Host(UIImage)
-(UIImage *)badge;
@end
@implementation MyModel(UIImage)
-(UIImage *)badge
{
if (green)
return [UIImage imageNamed:@"green.png"];
if (red)
return [UIImage imageNamed:@"red.png"];
}
@end
---
@interface Host(NSImage)
-(NSImage *)badge;
@end
@implementation MyModel(NSImage)
-(NSImage *)badge
{
if (green)
return [[NSImage alloc] initWithContentsOfFile: @"green.png"];
if (red)
return [[NSImage alloc] initWithContentsOfFile: @"red.png"];
}
@end
This has the added advantage that your unit tests can just load the model (without any image categories being loaded) whereas at runtime your code that needs to process the images (say, in a view controller) can load the model-with-category and load the badge with [model badge]
in a transparent way, regardless of which platform it's compiled for.