Posts

Swift materialized four years ago at WWDC in June 2014. At the outset, the language was intriguing, but the tooling was…let’s say minimal. Xcode has steadily improved since then — we can even refactor now! But one big chunk of the toolset hasn’t been Swiftified. Interface Builder (IB) stands stoic and steadfast in the face of change. Before we get into the Swift issues, let’s take a look at the history of IB.  

Back in the 80’s, some Very Smart People built the Objective-C language and its runtime which was the foundation of the NeXTSTEP operating system. Developers built user interfaces for NeXT computers using Interface Builder. (Yes, IB is 30 years old.) IB creates interface files called NIBs — NeXT Interface Builder. NeXT was acquired by Apple and the NeXTSTEP OS became the basis for Mac OS X, now macOS. macOS begat iOS and IB and its NIB files hung around, quietly doing their jobs. Today, we create user interfaces in Storyboard files, but under the hood, they’re just a bundle of NIBs and a bit of glue.  

So NIBs have been around for a while. They clearly get the job done, but how do they work? When you create an app’s GUI using IB, it snapshots the view hierarchy and writes it to a NIB. This is often referred to as a “freeze-dried” version of your UI. When the NIB loads at run time, the view hierarchy is rebuilt using the serialized info in the NIB along with UIView/NSView’s init(coder:) method. This init(coder:) uses an NSCoder instance to rebuild the view with all the settings from IB. And now comes the important part. Once the view is fully instantiated, the IBOutlets are hooked up via the Objective-C runtime, using setValue:forKey:. Because this happens after the view is created (ie, after init returns), the IBOutlets must be Swift optionals, since they are still nil after init completes. Also, because we’re using Key-Value Coding (KVC), IBOutlets are stringly (1) typed. If an outlet name changes, but the NIB isn’t updated, it’ll crash at runtime.   

When coding in Swift, we prefer immutability. We’d rather have a let than a var. Unfortunately, IB is built on the idea of on-the-fly mutation at runtime. The Objective-C runtime gives it the power to create objects and then monkey with them, trusting that the attribute names are valid and if they’re not? Crash. Game over. Thanks for playing. Please come again. So we cheat. Devs learn to mark all their IBOutlets with !, to forcibly unwrap the optional. This is an implicit promise to the compiler. “I know this is really an optional, but I swear on my CPU that it will never be nil when I use it.” Everyone has forgotten to hook up an outlet sometime. And it crashes. Force unwrapping an IBOutlet is a hack. It’s a shortcut to make it easier to take IB, a tool that relies on a weakly typed, dynamic runtime, and connect it with Swift, a strongly typed language.  

How we fix this? IB has been around for 30 years, relying on Objective-C’s loosey goosey ideas about types and KVC. How can Apple engineers Swiftify it? One way is to pass IBOutlet information to the init(coder:) method. Devs would have to implement hooking up outlets explicitly based on deserializing information in an NSCoder instance. That’s not always straightforward. NSCoder isn’t rocket science, but it’s a bit fiddly and verbose. One of the goals of Swift is give new devs less opportunity to shoot themselves in the foot. Writing this kind of intricate code just to hook up a textfield or a button isn’t very Swifty.

Let’s take a step back and consider similar problems. JSON decoding has highly similar requirements. In parsing JSON, you want to take a serialized format, dependent on string identifiers and map it into the attributes of a newly created Swift object. Early on, this was a common, tedious hurdle that developers had to overcome on their own. With Swift 4, the Codable protocol arrived, and made JSON processing so much easier, albeit through compiler magic (2). But Codable is a generic thing, not just for JSON. Out of the box, Swift comes with a plist encoder/decoder in addition to JSON. You can easily serialize any Codable object to a property list just as easily as JSON. And you can write your own encoder/decoder as well. This repo has examples of a dictionary encoder/decoder and several others.

What if you wrote a NIBDecoder for Codable and declared your view controller as a Codable object? The compiler generates an init(from:) method that takes the serialized data in the NIB file and instantiates your view object, including connecting the IBOutlets. Now it’s all Swifty! No more forced unwrapping of optionals because the outlets are all hooked up during init(from:). NIBs load now by using init(coder:), so this is a direct replacement, but instead of having a developer implement the complicated bits of deserialization, the compiler generates all the code. This would be simple for new developers, but provide the extensibility needed for complex situations to do it yourself.

The Codable approach also adds Swift style error handling. In the case where names don’t match up for some reason, the NIBDecoder can throw and the application has a chance to handle the error in a controlled way. Recoverable errors are a big upgrade from the existing situation, where any error causes an immediate and unforgiving crash. With non-fatal NIB errors, we can also think about handling these UI bundles in more dynamic ways.

I’m handwaving over some complicated processes and we haven’t even talked about backward compatibility or mixing old school NIB loading and this new Codable process. Maybe Codable isn’t the right process for this, but I think it’s tremendously similar. Maybe NIBs would get a new internal format (3) to support Codable deserialization. In any event, Swift is clearly the path forward and its time that Interface Builder evolve to work smoothly with Swift. It’s time to say goodbye to forced unwrapped IBOutlets and let Interface Builder embrace the goodness of immutable outlets. WWDC 2018 is days away, so fingers crossed.

1 – Swift is trying to avoid “stringly” typed items, even with KVC.  Swift 3.0 brought use #keyPath() which avoids the need to hard code strings in KVC code. Instead of using a string corresponding to the attribute name, like “volume”, you can give the dotted address of the attribute within the class, like #keyPath(AudioController.volume). With the hardcoded string, there are no checks until runtime. If it’s wrong, it crashes.  With the #keypath() version, the compiler finds this property at compile time and substitutes its name. If a name changes, compilation will fail and you can fix the issue. No more runtime surprises!

2 – The inner workings of Codable can get complex. It’s pretty simple for basic usage, but implementing your own encoder is a bit more intricate. Mike Ash has written a nice run down of the details. The magic part happens when the compiler generates encode(to:) and init(from:) methods for your classes that are Codable. The compiler knows all about the types of your attributes, so it can generate this correctly. You could hand write the code as well, but Swift introspection features aren’t mature enough yet that you could implement a Codable-like thing at runtime on your own. All the bits you need just aren’t there. But the compiler does a great job of implementing Codable at compile time, which’ll suffice for now.

3 – NIBs are all XML these days (XIB files). These are super complex files that are impossible to read, even though their text format invites you to try. If you’ve ever had two or more developers working on a project, it’s inevitable that the XIBs in your git repo will cause a “merge hell” situation. This would be a great time for Apple to implement a new kind of UI description file format. Maybe a well documented, clear, concise JSON format. Imagine how great it would be to be able to read the UI file. Or better yet, be able to programmatically generate one, should the need arise.

At Oak City Labs, we rely heavily on unit testing in our quality software process. Unit testing is the safety net that lets us reliably improve existing code. Our testing suite double checks that modified code still behaves the way we expect. I’ve written before (here and here) about how we use dependency injection (DI) which makes unit testing easier. DI helps us wrap code we need to test in a special environment. By controlling the environment, we can make sure that the code being tested gives the correct output for a given set of conditions. This works very well for pieces of the application that we can encase in a layer that we control. Unfortunately, we can’t wrap everything.

Consider our API layer. This is the part of our application that talks to the server over the internet. It makes network calls, processes replies and handles errors like slow network responses or no network at all. In testing this code, we want it to behave as normally as possible for accurate testing, so we still want it to make API requests and interpret the results. At the same time, these are unit tests, so they should be fast and not depend on external resources. We don’t want to make requests to a real server on the internet. If that test failed, it wouldn’t be obvious if our code was broken, the server was down or the network cable was mistakenly unplugged. It’s important that our unit tests be self contained so when something fails, we know that a specific section of code has failed and we can fix it ASAP.

Back in the old days, before Swift, we wrote in Objective-C. Swift is a strongly typed language where Objective-C is weakly typed. While weak typing in Objective-C often gave a developer enough rope to hang themselves, it was flexible enough to do interesting things like easily mock pieces of software. Using mocks, fakes and stubs, you could (with some effort) replace pieces of the system software with substitute code that behaved differently.  We could use this to test our API code by changing the way the system network routines worked. Instead of actually contacting a server on the internet, a certain call just might always return a canned response. Our API code wouldn’t change, but when it asked the system to contact the server for new data, our mocked code would run instead and just return the contents of a local file. By building a library of expected calls and prepared responses, we could create a controlled environment to test all our API code.

Swift, on the other hand, brought us strong typing, which wiped out whole classes of bugs and insured that objects were always the type we expected. We paid for this safety with the loss of the ability to easily mock, fake or stub things. Someday, Swift might gain new features that make this possible (maybe even easy) but for now, this is difficult to do with any efficiency. So, we need a new approach for testing Swift API code.

Like we said earlier, we don’t want to use external network resources to test our code because too many things are out of our control. But what if the test server were running on the development machine? In fact, what if the test server were running inside our application? Enter two open source projects — Ambassador and Embassy from the fine folks at Envoy.com.  Embassy is a lightweight, dependency free web server written in Swift. Ambassador is a web framework that sits on top of Embassy and makes it easy to write a mock API server.  

In this new approach to testing our API layer, we’ll write some Ambassador code to define our mock endpoints. We’ll declare what URL path they’ll respond to and what response the endpoint will return. Now, inside our unit test, we’ll fire up the mock server and run it on a background thread.  Since you’re controlling both the client and server side of the request, you can make asserts on both ends of the exchange. On the server side, you can validate the data submitted by the client. On the client side, you can ensure that the response from the server was interpreted correctly. Ambassador has some nice conveniences as well for working with JSON data, introducing delayed responses from the server and other common testings needs.

In order to use our freshly built API mock server, all you need to do is change the server name used by the API layer. This is important because we don’t want to make significant changes to our code in order to test it. We want to test as close as we can to production code. By switching our base URL from “https://api.ourserver.com” to “http://localhost:8080”, we can test all our network requests with no external dependencies. Since we’re using dependency injection, this change is very simple to implement in our unit testing routines.

The move from Objective-C to Swift has allowed us to write cleaner and safer code, but the price we pay is the loss of the fast and loose, über permissive Objective-C runtime environment. Fast and loose always caused more problems than it solved, so I’m happy to see it go. A few of our existing solutions have gone with it, but with a bit of ingenuity, we can move forward with better and safer new solutions.

 

At Oak City Labs, potential clients often ask if we write apps using React Native. Why not? Isn’t that the fastest way to market — write one app for both iOS and Android? That’s the crux of React Native’s pitch. Don’t spend time writing two apps when you can write a single React Native app instead. As CTO of a mobile dev shop, I should be able to answer that, so I’ve started doing some research on what it takes to be a React Native developer. How does React Native compare to Swift/Java development in terms of efficiency, stability and maintainability? I’ll walk through some of the things I’ve found.

Just a note before we dive in: this article addresses the idea of writing apps for iOS and Android in JavaScript, focusing on Facebook’s React Native implementation, not to be confused with the reactive programming model, which may be a compelling alternative to the traditional declarative programming style.  Reactive programming is a topic definitely worth following.  

Toolboxes

How many tools does a developer need to build an app? As an iOS developer, I live in Xcode, provided by Apple and designed to build apps for the Apple platforms. Well over 95% of my development time is spent in Xcode. Apple also provides Instruments, a suite of testing tools to examine memory, CPU, etc in your app. Occasionally, I use Instruments to track down a particularly difficult bug. That’s it — Xcode and a little bit of Instruments.

The situation for Android is even a little easier. Android developers use Android Studio, a tool provided by Google with the sole purpose of creating Android Apps. Features like memory analysis and CPU monitoring are built in, so there’s really just one hammer in the toolbox. Android developers live in Android Studio.

Now onto React Native. It’s hard to find data on the amount of shared code in React Native apps, but conversations like this one suggest it can be 80% or 90%. That’s still a significant amount of platform specific code. Let’s assume we’re building an average app that has at least 15% platform specific code.

React Native developers have a bigger hill to climb just to get started. According to their instructions, here’s the list of software to install to build a cross platform app.

  • Homebrew — A package manager that makes it easier open source tools on your Mac
  • Watchman — A utility from Facebook to watch the filesystem for changes and run commands in response to those changes
  • Node — A javascript runtime built on Chrome’s V8 JavaScript engine, often used for server-side JavaScript
  • NPM — Part of Node, this is another package manager for managing JavaScript components
  • react-native-cli — Command line interface for for interacting with the React Native environment
  • Xcode — Necessary for various iOS tools
  • Android Studio — Necessary for various Android tools

In this article, Tony Mann suggests you’ll need these other tools as well.

  • Flow — A static type checker for JavaScript
  • Chrome Debugger — Chrome’s JavaScript debugger which can attach to your React Native application
  • Babel — A JavaScript to Javascript compiler

Now you’ve got everything you need to build a React Native app… except a text editor, so find one of those too.

So here’s the rundown for setup requirements

Platform Number of Tools Required
iOS 2
Android 1
React Native 10+

 

Right out of the gate, the bar is set relatively high for a React Native developer to get up and running. If this were a one-time penalty, it would be easy to write off. One day lost to setup on a six-month project isn’t significant, but this represents a whole dependency tree. Any update in one component can have a cascade effect that forces upgrading other components. Maintaining this whole setup now becomes overhead that the React Native developer must deal with. This kind of yak shaving can regularly consume a day of developer time.

Writing Code

I’m an iOS developer, so I’ll address the writing of code as a discussion of Swift vs JavaScript. For the sake of brevity, let’s assume Java (or Kotlin) developers make similar arguments.

JavaScript is not a nice language. It’s stone aged tech compared to Swift. Ariel Elkin does a fantastic job in this article walking through the many technical shortfalls of the language. Some of the highlights include

  • Weak typing
  • Lack of optionals
  • Lack of functions signatures

Issues like weak typing and lack of optionals are specific issues from Objective-C that Swift was designed to solve. In my experience, we always struggled with nil pointer exceptions in Objective-C. A rogue nil was the root cause of the vast majority of crashes in our applications. These have all but disappeared with Swift. A whole class of very common crashes has been fixed by using a language that simply doesn’t allow it. The rare nil pointer crash now usually has to do with interacting with legacy Objective-C.

Strong typing, optionals, and other features of Swift let me quickly write expressive, memory safe code that won’t crash. The compiler makes sure of that. As Elkin points out, these crashes happen frequently with React Native. JavaScript for app development is a step (or leap?) backward technically. If our goal is efficient developers, we should empower them with the best tools available.

Testing is an integral part of our app development process at Oak City Labs. One of the best ways to encourage developers to embrace testing is making it as painless as possible. With React Native, developers get another stack of dependencies to maintain just to get the unit testing framework running. In this article, Carlo Francisco goes over the testing stack they use at Refinery29 to unit test their React Native code. It’s based on Jest and Calabash / Cucumber. Jest is a JavaScript unit testing frame. Calabash and Cucumber are used together for application level acceptance testing. Calabash and Cucumber (and any customizations) are written in Ruby. The actual Cucumber tests cases are written in another language called Gherkin, one of those terrible languages for non-developers which are still too difficult for non-developers and too weird and restrictive for developers.

It’s great that there are testing mechanisms for React Native, but in order to accomplish real testing, we’ve got to add another few rooms on to the house of cards we’ve built so far. Not only do we add more 3rd party JavaScript frameworks, but we can also tack on extra languages — Ruby and Gherkin — in order to implement application level testing.

Compare this to iOS development in Xcode, which provides XCTest for unit testing and XCUITest for application testing, all written in Swift. Likewise, Android developers have JUnit and Espresso for unit testing and application testing respectively.

Stability is definitely a casualty here, mostly because of the limitations of JavaScript. React Native also loses ground on maintainability as testing tools require more third party components be placed in our growing dependency tree. I worry about efficiency too since testing now requires a React Native developer to know even more languages.

Debugging Code

Finally, once the code is written and running, there’s always debugging to do. According to React Native’s documentation, there’s no one stop shop for React Native debugging. There’s an in-app developer menu that opens the door to turn on/off some debug features and provide an onboard inspector. Using the Chrome browser’s remote debug feature seems to be the most powerful way to connect to the React Native app and view internals. There’s also a standalone version of the React Dev Tools to use when you can’t connect with Chrome’s debugger. And finally, there are the native debuggers in Xcode and Android Studio when you need to debug pieces of native code.

Debugging apps written in the native language is much more straightforward. To debug a Swift app in iOS, run it from Xcode and debug. For an Android app, run it from the Android studio and debug. It’s such an integrated part of the development cycle with the native tools, it’s easy to take it for granted.

With no one definitive debugging environment, I worry about a React Native developer’s ability to efficiently track down a bug. I assume one would start debugging in one of the JavaScript console tools, but then you might have to jump to a native tool. As context switching goes up, efficiency goes down.

Tool Quality

I’d also like to comment on the tool quality. Much of React Native’s tool chain, react-native, npm, etc, is executed at the command line. While some developers will praise the hard core grit of the command line, (“Real developers type, not click!!”), I find that it increases the entry-level barrier for new developers and generally causes friction for developers at any level. Trying to remember the flags for subcommands of the react command line tool isn’t going to help ship an app faster. Compare that to a button or menu item in a more robust tool like Xcode or Android Studio. The cognitive load added by a bunch of command line tools is just another stone weighing down the React Native developer and causing efficiency to sink.

Adding It All Up

At the end of the day, the React Native developer needs quite a big toolbox to fit all their tools in. Here’s the list:

  • Homebrew
  • Node
  • Watchman
  • NPM
  • react-native-cli
  • Flow
  • Chrome-Debugger
  • Babel
  • Xcode
  • Android Studio
  • Standalone React Dev Tools
  • Jest
  • Calabash
  • Cucumber

In order to use all these effectively, the React Native developer also needs to have a working knowledge of these programming languages:

  • JavaScript
  • Swift/Objective-C (iOS native components)
  • Kotlin/Java (Android native components)
  • Ruby (Cucumber testing)
  • Gherkin (Cucumber testing)

The single platform iOS developer needs Xcode (and maybe Instruments) to write, test and debug applications in Swift. Likewise, the Android developer needs Android Studio to write, test and debug apps in Java and/or Kotlin.

For a shop that has experienced Swift/Java developers, it’s very clear to me that there is zero reason to switch to the React Native development stack. We’re concerned about efficiency, stability and maintainability. The enormous number of tools required for React Native along with the piecemeal nature of programming environment are going to tank the efficiency of an React Native developer. Even the world’s best JavaScript developer is going to face an uphill battle on this unlevel playing field. JavaScript as a language is the biggest barrier to stability. JavaScript, by itself, is a deal breaker for us. Maintainability is another worry with so many dependencies from so many sources and keeping it all playing well together. (Not to mention dependency on Facebook’s ongoing support after the Parse.com incident.)

I believe in using the right tool for the right job. Writing mobile apps in Swift/Java is the quickest, most friction free path to shipping apps to customers. I can understand how React Native appeals to web developers, offering to turn their JavaScript experience into mobile apps, but there is no free lunch. It may work in the end, but JavaScript (plus a lot of frameworks) can’t match apps written in the native toolsets when it comes to quickly and efficiently shipping a high quality, maintainable native app.

Last time on Pragmatic Dependency Injection in Swift, we discussed the horror of global variables and the duplicity of singletons, globals in disguise. Dependency Injection (DI) can help us avoid these pitfalls, but how do we implement DI in Swift without incurring a prohibitive amount of overhead? To find out, we join our regularly scheduled blog post, already in progress…

DI in Swift

So, DI is awesome. Now I want to use this in my Swift project. Let’s consider a standard mobile app. You might have a whole list of objects that had been singletons in the past. Some devs like to call these Manager objects.

  • LocationManager — keeps track of GPS coordinates
  • DataManger — handles data storage
  • ApiManager — coordinates communication to a server
  • LogManager — aggregates debug/error messages to multiple destinations
  • NotificationManager — receives and interprets APNS notifications

And that’s just the tip of the iceberg. In a current project, I’ve got fifteen of these manager style objects. With DI, we need to pass these down through our object hierarchy, but all those init() methods start to look a bit cumbersome with fifteen arguments. Maybe we could just pass what’s needed? That’s a good idea, except the table view cell ten levels deep in the view controller hierarchy suddenly needs access to the LocationManager. You’ll have to create a path to pass it all the way down through the full view controller tree.

So we’ll need to pass everything down, but clearly it’s too cumbersome to do that for each Manager. Also, if you add a new one, you’d need to update a ton of code. We need some kind of Manager holder to encapsulate these into one object that can easily be passed through our application. I like to think of it like a drink caddy that holds multiple bottles of sweet cherry goodness.

Now that we’re moving away from singletons, we’ll change the name from Manager to Controller. Our container object is really a Controller for Controllers, so we’ll call it a MetaController.

Show Me the Code!

Let’s talk about semantics for just a moment. When I say we’re moving away from singletons, I mean the “singleton pattern” of an object that guarantees that it’s only instantiated once and accessed through a static method, like a global variable. In most cases, our Controller objects in the app are single instances, but they are created like normal objects and explicitly passed around via the MetaController. Nothing prevents you from creating more than one of the controller objects, but it’s just not necessary in most cases. If you do need more than one, you have that option.

Back to the MetaController. Here’s what it looks like in basic form.

class MetaController {
    var locationController: LocationController? = nil
    var logController: LogController? = nil
    var apiController: ApiController? = nil 
    var dataController: DataController? = nil 
    var notificationController: NotificationController? = nil 
}

And that’s it! Okay, it’s just the beginning. Like other components in the system, the controllers themselves often need access to the other controllers in the system. For example, the ApiController needs access to the LogController to log network issues connecting to the server. To make this more convenient, we add a little didSet action in the MetaController to pass the MetaController itself to the payload controllers.

class MetaController {
    var locationController: LocationController? = nil { didSet { passController(locationController) }}
    var logController: LogController? = nil { didSet { passController(logController) }}
    var apiController: ApiController? = nil { didSet { passController(apiController) }}
    var dataController: DataController? = nil { didSet { passController(dataController) }}
    var notificationController: NotificationController? = nil { didSet { passController(remoteNotificationController) }}

    func passController(target: AnyObject?) {
        if var target = target as? MetaControllerConsumer {
            target.metaController = self
        }
     }
 }

Here’s our first glimpse of the MetaControllerConsumer protocol. This swift protocol is adopted by every object in our system that uses the MetaController. The basic definition is super simple and just declares that the consumer will have a place to store the MetaController

protocol MetaControllerConsumer {
    var metaController: MetaController? { get set }
}

And that’s all that’s necessary, but we want to make this easy to use as well. Every time we access the dataController, it would be a pain to type metaController?.dataController. The great thing about Swift protocols is that you can add default implementations for protocol compliance. This lets us expand the properties required by the protocol, but also define default implementations of synthetic properties to fulfill the protocol requirements. That’s a mouthful. Here’s what that looks like:

protocol MetaControllerConsumer {
    var metaController: MetaController? { get set }
    var locationController: LocationController? { get }
    var logController: LogController? { get }
    var apiController: ApiController? { get }
    var dataController: DataController? { get }
    var notificationController: notificationController? { get }
}

// default protocol implementation to return the sub controllers from the meta controller
extension MetaControllerConsumer  {
    var locationController: LocationController? {  return metaController?.locationController }
    var apiController: ApiController? { return metaController?.apiController }
    var dataController: DataController? { return metaController?.dataController }
    var userController: UserController? { return metaController?.userController }
    var remoteNotificationController: RemoteNotificationController? { return metaController?.remoteNotificationController }
}

A MetaControllerConsumer still only has to declare a real property called metaController to hold the container, but can now access the sub-controllers like dataController directly via the default implementation of the computed properties.

We can add more default implementations for other convenience operations too. Logging is a good candidate for these sorts of shortcuts.

extension MetaControllerConsumer {    // Logging shortcuts
    func debugLog(message: String, _ path: StaticString = #file, _ function: StaticString = #function, _ line: UInt = #line){
        logController?.debugLog(message, path, function, line)
    }
    func errorLog(message: String, _ path: StaticString = #file, _ function: StaticString = #function, _ line: UInt = #line){
        logController?.errorLog(message, path, function, line)
    }
}

With this in place, the logging functions debugLog() and errorLog() are now available in every MetaControllerConsumer. By leveraging Swift protocols like this, it’s easy to add general methods in one place that are accessible everywhere.

BTW, the #file, #function, and #line arguments in these functions are tokens that the compiler recognizes and replaces with the current filename, function name, and line number. Adding a debug statement like debugLog("Bazinga!") can print out

LocationController.swift (line 34): updateLocation(): Bazinga!

which is pretty handy.

Another useful function allows us to quickly pass the MetaController to another object, using the MetaController itself to first check if the target object can consume it.

extension MetaControllerConsumer {
    func passMetaController(target: AnyObject?)         
        metaController?.passController(target)
    }
 }

This is especially useful when programmatically instantiating a new object in the code or configuring a child view controller in prepareForSegue().

We load up the MetaController early in the app bootstrap process (from the app delegate’s didFinishLaunchingWithOptions) and pass it down through our object hierarchy. Initialization is really straightforward.

metaController = MetaController()
metaController?.logController = LogController()
metaController?.locationController = LocationController()
metaController?.apiController = ApiController()
metaController?.dataController = DataController()
metaController?.notificationController = NotificationController()

The MetaController is also handed to the root view controller which handles propagating it down through the view controller ranks.

Testing

With the MetaController construct, we reap all the normal DI benefits for testing. For each test, we simply construct a new MetaController, add the controllers that the test needs (replacing with “Fake” subclasses as necessary) and go. This is a little bit of compromise, since we need to know which sub-controllers are required by a class, but it’s a trade-off for the convenience of this proxy model of direct injection. In the strictest sense, we could still provide a fully loaded MetaController if the software under test were truly a black box. In the end, we still have a highly testable framework where we can easily satisfy dependency requirements and create robust, maintainable, explicit test suites.

Solving DI with Swift

Building an app by relying on singletons quickly leads to a mine field of Spooky Action, unintended consequences and unexpected explosions. By moving to a direct injection model, we remove the magic and hidden pitfalls of implicit dependencies and replace them with clear and explicit declarations of dependencies between the components of our app. With the MetaController model and heavy use of Swift protocols, we’re able to encapsulate the overhead of DI in a way that makes it easy and fast to apply DI to our app development process.

At Oak City Labs, we’re always trying to find ways to improve our code’s stability, testability and maintainability. Today we look at the problem of singletons, global state and how to avoid these landmines in the context of Swift.

The Problem

Let’s talk about global variables. Most everyone can agree that global variables are A Very Bad Thing. Globals are undoubtably a code smell and religiously avoided by experienced programmers. Globals are problematic for lots of reasons, but mostly because of Spooky Action At A Distance. If two pieces of code both access a global variable, they can interact via the global variable, often in unexpected and undesirable ways. In this article, Misko Hevery (the Angular guy) dives deep into the many ways this can go pear shaped quickly.

New developers sometimes try to fix global variables by switching to singletons and maintain state within a magic object that is just a static method call away. I’ve heard more than one developer exclaim “Oh no, we never use global variables. We use singletons instead.” The ugly truth though, is that singletons are really just fancier global variables. You still get Spooky Action. You still have implicit and hidden dependencies. Try to write a unit test on a class that relies on a singleton. You can’t look at the exposed interface for this class (think header file) and know about the secret, hidden dependence on that singleton. You must inspect the code and it’s a matter of tedious and confusing mocking of static class methods to try and make this testable. Then write a second test and discover that the tests fail depending on the order in which they run. It’s crazy making!

The most disappointing aspect of singletons is that Apple uses them so often in their own API — UIScreen, NSNotificationCenter, NSUserDefaults, etc. It seems as if they’re encouraging us to use singletons, even when it makes applications fragile and testing difficult.

The Solution — Dependency Injection

“Dependency Injection” (DI) sounds intimidating. And complicated. And scary. People write long, drawn out blog posts about it. But it’s not nearly as intimidating as you might think. At the core, DI just means that you explicitly supply a class with any dependencies, either through initialization or a method call.

Consider this class that uses a two singletons:

class SocialInfo {
    func connectionCount() {
        let twCount = TwitterApi.sharedManager.followerCount
        let fbCount = FaceBookApi.sharedManager.friendCount
        return twCount + fbCount
    }
}

The connectionCount() function returns the total number of connections on FaceBook and Twitter, using the singleton managers to access those two different APIs. But if we just look at the API, it’s not clear that SocialInfo depends on FaceBookApi and TwitterApi. For all we know, it’s valid to write

let count = SocialInfo().connectionCount()

but looking at the code for that class, we can tell that TwitterApi and FaceBookApi need to be set up and configured first. Imagine if this were a closed source library. We would have no way to know about these implementation details. And we shouldn’t have to care about implementation details anyway.

Now think about what it means to test the SocialInfo class. Our tests shouldn’t rely on network resources, so we’re going to have to mock those singletons. I’ve used several mocking libraries and these are always finicky at best. In Swift especially, the mocking libraries are quite young and this sort of thing is still difficult.

Now let’s consider the same class, rewritten a little for DI.

class SocialInfo {
    let twApi: TwitterApi
    let fbApi: FaceBookApi

    init(twitterApi: TwitterApi, facebookApi: FaceBookApi) {
        twApi = twitterApi
        fbApi = facebookApi
    }

    func connectionCount() {
        let twCount = twApi.followerCount
        let fbCount = fbApi.friendCount
        return twCount + fbCount
    }
}

With the updates, it’s clear that SocialIinfo depends on a valid instance of TwitterApi and FaceBookApi. Getting a connection count looks like

let count = SocialInfo(twitterApi, facebookApi).connectionCount()

It’s clear that we need to have a configured instance of the Twitter and Facebook API objects ready to pass in so that SocialIinfo can use them to connect with the services.

And testing is much easier too! Instead of having to build mock objects when can create FakeTwitterApi and FakeFaceBookApi objects that are subclasses of our real objects and just hardwired to return known values for the counts. This is really straightforward, readable, and doesn’t require any mocking.

So that’s dependency injection — simply the idea that dependencies are passed in explicitly rather than pulled out of the ether via some magical singleton or (gasp!) global variable.

Tune in next time for the exciting conclusion and the implementation of pragmatic dependency injection in Swift.