Glue factories

· 4 minute read

Before getting into iOS development, I used to build server-side web applications. In doing so, we’d write code that would read many different values from configuration files. In Objective-C or Swift, you’d simply create constants for these things. But we used configuration files on the server for one very powerful reason: in order to tweak a value, you could simply patch the text file and bounce your application server, without recompiling any code.

Of course, code that reads from disk isn’t particularly testable. Rather than mock out the file system access, we’d create factory classes that would simply read our config file and inject the values into our class’s constructor. In pseudocode, it’d look something like this:

final class ObjectFactory {
    private let properties: Properties

    init() {
        properties = Properties(path: "/path/to/file")
    }

    func object() -> Object {
        return Object(value1: properties.value1, value2: properties.value2, value3: properties.value3)
    }
}

The ObjectFactory class might look somewhat pointless, but it serves an important role: it keeps our Object class extremely testable. The factory class itself is merely configuration – glue code, if you will. There’s nothing there to test, but it allows Object to take all of its inputs in its constructor which makes it trivial to test.

In iOS development, we don’t have this same deployment concern (sadly, changing a value in a running production application isn’t as easy as swapping one config file out for another). But that doesn’t mean there aren’t cases in which the factory pattern can help yield similar benefits when it comes to keeping your classes decoupled and flexible.

Perhaps you’re starting a new application from scratch today. You can architect your codebase in such a way that your dependencies are always passed in and it’s easy to keep your different classes isolated and testable. This is idealism worth striving for but unfortunately not the environments that many of the apps that we know and love are developed in.

When working on a legacy iOS codebase, you likely have a number of intertwined, coupled dependencies that aren’t particularly testable. Maybe you have a single Core Data controller, or a shared API client instance. Similarly, maybe your user defaults and keychain are stored in an app group that doesn’t particularly lend itself to easily unit testing. Your class might look something like this:

final class AuthenticationController {
    func login(credentials: Credentials) {
        APIClient.sharedInstance().authenticate(credentials) { result in
            if result.isSuccessful {
                CoreDataController.sharedInstance().createUser(credentials.userName)

                Keychain(path: "/path/to/app/group").saveTokens(result.tokens)
            }
        }
    }
}

Shared instances aren’t bad in and of themselves – there could be a perfectly valid reason for only having a single APIClient instance in a given application. What is bad is when classes throughout your application know that they’re accessing a single instance, as the result of using some global accessor to grab this reference.

It’d be pretty hard to write a test for this controller. Ideally, you’d pass API client, Core Data controller, and keychain instances into something that looks more like:

final class AuthenticationController {
    private let coreDataController: CoreDataController
    private let APIClient: APIClient
    private let keychain: Keychain

    init(coreDataController: CoreDataController, APIClient: APIClient, keychain: Keychain) {
        self.coreDataController = coreDataController
        self.APIClient = APIClient
        self.keychain = keychain
    }

    func login(credentials: Credentials) {
        APIClient.authenticate(credentials) { result in
            if result.isSuccessful {
                coreDataController.createUser(credentials.userName)

                keychain.saveTokens(result.tokens)
            }
        }
    }
}

And now, testing becomes a lot easier:

final class AuthenticationControllerTest: XCTest {
    private let coreDataController = InMemoryCoreDataController()

    private let tokens = Tokens(token: "a4ka3", secret: "pk601n")
    private let APIClient = TestAPIClient(tokens: tokens)

    private let keychain = Keychain(path: "/tmp/location")

    private let authController = AuthenticationController(coreDataController: coreDataController, APIClient: APIClient, keychain: keychain)

    private let userName = "bryan"
    private let credentials = Credentials(userName: userName))

    func testSuccessfulLoginCreatesUser() {
        authController.login(credentials: credentials)

        XCTAssertEqual(coreDataController.user.name, userName)
    }

    func testSuccessfulLoginPopulatesKeychainWithTokens() {
        authController.login(credentials: credentials)

        XCTAssertEqual(keychain.tokens, tokens)
    }
}

Rather than a separate factory class, could we simply give our object a new class method or convenience initializer that returns a configured instance? Or configure our object’s constructor with shared instances using Swift default parameters? These approaches would help with testability but not portability. By breaking out a separate class, the authentication controller itself can now be moved into a framework, while the factory class stays specific to our application.1 The framework remains generic and oblivious to the existence of our application’s global accesors.

Let’s assume that – despite how nice it’d be – that it isn’t practical at this point in time for us to rewrite our application to make it particularly easy to get rid of all of these sharedInstance accessors. Introducing a factory is an easy way to avoid reworking our application to go all-in on dependency injection, but still keep our class’s logic isolated and testable.

final class AuthenticationControllerFactory {
    class func authenticationController() -> AuthenticationController {
        return AuthenticationController(
            coreDataController: CoreDataController.sharedInstance(),
            APIClient: APIClient.sharedInstance(),
            keychain: Keychain("/path/to/app/group")
        )
    }
}

Glue code. A throwaway class. When we get some time to pay off some of our technical debt, we can probably get rid of it. But in the interim, we’ve isolated our global access to a single place that we can blissfully ignore to when it comes time to write tests for or reuse our AuthenticationController.

  1. A class method or convience initializer in a Swift extension or Objective-C category would also work, if you find that approach to be a bit more familiar. Though factory methods are prevalent throughout the iOS SDK, factory classes aren’t. I still prefer them personally due to how obvious their single purpose ends up being. 

Just watch

· 0 minute read

Having a sensor-packed computer on your wrist for identity/payments/etc. is going to be awesome in like, three or four years, when sensor technology is better, battery life is improved, the cases are a bit smaller, and crucially, there are way more Internet-connected devices to interact with both in our homes and while out and about.

In the meantime, hearing people discuss smartwatches’ merits – or lack thereof – will become unbearable. It already is.

Apple is smart to release it now and to frame the narrative largely around getting people interested in actually being seen wearing it, followed by what I can only assume will be years of both working hard and waiting patiently for the rest of the pieces to fall into place. If anyone can afford a long play like this, it’s the company with $160B in cash. It’s a much longer play than the iPhone or iPad were. Not to mention that it’ll probably generate a shit-ton of revenue in the interim.

It’s not the end of a trek but we’ll be reading articles that treat it as such for quite some time.


Originally published on Tumblr

We need a “Safari view controller”

· 2 minute read

In-app web browsers are extremely common on iOS, which shouldn’t be surprising at all. They allow developers and users alike to quickly view something on the web without being completely removed from the app they were using just moments prior. Many developers prefer this because it presumably leads to more usage of their applications1. Many users like this because it provides a faster and cognitively lighter experience:

But in-app browsers have some pretty massive downsides as well. They can’t access cookies stored by other in-app browsers, nor Safari, requiring the user to repeatedly log in to websites that they should already be authenticated with. iCloud Keychain is great for syncing credentials across devices, but while Safari has access to its data, in-app browsers don’t2. This isn’t merely Apple being punitive – it’d be horribly negligent to give third-party applications access to this kind of information.

That said, it’s also negligent to ask users to enter passwords into in-app browsers to begin with. Third-party developers can easily access anything that a user types into their applications, even if the website being accessed is viewed over HTTPS. While Apple has the power to help mitigate this problem, they’re encouraging it instead, insisting that developers use in-app browsers for web-based authentication instead of a clunkier-yet-more-secure Safari-based OAuth flow, explicitly favoring user experience over security. There needs to be an in-between.

It’d be wonderful if Apple provided a “Safari view controller” that developers could present directly from within their applications. This controller would run out of process and work almost exactly like MFMailComposeViewController and MFMessageComposeViewController already do for composing emails and text messages respectively. The app would provide the controller with a URL (and optionally, a tint color), but otherwise what the user does in it would remain secure and isolated from any third-party code, yet fully integrated with Safari.app and Safari controllers presented by other applications.

iOS 8 share and action extensions are further proof that Apple thinks being able to display view controllers from one application inside of another strikes a great balance of security and user experience. Why not let us do the same with Safari as well?

Here’s a radar that you can duplicate if you feel the same way that I do about this.


Originally published on Tumblr

  1. Another reason to build in-app browsers in the past had been to augment them with functionality like “Save to Instapaper” and “Login with 1Password.” Now that iOS 8 extensions bake these capabilities into Safari itself, this is no longer a valid justification. 

  2. Authentication isn’t the only feature that non-Safari browsers lack out of the box. Third-party apps can’t integrate with a user’s bookmarks nor iCloud Tabs, either, just to name a few.