Embedding a debug view in your app is a great tool to save a lot of time, but it can also be a tedious one to build.
ControllerFactory is a framework that aims at easily allowing the instantiation of any UIViewController
from your app, in a way that is as fast and painless as possible.
The framework will leverage the runtime environment to retrieve the complete list of all the view controllers in your app, and let you choose which one you wish to see displayed on screen. Additionally, it also embeds customization options, for instance to provide initial data.
- iOS 8.0+
- Xcode 9.0+
- Swift 4.1+
Add the following to your Podfile
:
pod "ControllerFactory"
Add the following to your Cartfile
:
github "worldline/ControllerFactory"
Let's say you want to adapt your app to make the best use of the gorgeous iPhone X display.
Step 1 - You adapt and test your app's homepage. Check.
Step 2 - Then you adapt and test your app's settings view controller. Easy.
But then, you adapt and need to test, for the eleventh time, the third controller of the enrolment process. You need to go through the whole enrolment process, again and again. Maybe you cheat a little, and, why not, create an easy access button on your homepage, which loads this controller directly. This is fine...
But, are you willing to do that for the 35 controllers left?
No, you are not. This is where ControllerFactory saves the day.
ControllerFactory
provides you with a debug view that will automatically retrieve and list all your view controllers:
Then you just need to select the view controller you want to instantiate, and choose wether you want it pushed or presented modally.
All your view controllers can now be displayed on screen, with just two taps 👌
For the moment, ControllerFactory
cannot:
- deal with controllers that are using Swift's Generics, as they cannot be marked
@objc
; - deal with controllers that are instantiated from a UIStoryboard, without writing some code (see below);
- fetch controllers from multiple bundles at the same time.
- Import
- Displaying the debug view
- Excluding specific view controllers
- Setting up initial data
- Tackling controllers with several use cases
- Custom initializer
- Custom initializer with use cases
// Objective C
@import ControllerFactory;
// Swift
import ControllerFactory
To instantiate the debug view, you can use any of the following methods:
// Objective C
[ControllerFactory instantiate];
[ControllerFactory instantiateWithBundle:(NSBundle * _Nonnull)];
[ControllerFactory instantiateWithExcludedViewControllers:(NSArray<NSString *> * _Nonnull)];
[ControllerFactory instantiateWithBundle:(NSBundle * _Nonnull) excludedViewControllers:(NSArray<NSString *> * _Nonnull)];
// Swift
ControllerFactory.instantiate()
ControllerFactory.instantiate(bundle: Bundle)
ControllerFactory.instantiate(excludedViewControllers: [String])
ControllerFactory.instantiate(bundle: Bundle, excludedViewControllers: [String])
The basic method will show you all the view controllers from your main bundle.
You also have the possibility to specify a different bundle if you need to, and/or exclude some specific view controllers (for instance, abstract classes).
In order to exclude some specific view controllers, first start by printing out to the console the list of all the controllers that were retrieved:
// Objective C
[ControllerFactory printControllers];
[ControllerFactory printControllersWithBundle:(NSBundle * _Nonnull)];
// Swift
ControllerFactory.printControllers()
ControllerFactory.printControllers(bundle: Bundle)
Just as before, the first method will print all the view controllers from your main bundle, while the second one allows you to print all the view controllers from another bundle.
You then need to use the values that were printed in order to populate the excludedViewControllers
array.
Up to this point, we used the debug view to instantiate controllers using their default initializer. While this works well for simple controllers, it does not allow you to provide some initial data.
To do so, you can implement the following protocol:
// Swift
@objc public protocol ControllerFactoryCompliant {
func prepareForControllerFactory()
}
The prepareForControllerFactory()
method will be called after the controller has been instantiated and before the it is displayed, so this is a perfect place to set up some initial data.
If your controller implements several use cases, or works with several sets of data, you can hand the them through the protocol ControllerFactoryCompliant
:
// Swift
@objc public protocol ControllerFactoryUseCaseCompliant {
static func getUseCases() -> [String]
func prepareForControllerFactory(useCase: String)
}
The getUseCases()
method allows you to list your use cases.
The prepareForControllerFactory(useCase: String)
method will be called after initialization, so this is where you should set your data or by switch to a particular use case.
The debug view will then present the different use cases, allowing you to select the one you need.
If your view controller doesn't make use of the default initializer, trying to instantiate it will cause your app to crash. To resolve the issue, you will need to implement the following protocol:
// Swift
@objc public protocol ControllerFactoryInstantiable {
static func initForControllerFactory() -> UIViewController
}
In the initForControllerFactory()
you can use your custom initializer, set all the required data, and then return the controller.
Finally, if your view controller doesn't make use of the default initializer and depends on different use cases, or different sets of data, you will need to implement the following protocol:
// Swift
@objc public protocol ControllerFactoryUseCaseInstantiable {
static func getUseCases() -> [String]
static func initForControllerFactory(useCase: String) -> UIViewController
}
Just as before, the getUseCases()
method will allow you to name your different use cases.
In the initForControllerFactory(useCase: String)
you use your custom initializer to create the controller.
The debug view will then present the different use cases for you to select, before instantiating your view controller.
This framework has been put together at Worldline by Benoît Caron and Vincent Pradeilles.
This framework is provided under the MIT license.