TestsTested | ✗ |
LangLanguage | Obj-CObjective C |
License | MIT |
ReleasedLast Release | Jun 2015 |
Maintained by Ian Keen.
A simple little DSL for chaing UIViewControllers
together to obtain a final/single value
Whenever possible I always try and create my UIViewControllers
to be just as modular as any other non-visual component. They should be able to be inserted into a 'flow' of UIViewControllers
to get back some result at the end. In other words my UIViewControllers usually are dumb, they might take some input and have some output but they don't know about anything outside their function.
Imagine we are creating a small contacts app, the 'flow' of that app might be something like:
Using IKAsyncViewController
s that logic can be abstract and written as
-(void)callContact:(UINavigationController *)navController animate:(BOOL)animate {
navController
.push(^{
return [ContactListViewController new];
}, animate)
.then(^(User *user){
return [PhoneNumberViewController newWithUser:user];
}, animate)
.finally(^(NSString *phoneNumber) {
NSString *phoneNumberString = [NSString stringWithFormat:@"tel://%@", phoneNumber];
NSURL *url = [NSURL URLWithString:phoneNumberString];
[[UIApplication sharedApplication] openURL:url];
});
return YES;
}
Setting up a UIViewController to support this flow is simple
.h
#import <IKAsyncViewControllers/IKAsyncViewController.h>
@interface ViewController : UIViewController <IKAsyncViewController>
@end
.m
#import "IKAsyncViewControllerOutput.h"
@interface ViewController ()
@property (nonatomic, strong) IKAsyncViewControllerOutput *output;
@end
@implementation ViewController
-(void)useOutput:(IKAsyncViewControllerOutput *)output {
self.output = output;
}
-(IBAction)actionTriggered:(id)sender {
[self.output output:<output_value>];
}
@end
Starting a chain of IKAsyncViewController
s is easy using the UINavigationController
category methods. Given a method to create the first view controller you want to show
-(UIViewController<IKAsyncViewController> *)firstViewController { ... }
You can make it the root view controller using the root
block
-(void)startChain:(UINavigationController *)navController animate:(BOOL)animate {
navController.root(^{ return [self firstViewController]; }, animate);
}
Or you can just push it onto an existing stack with the push
block
-(void)startChain:(UINavigationController *)navController animate:(BOOL)animate {
navController.push(^{ return [self firstViewController]; }, animate);
}
Parsing a value from one IKAsyncViewController
to the next is also simple.
Given methods to create an IKAsyncViewController
that take some input and produce some output
-(UIViewController<IKAsyncViewController> *)anotherViewController:(id)input { ... }
-(UIViewController<IKAsyncViewController> *)andAnotherViewController:(id)input { ... }
You using the then
block
-(void)startChain:(UINavigationController *)navController animate:(BOOL)animate {
navController.root(^{ return [self firstViewController]; }, animate)
.then(^(id output) { return [self anotherViewController:output]; }, animate)
.then(^(id output) { return [self andAnotherViewController:output]; }, animate);
}
Or using the thenIf
block to conditionally show an IKAsyncViewController
. If the predicate is NO
the IKAsyncViewController
will be skipped in the chain
-(void)startChain:(UINavigationController *)navController {
navController.root(^{ return [self firstViewController]; })
.thenIf([self shouldShowAnother], ^(id output) { return [self anotherViewController:output]; }, animate)
.then(^(id output) { return [self andAnotherViewController:output]; }, animate);
}
-(BOOL)shouldShowAnother { ... }
Eventually the idea is to get some final value from a chain of IKAsyncViewController
s this is achieved with the finally
block
-(void)startChain:(UINavigationController *)navController {
navController.root(^{ return [self firstViewController]; })
.thenIf([self shouldShowAnother], ^(id output) { return [self anotherViewController:output]; }, animate)
.then(^(id output) { return [self andAnotherViewController:output]; }, animate)
.finally(^(id finalValue) {
//.. do something with finalValue
});
}
-(BOOL)shouldShowAnother { ... }
IKAsyncViewControllers
is a nice, simple DSL that can help you think about making your UIViewControllers more modular. It is a great tool you can use when you have a clearly defined set of UIViewControllers that you can chain together to get some output.
IKAsyncViewControllers
is not a replacement for storyboards and while it can be used for complex/multi branch navigation that doesn't necessarily make it the right tool for the job. Have a good think about wether IKAsyncViewControllers
is right for your app.
or manually by adding the source files from the IKEvents
subfolder to your project
Pull Requests are welcome!
If you use this in a project I would love to hear about it!..
I'm usually hanging out on iOS Developers. You should check them out!