iBeacons And Hue Lights Tutorial Part 4: Typhoon Dependency Injection Framework
This is post 4 of 4 in the series “iBeacons and Hue Lights Tutorial Series”
- iBeacons and Hue Lights Tutorial Part 1: Introducing The Beacon Solution
- iBeacons and Hue Lights Tutorial Part 2: Implementing a Node.JS Server
- iBeacons And Hue Lights Tutorial Part 3: Integrating Node.JS with iOS
- iBeacons And Hue Lights Tutorial Part 4: Typhoon Dependency Injection Framework
A small band of Gorillas created a three-week iBeacons and Hue Lights project using different technologies. In our first post, we introduced our project and provided a tutorial on implementing the Beacon SDK and integrating iOS devices through Bluetooth. In the second post, we discussed how to implement a Node.JS Server to manage and handle a Philips Hue Bridge with Hue Lights. In the third post, we demonstrate Hue Lights integration for Node.JS and our iOS App. In this post, we are going to discuss the final step in the iBeacons-Hue Lights Project: the implementation of the Typhoon Dependency Injection Framework.
Typhoon Dependency Injection Framework
Usually, in this type of project, our ResAPIClient, the HueLightsManager and the BeaconManager should be a Singleton instance, because that makes it easier to manipulate and call the functions. But for this project, we want to take another approach.
Typhoon is an easy way to create a dependency injection for the code. The framework manages all injections, making them really easy to implement.
Also one—and perhaps the biggest—benefit of Typhoon is that it allows for SDKs or libraries to be changed without having to alter the code in a complex way.
For our project, we create an Assembly class to the instance through injection of all classes.
1. Add the Library to the cocoa pod file: pod ‘Typhoon’ and install the pods again:
- In a terminal, go to the directory of the project.
- Run the command: pod install.
- When this is finished, reopen the Xcode’s workspace file of the project.
2. Create a new Swift file called: iBeaconHueTyphoonAssembly.swift
- Set a class inside the file: public class iBeaconHueTyphoonAssembly : TyphoonAssembly, leave the class empty for now.
3. We need to register the Assembly class in the info.plist file.
- Go to the file and create a new row (array type) and set the name: TyphoonInitialAssemblies
- Add a row to the array, with the name of our Assembly file: item 0, type String: value: iBeaconHueTyphoonAssembly
4. Delete all the initializations of the RestAPIClient and the HueLightsManager, like in the HueLightsSetupViewController class.
let restApi = RestAPIClient(serverBaseUrl: Constants.serverBaseUrl, serverPort: Constants.serverPort) self.hueLightsManager = HueLightsManager(api: restApi)
5. Add a “User Defined Runtime Attribute” to the ViewController in the main.storyborard file.
- Open the storyboard file
- Find the HueLightsSetupViewController
- Go to the Identity Inspector and add a new User Defined Runtime Attribute
- KeyPath: typhoonKey, type String, value: hueLigthsSetupViewController
6. Implement the injections in the Assembly.
public class iBeaconHueTyphoonAssembly : TyphoonAssembly { public dynamic func hueLigthsSetupViewController() -> AnyObject { return TyphoonDefinition.withClass(HueLightsSetupViewController.self) { (definition) in definition?.injectProperty(Selector(("hueLightsManager")), with: self.lightManager()) } as AnyObject } public dynamic func lightManager () -> AnyObject { //Perform an initializer injection return TyphoonDefinition.withClass(HueLightsManager.self) { (definition) in definition!.useInitializer(#selector(HueLightsManager.init(api:))) { (initializer) in initializer!.injectParameter(with: self.apiClient()) } } as AnyObject }
public dynamic func hueLigthsSetupViewController() is called. When the HueLightsSetupViewController is created, our User Defined Runtime Attribute calls the function. This function injects a LightsManager instance inside the hueLightsManager Property before the class is presented to the user.
public dynamic func lightManager () creates an instance of a LightManager, injecting an APIClient with the initializer into the init of the instance.
And finally for our example the public dynamic func apiClient() -> AnyObject function creates an instance of RestAPIClient, injecting the parameters into the initializer.
In this way, we are injecting the parameters and the properties initialized in this assembly and not in the ViewController.
Conclusions
Typhoon helps us to easily manage the dependencies. Usually, developers overuse Singleton instances. In reality, this is bad practice since this manages the memory and other app resources in an irresponsible way. Dependency injection is a really simple design pattern, but it is easy to lose dependency references – something that is easier to avoid with Typhoon. Applying unit testing is also made easier. However, the most important conclusion is that Typhoon makes it incredibly easy to update and change SDKs or services.
Final Application Demo
Here is a simple application flow demo recorded with QuickTime:
- When the user opens the app a list of available locations (beacons) is displayed.
- As the user gets closer to the location the Philips Hue light bulb, the light intensity changes from dim to bright.
- After the user reaches the location, the light “breathes,” showing the user that he or she has reached the destination.
- The user is able to change the light’s color.
- An error screen + Hue Light Server Setup Screen was added to complete the project.