Loose coupling using default implementations in protocol extensions

When setting up Firebase in your app you have to call an API to configure it. It should be done once, at launch time, so you know where this is headed, you put it in the AppDelegate. Or at least that's what you're told to do.

The offending integration instructions

There are plenty of 3rd parties that require some form of configuration at launch, all of which tell you to put the code in the AppDelegate. The AppDelegate is fast becoming as massive as a neglected ViewController, it's a dumping ground for any and all setup code. 

And not just a dumping ground, a tightly coupled dumping ground. 

Firebase tells you to `import Firebase` in AppDelegate.swift and call FIRApp.configure() on launch. But now we have a tight coupling between the AppDelegate and Firebase. I'm not happy with that.

There is a difference between an app that is built on Firebase and an app that uses Firebase. And that's the same difference between a tightly coupled and loosely coupled app.

I want as few files in my project to be aware of Firebase. The lower the number, the looser the coupling. (Ok, this is a pretty poor metric, I could just put a hundred classes and structs into a single file and I'd only write import Firebase once, but as long as I don't do that it's a valid metric, and I try to to restrict myself to a single class or struct per file)

So how do we decouple the AppDelegate from configuring a backend service such as Firebase? Our first step should be to abstract away the concept of configuration, and leave the concrete configuration for later. As always, we use a protocol

Now we add this protocol conformance to AppDelegate and call the configuration method on launch.

Of course, this will fail to compile, AppDelegate conforms to the protocol, but it doesn't implement it. Nothing is configuring Firebase at this point.

We could now add the implementation in AppDelegate, in which case we've achieved nothing. Instead we're going to leverage default implementations in protocol extensions to keep the call to Firebase away from AppDelegate.

But, once again, we've achieved nothing. The coupling is still there, it's just in a different file.

So, here's the trick. While it's standard to put the protocol extension in the same file that defines the protocol, there's no rule that says it's mandatory. You can put them anywhere. It's probably super dangerous to put them anywhere else unless you have a good reason, but this is a great time to do so.

Somewhere in your code you're going to have a Firebase wrapper, and that is the file that is going to import Firebase. So we can take the extension containing the default implementation out of the protocol's file, and move it to the Firebase wrapper.

Now all of our code is nicely decoupled.

1. AppDelegate knows nothing about Firebase, it just calls the code to configure a backend.

2. The backend configuring protocol is declared without any knowledge of a default implementation.

3. The Firebase wrapper contains the Firebase-specific default implementation for backend configuration

If at some point in the future Parse rises like a phoenix and you want to replace Firebase, you remove the firebase wrapper file and replace it with your Parse wrapper, plus a Parse-specific default implementation for BackendConfiguring. You can only have one such default implementation, so the compiler will stop you from accidentally enabling two different backends.