How to have a clean AppDelegate. A simple and elegant architecture

Hi from the codebug,

A long time ago the entire iOS community was presented with VIPER. It was a revelatory new architecture which split up nicely the responsibilities into business rules, data formatting, data presentation, data acquisition and made our iOS apps entirely testable. VIPER in its turn is an interpretation of Uncle Bob's clean architecture presented in episode 7 . Episodes 2-6 strongly recommended for fully understanding VIPER.

But VIPER doesn't and cannot solve the problem of large AppDelegate classes. How could it? The App delegate has tens of different responsibilities by default. Consider them. Setting up: crash reporting, analytics, feedback gathering, setting up a coredata stack, handling remote notifications, handling url schemes. If you keep all of them in the AppDelegate you get will suffer because:

  • You break the Single Responsibility principle, because the AppDelegate now handles too many things, changes for more than one reason
  • Developers can't parallelise work because they all work on the same class
  • Things which generally should not be together are now gobbled together in one giant class called AppDelegate.

Splitting things up to smaller methods is not a good idea here as this class still keeps a lot of responsibilities spread across smaller methods. We can solve the problem by using the same idea behind the Strategy Pattern

I call this solution App Services. Here is how it works:
The AppDelegate keeps pointers to different service objects in an array called services:

The idea is simple: The AppDelegate gets only one responsibility: informing all of its services what just happened: the app started, the app entered background, e.t.c.

We iterate through all services and inform them what just happened so they can perform specific work. This way your AppDelegate will have only one reason to change: Adding or removing services. Here is how the AppDelegate looks like:

class AppDelegate: UIResponder, UIApplicationDelegate {  
    var window: UIWindow?

    var services: [UIApplicationDelegate] = [
        PersistenceService(),
        AnalyticsService(),
        CrashReporterService()
    ]


    func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
        // Here is an example how you pass a message to your services
        for service in self.services {
            service.application?(application, didFinishLaunchingWithOptions: launchOptions)
        }
        return true
    }
}

Really simple. We inform each service something happened, and allow them to perform their work, without caring what that work actually is. The source code can be found here.

Each service responds to the UIApplicationDelegate protocol making it a miniature version of the AppDelegate, specialised in one thing.

In fact here is how the analytics service looks like:

class AnalyticsService: NSObject, UIApplicationDelegate {

    func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
        #if ALPHA
        //register with one id
        #else
        //Register with another one
        #endif
        //Analytics manager starttracking
        return true
    }
}

We give each individual feature its own service leaving us with a clean AppDelegate. It's a good idea to keep the services in one folder or group them somehow, so when thinking about what happens when your app launches you can quickly see everything in one place:

The high level philosophy is this: The AppDelegate shouldn't be concerned with anything except handling high level services. If you use this architecture and after a while you find yourself adding patches in the AppDelegate then the architecture failed you and this abstraction leaks. If you want to add a new responsibility to the AppDelegate, just create a new service.

PS: It remains an open question about the way services communicate with each other and left as a difficult exercise for the reader

The Code Bug

A passionate iOS developer. Looking to radically improve the way we all develop software.

Amsterdam