Dependency Injection in Swift with the help of Sourcery

Dependency Injection is a really simple concept allowing among other things to decouple the different components of your application’s code (and it’s not just about getting rid of these nasty shared instances!).

There is already a ton of resources online that talks about it. There are also different implementations for Swift projects, but not of them were quite fitting to my needs as they are often:
– done at compile time, meaning that your app can crush at runtime if you mis-configured something and you could notice it too late when your app is already released in production
– not type safe, passing optionals all around
– asking for ton of boilerplate
– not that easy to use when it comes to testing
– not very opinionated on what belong to a centralized dependency injection container and what should just be passed around

In a decently sized project I’m working on, transitioning from all the Singleton design pattern used all over the project with shared instances representing most of my controllers, managers and services to something where dependencies are properly injected asks for a big deal of time and re-architecting.

The following is an experimental solution, even though it works in a small project, it’s really easy to generate cyclic dependencies and have your project crash at run-time — do not use in production.

Among all possible dependency injections (initializers, properties, methods, ambient, etc.), I believe passing down dependencies during components initialization is the cleanest way as it allows compile-time enforcement and remove the need for optionals and/or force unwrapping. Sadly, passing down dependencies from where there are likely being initialized (often the root of the application, like the AppDelegate) throughout the app isn’t really something you would consider clean. For example, if a component six levels down the navigation needs a particular dependency to function, you would have to pass it through the five first view controllers for it to be available during the sixth component initialization, this isn’t really elegant as most of these view controllers probably won’t need it and it’s simply tightening up the whole dependency graph for nothing.

With the rise of the Coordinator design pattern, it become easier to centrally manage dependencies. Coordinators is the one place responsible for creating view models and view controllers and can therefore pass on the dependencies in initializers. This is convenient and already cleaner, but let’s imagine this view that requires a lot of dependencies:

class SomeViewModel {
  // MARK: - Initializers
  init(dependencyA: DependencyA, dependencyB: DependencyB, dependencyC: dependencyC, ..., dependencyX: DependencyX) {
    self.dependencyA = dependencyA
    // ...
  }

  // MARK: - Dependencies
  let dependencyA: DependencyA
  let dependencyB: DependencyB
  let dependencyC: DependencyC
  ...
  let dependencyX: DependencyX
}

Now let’s look at the component initializing this view model:

class SomeCoordinator {
  let dependencyContainer: DependencyContainer // For our own sake, let's at least imagine dependencies are centralized somewhere

  func showSomeViewController() {
    let viewModel = SomeViewModel(dependencyA: dependencyContainer.dependencyA,
                                  dependencyB: dependencyContainer.dependencyB,
                                  dependencyD: dependencyContainer.dependencyD,
                                  ...
                                  dependencyX: dependencyContainer.dependencyX)
    let viewController = SomeViewController(viewModel: viewModel)
    navigationController.push(viewController: viewController, animated: true)
  }
}

By now, you must certainly see where I’m going: there is a ton of boilerplate to write just to pass down these dependencies. It’s super annoying to write, and it’s taking a huge amount of space in your code making it not as clear and readable as it should be.

But what solutions do we have? Let’s take it bit by bit.

Surely we could fix the initializer signature from getting too many parameters by grouping the dependencies into some kind of data structure:

class SomeViewModel {
  // MARK: - Initializers
  init(dependencies: SomeViewModel.Dependencies) {
    self.dependencies = dependencies
  }

  // MARK: - Dependencies
  let dependencies: SomeViewModelDependencies

  struct Dependencies {
    let dependencyA: DependencyA
    let dependencyB: DependencyB
    let dependencyC: dependencyC
    ...
    let dependencyX: DependencyX
  }
}

We would still need to initialize the dependencies struct before passing it to the view model, and it introduces one more annoyance in not having the dependencies accessible directly. Instead, the methods insides of view model would have to refer to dependencies.dependencyA to use it.

So, we are left with boilerplate issue. It’s now time for some code generation and Sourcery seemed to be a good tool to use.

I like to keep things isolated, so I will create a new folder at the root of my project named AutoGenerated. This folder will contain three sub-folders:
Generated/ where we will add files generated by Sourcery to our project
Templates/ where we will group our stencil templates
Protocols/ where we will define some types to be detected by Sourcery

Let’s then add the AutoDependencies.swift file in the Protocols/ folder with its content being as simple as:

import Foundation

protocol AutoDependencies {}

In the Templates/ folder, we will then add the AutoDependencies.stencil template, it’s content relatively easy to understand:

{% for type in types.implementing.AutoDependencies %}
// MARK: {{type.name}}

extension {{type.name}} {
{% for dependencies in type.containedTypes.Dependencies %}
{% for variable in dependencies.variables %}
    var {{variable.name}}: {{variable.typeName}} {
        return dependencies.{{variable.name}}
    }
{% endfor %}
{% endfor %}
}

extension DependencyContainer {
    func generate{{type.name}}Dependencies() -> {{type.name}}.Dependencies {
        return {{type.name}}.Dependencies({% for dependencies in type.containedTypes.Dependencies %}{% for variable in dependencies.variables %}{{variable.name}}: {{variable.name}}{% if not forloop.last %}, {% endif %}{% endfor %}{% endfor %})
    }
}

{% endfor %}

For each type implementing the empty AutoDependencies protocol, two things will be done:
– an extension on the type will be created to make and a simple get variable will be made so that you can use dependencyA directly
– an extension on the DependencyContainer will be created to easily create the dependencies struct

Now, let’s configure our project for it generate the code we need at build time. To do that, we simply need to add new Run Script in our target Build Phases (replace XXX with the proper path in your project):

sourcery --sources XXX/\
         --templates XXX/AutoGenerated/Templates/\
         --output XXX/AutoGenerated/Generated/

(note that at this stage, I’m using Sourcery installed on my machine with brew install sourcery)

We now need our class to implement the AutoDependencies protocol, it stays exactly the same as before otherwise:

class SomeViewModel: AutoDependencies {
  // MARK: - Initializers
  init(dependencies: SomeViewModel.Dependencies) {
    self.dependencies = dependencies
  }

  // MARK: - Dependencies
  let dependencies: SomeViewModelDependencies

  struct Dependencies {
    let dependencyA: DependencyA
    let dependencyB: DependencyB
    let dependencyC: dependencyC
    ...
    let dependencyX: DependencyX
  }
}

On save, a AutoDependencies.generated.swift is automatically created by Sourcery. This file looks like:

// MARK: SomeViewModel

extension SomeViewModel {
  var dependencyA: DependencyA {
    return dependencies.dependencyA
  }
  var dependencyB: DependencyB {
    return dependencies.dependencyB
  }
  var dependencyC: DependencyC {
    return dependencies.dependencyC
  }
  ...
  var dependencyX: DependencyX {
    return dependencies.dependencyX
  }
}

extension DependencyContainer {
  func generateSomeViewModelDependencies() -> SomeViewModel.Dependencies {
   return SomeViewModel.Dependencies(dependencyA: dependencyA, dependencyB: dependencyB, dependencyC: dependencyC, ..., dependencyX: dependencyX)
  }
}

We need to add it to the Xcode project by right-clicking on our Generated/ folder and “Add Files to …”.

In our coordinator, we can now simplify:

class SomeCoordinator {
  let dependencyContainer: DependencyContainer // For our own sake, let's at least imagine dependencies are centralized somewhere

  func showSomeViewController() {
    let dependencies = dependencyContainer.generateSomeViewModelDependencies()
    let viewModel = SomeViewModel(dependencies: dependencies)
    let viewController = SomeViewController(viewModel: viewModel)
    navigationController.push(viewController: viewController, animated: true)
  }
}

Want to see what’s in the DependencyContainer?

class DependencyContainer {
  lazy var dependencyA: DependencyA = DependencyA()
  lazy var dependencyB: DependencyB = DependencyB(dependencies: generateDependencyBDependencies()) // Assuming DependencyB is also an AutoDependencies
  lazy var dependencyC: DependencyC = DependencyC()
  ...
  lazy var dependencyX: DependencyX = DependencyX()
}

It’s a really dumb file lazy instantiating my dependencies as they need be. (Obviously, this kind of dependency container is too dumb to manage circular dependencies and is not a proper dependency graph, I let that to you to solve this problem!)

What about testing? The dependency container implementation being a bunch of lazy var, you can easily swap one with a mock:

class SomeViewControllerTests: XCTestCase {

    func testExample() {
        // Given
        let container = DependencyContainer()
        container.dependencyA = MockDependencyA()
        let dependencies = container.generateSomeViewModelDependencies()
        let viewModel = SomeViewModel(dependencies: dependencies)

        // When
        ...

        // Then
        ...
    }

class MockDependencyA: DependencyA {
    override func someMethod() -> Bool {
        return true // Let's say I want this method to always return true in my tests
    }
}

So in the end, what do we have? I will say:
– a functional type safe dependency injection enforced at build time that will probably work in most project
– some low(er) amount of boilerplate: you simply have to declare you type to comply to the AutoDependencies protocol, have a let dependencies: Dependencies and a Dependencies struct with all the desire dependencies
– Sourcery runs automatically when you build, so Xcode might show errors until you actually press Build
– adding a dependency to a type is done in only one place (the Dependencies struct), you simply Build again and you are good to use it in your code
– testable code

One caveat this system is build on-top is that no all dependencies need to be injected this way. For instance, a view controller is still responsible for passing dependencies to subview that will not own necessarily own it, for instance using initializer, property or method injection.

Don’t Be A Free User

An article I really liked to reference disappeared from it’s original website, so here is a copy of its content from the Web Archive.

“Someone builds a cool, free product, it gets popular, and that popularity attracts a buyer. The new owner shuts the product down and the founders issue a glowing press release about how excited they are about synergies going forward. They are never heard from again.

Whether or not this is done in good faith, in practice this kind of ‘exit event’ is a pump-and-dump scheme. The very popularity that attracts a buyer also makes the project financially unsustainable. The owners cash out, the acquirer gets some good engineers, that’s the principle of marketing, and is why is important to know how to target your products and is when services as SEO for IT companies can be helpful so the companies can learn how to market their apps and services.

To avoid this problem, avoid mom-and-pop projects that don’t take your money! You might call this the anti-free-software movement.

If every additional user is putting money in the developers’ pockets, then you’re less likely to see the site disappear overnight. If every new user is costing the developers money, and the site is really taking off, then get ready to read about those synergies.

To illustrate, I have prepared this handy chart:

Free Paid
Stagnant losing money making money
Growing losing more money making more money
Exploding losing lots of money making lots of money

What if a little site you love doesn’t have a business model? Yell at the developers! Explain that you are tired of good projects folding and are willing to pay cash American dollar to prevent that from happening. It doesn’t take prohibitive per-user revenue to put a project in the black. It just requires a number greater than zero.

I love free software and could not have built my site without it. But free web services are not like free software. If your free software project suddenly gets popular, you gain resources: testers, developers and people willing to pitch in. If your website that’s on a fully managed dedicated server takes off, you lose resources. Your time is spent firefighting and your money all goes to the nice people at Linode.

So stop getting caught off guard when your favorite project sells out! “They were getting so popular, why did they have to shut it down?” Because it’s hard to resist a big payday when you are rapidly heading into debt. And because it’s culturally acceptable to leave your user base high and dry if you get a good offer, citing self-inflicted financial hardship.

Like a service? Make them charge you or show you ads. If they won’t do it, clone them and do it yourself. Soon you’ll be the only game in town!”

— maciej from Pinboard on December 06, 2011

Detecting first launch, app upgrades and other useful launch informations

One of the first things every iOS developer should add to their project is a way to detect what is happening at launch. It doesn’t have to be complicated, but trust me, this can potentially save you some trouble later on.

I usually create a BaseAppDelegate class that will be responsible for detecting the following:

  • is it the first time the user launch the app?
  • how many times did the user launch count?
  • is this launch the first launch after an app upgrade?
  • when was last time the user launched the app before this launch?

Here is the code I usually put in this BaseAppDelegate class:


//
// BaseAppDelegate.swift
//
// Created by Cyril Chandelier on 21/04/2018.
// Copyright © 2018 Cyril Chandelier. All rights reserved.
//
import UIKit
class BaseAppDelegate: UIResponder, UIApplicationDelegate {
// MARK: – Dependencies
lazy var bundle: Bundle = Bundle.main
lazy var userDefaults: UserDefaults = UserDefaults.standard
// MARK: – UIApplicationDelegate methods
@discardableResult func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey : Any]? = nil) -> Bool {
print("Initializing app")
// Try to detect first launch
if launchCount == 0 {
print("…first launch detected")
applicationFirstLaunch()
}
// Update app launches count
launchCount += 1
print("…\(launchCount) launches")
// Try to detect version upgrade
let currentVersion = bundle.object(forInfoDictionaryKey: "CFBundleShortVersionString")
as! String
if let lastLaunchVersion = lastLaunchVersion, currentVersion != lastLaunchVersion {
print("…upgrade detected")
applicationDidUpgrade(from: lastLaunchVersion, to: currentVersion)
}
// Update last known version if new
if lastLaunchVersion != currentVersion {
print("…saving new version: \(currentVersion)")
lastLaunchVersion = currentVersion
}
// Update last known launch date
print("…saving current launch date")
appLastLaunchDate = Date()
// Make sure user defaults are saved
userDefaults.synchronize()
return true
}
// MARK: – Optional methods to override
func applicationFirstLaunch() {}
func applicationDidUpgrade(from: String, to: String) {}
// MARK: – Utilities
private(set) var launchCount: Int {
get {
return userDefaults.integer(forKey: Keys.appLaunchCount)
}
set {
userDefaults.set(newValue, forKey: Keys.appLaunchCount)
}
}
private(set) var lastLaunchVersion: String? {
get {
return userDefaults.string(forKey: Keys.appLastLaunchVersion)
}
set {
if let newValue = newValue {
userDefaults.set(newValue, forKey: Keys.appLastLaunchVersion)
} else {
userDefaults.removeObject(forKey: Keys.appLastLaunchVersion)
}
}
}
private(set) var appLastLaunchDate: Date? {
get {
return userDefaults.object(forKey: Keys.appLastLaunchDate) as? Date
}
set {
if let newValue = newValue {
userDefaults.set(newValue, forKey: Keys.appLastLaunchDate)
} else {
userDefaults.removeObject(forKey: Keys.appLastLaunchDate)
}
}
}
// MARK: – User default keys
private struct Keys {
static let appLaunchCount = "appLaunchCount"
static let appLastLaunchVersion = "appLastLaunchVersion"
static let appLastLaunchDate = "appLastLaunchDate"
}
}

You just need to make your AppDelegate extends BaseAppDelegate, and call the super method in application(_:didFinishLaunchingWithOptions). Then, you can choose (or not) to override (and therefore implement something for) the applicationFirstLaunch() and/or applicationDidUpgrade(from:to:) methods.

Here are a couple use cases where having this super-class reveals itself handy:

  • I want to know how many times a users came into the app for my analytics
  • I want to know when a users re-open the app after a while, to customize his experience as a returning user
  • I want to show a “What’s new” popup to my returning users who upgraded their app
  • my app was a paid app before, and now it’s free with In-App Purchases to unlock some content, but I don’t want my users who already paid to hit my paywall

Detecting organic vs. local notification vs. remote notification app launches

Since iOS 10 and with the UserNotifications framework, detecting wether the user launched the app organically or from a local/remote notification is a bit different than before.

Let’s say I’ve to send an event to my analytics service when user launches the app, I do this using one of these three calls:

  • Analytics.trackAppLaunched(source: .organic)
  • Analytics.trackAppLaunched(source: .localNotification)
  • Analytics.trackAppLaunched(source: .remoteNotification)

I have a NotificationController class that will be the UNUserNotificationCenter delegate. After experimenting a bit with it, I noticed that userNotificationCenter(:didReceive:withCompletionHandler:) is called after application(:didLaunchWithOptions:) but before applicationDidBecomeActive(_:).

In order to not send the event multiple times, we simply need to set a flag when we handle a notification during launch, I called it handledLaunchAnalytics.


func applicationDidBecomeActive(_ application: UIApplication) {
let notificationController = NotificationController.shared
// Track app launch if the notificationController didn't already do it
if !notificationController.handledLaunchAnalytics {
Analytics.trackAppLaunched(source: .Organic)
}
}


private(set) var handledLaunchAnalytics: Bool = false
func userNotificationCenter(_ center: UNUserNotificationCenter, didReceive response: UNNotificationResponse, withCompletionHandler completionHandler: @escaping () -> Void) {
let atAppLaunch: Bool = (UIApplication.shared.applicationState != .active)
let request: UNNotificationRequest = response.notification.request
if let trigger = request.trigger, trigger is UNPushNotificationTrigger {
// if atAppLaunch == true, call Analytics.trackAppLaunched(source: .remoteNotification)
// and flag handledLaunchAnalytics as true
// then you can handle your remote notification here
} else {
// if atAppLaunch == true, call Analytics.trackAppLaunched(source: .localNotification)
// and flag handledLaunchAnalytics as true
// then you can handle your local notification here
}
completionHandler()
}

Let’s just not forget to reset handledLaunchAnalytics when the app is going back to background.

If you know of a better way for doing this, I would be happy to hear about you!

 

Chasing UIViewController retain cycles

A retain cycle is a common memory issue preventing objects to be released. It’s happens when two (or more) objects have a strong reference one to each other. The main object can not be released because the other object has a strong reference to it, and this other object will only be released when the main object is released. Dead end, these two objects will never be released.

This can lead to random crashes due to lack of memory on your users’ phone or weird behaviours in your app due to existing but not visible objects that are interfering with the normal execution.

Do I have a retain cycle?

That’s easy to test, in your class, add a print statement in the deinit method, also add a breakpoint. Assuming that your view controller is part of a navigation controller, run your app and go to the screen then hit back, you should hit the breakpoint and “MyViewController deinit called” should appear in your console. If this is not happening, then you view controller is part of a retain cycle and will never be released.

deinit {
    print("MyViewController deinit called")
}

Find and fix the retain cycles

We now know that a view controller generates retain cycles, let’s try to fix them. There are several reasons why a retain cycle exists.

I passed my view controller to someone else and he is retaining me

Typical example is with delegate. Let’s say we have this class that needs a delegate

MyCustomObject.swift

protocol MyCustomObjectDelegate {
   // some methods to be implemented
}

class MyCustomObject {
    var delegate: MyCustomObjectDelegate?
}

MyViewController.swift

let myCustomObject = MyCustomObject()
myCustomObject.delegate = self

To fix the retain cycle, we need to change this a little bit:

MyCustomObject.swift

protocol MyCustomObjectDelegate: class {
   // some methods to be implemented
}

class MyCustomObject {
    weak var delegate: MyCustomObjectDelegate?
}

Note that MyCustomObjectDelegate is now a class protocol and that the reference to this object is now weak.

Another case, slightly more subtle is possible, in this case, your delegate is not optional because you always need one and it always has to provide you with some values

MyCustomObject.swift

protocol MyCustomObjectDelegate {
   // some methods to be implemented
}

class MyCustomObject {
    private let delegate: MyCustomObjectDelegate

    init(delegate: MyCustomObjectDelegate) {
        self.delegate = delegate
    }
}

MyViewController.swift

let myCustomObject = MyCustomObject(delegate: self)

In that case, we will need to use the unowned keyword instead of the weak keyword:

MyCustomObject.swift
protocol MyCustomObjectDelegate: class {
   // some methods to be implemented
}

class MyCustomObject {
    private unowned let delegate: MyCustomObjectDelegate

    // init remains the same
}

A block / closure is retained

It’s also easy to generate a retain cycle when using closures. In the following example, the addInfiniteScrollWithHandler methods copies my block to eventually use it later. A copy automatically performs a retain on it, and if the block make use of self, then it’s incrementing my view controller retainCount.

tableView.addInfiniteScrollWithHandler { tableView in
    self.nextPageForScrollView(tableView)
}

The following one is similar, except that a method has been passed as the closure, this is nasty because the self is here implicit:

func infiniteScrollHandler(tableView: UITableView) {}
tableView.addInfiniteScrollWithHandler(infiniteScrollHandler)

To fix it, we need to use a safe version of self, using the [weak self] parameters in the closure. From now, all self. occurrences have to be replaced with self?. or self!.:

tableView.addInfiniteScrollWithHandler { [weak self] tableView in
    self?.nextPageForScrollView(tableView)
}

This can happen, but remember that most of the closures you are writing will probably not be retained! It’s not always necessary and therefore considered as overkill to write [weak self] and handle the optional self in all every closures. In the following example, the block will be executed directly and will not be retained, this will not generate a retain cycle.

UIView.animateWithDuration(0.25, animations: {
    self.view.alpha = 1.0
})