App Architecture

Here’s some thoughts on how you should architect your App, based upon discussions between two well-seasoned iOS developers. Let’s jump in!

MVC

Model-View-Controller is a very important concept you should adhere to! Don’t be intimidated by the complicated sounding name. All it means is that you should split your responsibilities between 3 broad areas of your code.

Think of it like the company where you work (unless you’re self-employed, in which case you should close your laptop and go surfing now!). Your company should have a marketing department, an accounting department, and an HR department. Whenever anything needs to be done, the appropriate department is responsible for making it happen. That’s the gist of it.

Views are any part of your app that is visible on screen. On iOS, this is easy: UIViews, Xibs, and possibly some helper classes/categories that do display-related things.

Models is your data and core logic of your application. You might consider this your middleware. Managers, Entities, and Service classes fall under this banner.

Controllers are anything that bridges the gap between Views and Models. These are your UIViewControllers, and occasionally some helper classes.

Note: Of course, sometimes you’ll have to bend these guidelines to get things done. Don’t be a hardliner!

Fetching Data

A common scenario is that you’ll want to fetch data from the server and display it on screen. In our experience, this is a good way of achieving this:

MVC

I’ll explain in detail:

  • A manager class is a singleton. It has an array of your entity objects. On init of this singleton object, it loads the objects from the data store.
  • On app startup, the view controller displays the current list of objects.
  • When appropriate, the view controller calls a method on the manager singleton object, signalling it would like fresh data to be fetched.
  • The manager sets a ‘isLoading’ property to YES.
  • The view controller uses KVO to observe the ‘isLoading’ is now YES, and display the loading activity indicator.
  • The manager calls a static method on the Services class, passing in a completion block. The last argument of the completion block is a ‘success’ BOOL.
  • The services class calls AFNetworking to make the appropriate remote call, parsing the JSON results into a strongly-typed array of entity objects, calling the completion block.
  • Your entity objects are simple NSObject subclasses with a set of properties, and little else.
  • The manager is responsible for displaying the alert to the user if the service call failed. This prevents multiple view controllers displaying multiple error messages.
  • If it succeeded, the manager stores the newly returned array of entity objects in a property.
  • Either way, the manager sets its isLoading property to NO.
  • The view controller uses KVO to observe that the objects have changed, and refreshes itself to reflect the new data.
  • The view controller also uses KVO to observe the isLoading, and stops/hides the loading indicator now that it is NO.
  • A manager singleton persists its objects to disk at some appropriate time (eg just after new data has arrived).

This works well when you may have multiple view controllers who are interested in a common set of data from the same manager. This method will elegantly prevent the multiple view controllers from kicking off multiple loads from the server. This is very important for iPad apps where you’ll likely have multiple view controllers contained on the one screen, eg a UISplitViewController setup.

You may consider simplifying this workflow if eg you will only ever have one view controller querying the manager, common in simpler iPhone apps. In cases like this, you could consider using callbacks instead of KVO.

Swift

One issue with the aforementioned architecture is that KVO appears to be unidiomatic in Swift. You need to inherit NSObject, sprinkle @objc around the place, and other tricky things to make it work. It appears likely that a future version of Swift will bring us a successor to KVO, but in the meantime here’s what I recommend you consider:

Lets borrow C#’s concept of Events and combine it with Swift’s weak delegates to get a simple effective solution. Basically we’re trying to create a manager which can have multiple weakly-referenced delegates. Here’s how it would look:

/// Since there can be multiple delegates, they can only
/// be used to signifying events (output), not for
/// supplying data for the manager (input).
@objc protocol MyManagerDelegate {
    func manager(manager: MyManager, isNowLoading: Bool)
}

@objc class MyManager {
    /// Multiple delegates.
    let delegates = NSHashTable.weakObjectsHashTable()
    
    // Add a delegate.
    func addDelegate(delegate: MyManagerDelegate) {
        delegates.addObject(delegate)
    }
    
    /// Example of how you'd call the multiple delegates.
    private func tellDelegatesIsNowLoading(loading: Bool) {
        for object in delegates.allObjects {
            let delegate = object as MyManagerDelegate
            delegate.manager(self, isNowLoading: loading)
        }
    }
}

I’ll leave it up to your discretion whether you think the above is worse or better than KVO in Swift. I’ll concede that it is indeed debatable ;)

Update: Better alternatives may be possible.

Data Stores

As for saving your data to ‘disk’, you’ve got a lot of options. From my experience, the good options are as follows: (in order of simple to complicated)

  • NSCoding + plists
  • YapDatabase (simple object store)
  • Sqlite + FMDB
  • Realm (CoreData done right, still can’t pass objects between threads)
  • CoreData

Only you can really decide which of the above are right for your app, as it depends on many factors.

I’d recommend starting with NSCoding for simple apps, moving to YapDatabase if you need further functionality, and Realm if you’ve got a big enterprisey app with a team full of smart people who can handle the complication (eg threading implications) of a proper ORM.

Kudos belongs to Tom from BareFeetWare for the discussion which led to this post!

Thanks for reading! And if you want to get in touch, I'd love to hear from you: chris.hulbert at gmail.

Chris Hulbert

(Comp Sci, Hons - UTS)

Software Developer (Freelancer / Contractor) in Australia.

I have worked at places such as Google, Cochlear, Assembly Payments, News Corp, Fox Sports, NineMSN, FetchTV, Coles, Woolworths, Trust Bank, and Westpac, among others. If you're looking for help developing an iOS app, drop me a line!

Get in touch:
[email protected]
github.com/chrishulbert
linkedin



 Subscribe via RSS