More Related Content Similar to ITT 2014 - Peter Steinberger - Architecting Modular Codebases (20) More from Istanbul Tech Talks (8) ITT 2014 - Peter Steinberger - Architecting Modular Codebases14. Find large files
find.-name"*.m"-execwc-l"{}";|sort-rn|head
4351 ./TextParser/PSPDFFontFileDescriptor.m
3651 ./Controller/PSPDFViewController.m
2889 ./Views/PSPDFPageView.m
2189 ./Parser/PSPDFDocumentParser.m
2017 ./XFDF/PSPDFXFDFParser.m
2004 ./Model/PSPDFDocument.m
1783 ./TextParser/PSPDFTextParser.m
1776 ./Annotations/PSPDFAnnotation.m
1649 ./Views/PSPDFPageView+AnnotationMenu.m
1469 ./Gallery/PSPDFGalleryViewController.m
16. Application Components
Use private headers within components.
PSPDFEmbeddedFile.h/m
PSPDFEmbeddedFile+Private.h
@interface PSPDFEmbeddedFile (Private)
// Parses the PDF dictionary and returns an `PSPDFEmbeddedFile` object if found.
+ (instancetype)embeddedFileWithPDFDictionary:(CGPDFDictionaryRef)pdfDictionary;
// Index of the file in the original document.
@property (nonatomic, copy) NSString *streamPath;
@end
17. Application Components
Use class extensions.
@interface UIView (UIViewHierarchy)
- (void)layoutSubviews;
@end
@interface UIView (UIViewRendering)
- (void)drawRect:(CGRect)rect;
@end
@interface UIView (UIViewGestureRecognizers)
- (void)addGestureRecognizer:(UIGestureRecognizer*)recognizer
@end
@interface UIView (UIViewMotionEffects)
- (void)addMotionEffect:(UIMotionEffect *)effect
@end
18. Application Components
Use class extensions.
@interface PSPDFViewController (EmbeddedFileSupport)
<PSPDFEmbeddedFilesViewControllerDelegate>
@end
@implementation PSPDFViewController (EmbeddedFileSupport)
- (void)embeddedFilesController:(PSPDFEmbeddedFilesViewController *)controller
didSelectFile:(PSPDFEmbeddedFile *)embeddedFile sender:(id)sender {
// delegate implementation.
}
19. Application Components
Extract features into sub-controllers.
* PSPDFSearchHighlightViewManager
* PSPDFPresentationController
* PSPDFHalfModalController
objc.io/issue-1/lighter-view-
controllers.html
23. DependencyInjection
- (PSPDFCache *)cache {
return PSPDFCache.sharedCache;
}
- (void)viewWillDisappear:(BOOL)animated {
[super viewWillDisappear:animated];
// Stop further caching the document.
[self.cache stopCachingDocument:self.document];
}
25. DependencyInjection
// In class extension
@property (nonatomic, strong) PSPDFCache *cache;
- (id)initWithCache:(PSPDFCache *)cache {
NSParameterAssert(cache);
if (self = [super init]) {
_cache = cache;
}
return self;
}
32. ViewController Decouplingwith
Routes
// Set up routes
[JLRoutes addRoute:@"/user/:userID" handler:^BOOL(NSDictionary *parameters) {
NSUInteger userID = [parameters[@"userID"] integerValue];
UserDetailController *controller = [[UserDetailController alloc] initWithID:userID];
[self.navigationController pushViewController:controller animated:YES];
return YES;
}];
33. ViewController Decouplingwith
Routes
// Open the user controller.
NSUInteger userID = 42;
NSString *URLString = [NSString stringWithFormat:@"myapp://user/%tu?animated=NO", userID];
[UIApplication.sharedApplication openURL:[NSURL URLWithString:URLString]];
// Install the URL handler.
- (BOOL)application:(UIApplication *)app openURL:(NSURL *)URL
sourceApplication:(NSString *)source annotation:(id)annotation {
return [JLRoutes routeURL:URL];
}
34. ViewController Decouplingwith
Routes
// Set up route and also process the animation property.
[JLRoutes addRoute:@"/user/:userID" handler:^BOOL(NSDictionary *parameters) {
NSUInteger userID = [parameters[@"userID"] integerValue];
BOOL animated = parameters[@"animated"] ? [parameters[@"animated"] boolValue] : YES;
UserDetailController *controller = [[UserDetailController alloc] initWithID:userID];
[self.navigationController pushViewController:controller animated:animated];
return YES;
}];
40. tl;dr
» Hacky "static framework" workarounds
using lipo
» Resource Loading
» Debugging and Symbolication
» Dependent frameworks
» Two-level Namespacing
43. Plugin Infrastructure
TheXcodeway
+ (void)pluginDidLoad:(NSBundle *)plugin {
// Initialize class
}
// BBUDebuggerTuckAway
- (void)swizzleDidChangeTextInSourceTextView {
id sourceTextView = [objc_getClass("DVTSourceTextView") new];
[sourceTextView yl_swizzleSelector:@selector(didChangeText)
withBlock:^void(id _self) {
[self toggleDebuggersIfNeeded];
// call original
[_self yl_performSelector:@selector(didChangeText)];
}];
}
51. Plugin Infrastructure
SDK == Driver
@protocol PSPDFStylusDriver <PSPDFPlugin>
- (BOOL)enableDriver:(NSError **)error options:(NSDictionary *)options;
- (void)disableDriver;
- (NSDictionary *)connectionInfo;
@property (nonatomic, weak, readonly) id<PSPDFStylusDriverDelegate> delegate;
@optional
- (id<PSPDFStylusTouch>)touchInfoForTouch:(UITouch *)touch;
- (UIViewController *)settingsController;
@end
52. Plugin Infrastructure
@protocol PSPDFPlugin <NSObject>
// Designated initializer. Will be called upon creation.
- (id)initWithRegistry:(PSPDFPluginRegistry *)registry options:(NSDictionary *)options;
// Plugin details for auto-discovery.
+ (NSDictionary *)pluginInfo;
@end
extern NSString * const PSPDFPluginNameKey;
extern NSString * const PSPDFPluginEnabledKey;
extern NSString * const PSPDFPluginInitializeOnDiscoveryKey;
extern NSString * const PSPDFPluginSaveInstanceKey;
extern NSString * const PSPDFPluginProtocolVersionKey;
54. Plugin Infrastructure
Discovery
- (NSDictionary *)discoverPluginClasses {
NSMutableDictionary *pluginClasses = [NSMutableDictionary dictionary];
// Interate all classes.
unsigned int count = 0;
Class *classList = objc_copyClassList(&count);
for (int index = 0; index < count; index++) {
Class class = classList[index];
if (class_conformsToProtocol(class, @protocol(PSPDFPlugin)) &&
[self isValidPluginClass:class]) {
NSDictionary *info = [class pluginInfo];
pluginClasses[info[PSPDFPluginNameKey]] = class;
}
}
free(classList);
return [pluginClasses copy];
}
55. Plugin Infrastructure
Discovery
// Static method declared in <PSPDFPlugin>
+ (NSDictionary *)pluginInfo {
return @{PSPDFPluginNameKey : @"com.pspdfkit.stylus.driver.wacom",
PSPDFPluginProtocolVersionKey : @(PSPDFPluginProtocolVersion_1),
PSPDFStylusDriverNameKey : @"Wacom",
PSPDFStylusDriverSDKVersionKey : WacomManager.getManager.getSDKVersion,
PSPDFStylusDriverVersionKey : @(PSPDFStylusDriverProtocolVersion_1),
PSPDFStylusDriverPriorityKey : @(80)};
}
57. Plugin Infrastructure
Implementation
// Lazily initialize our stylus support class.
- (PSPDFAnnotationStateManagerStylusSupport *)stylusSupport {
PSPDFAnnotationStateManagerStylusSupport *stylusSupport = objc_getAssociatedObject(self, _cmd);
if (!stylusSupport) {
stylusSupport = [[PSPDFAnnotationStateManagerStylusSupport alloc] initWithStateManager:self];
objc_setAssociatedObject(self, _cmd, stylusSupport, OBJC_ASSOCIATION_RETAIN);
}
return stylusSupport;
}
58. Plugin Infrastructure
Implementation
// Hook into didMoveToWindow for view registration.
[PSPDFDrawView.class pspdf_hookSelector:@selector(didMoveToWindow)
withBlock:^(PSPDFDrawView *view, NSArray *args) {
if (view.window) {
[PSPDFStylusManager.sharedInstance registerView:view];
}else {
[PSPDFStylusManager.sharedInstance unregisterView:view];
}
}];
59. Plugin Infrastructure
Implementation
@interface NSObject (PSPDFMessageHooks)
- (BOOL)pspdf_hookSelector:(SEL)selector
withBlock:(void (^)(id object, NSArray *arguments))block;
+ (BOOL)pspdf_hookSelector:(SEL)selector
withBlock:(void (^)(id object, NSArray *arguments))block;
@end
65. AspectOriented
Programming
- (NSDictionary *)analyticsConfiguration {
return @{
@"trackedScreens" : @[
@{@"class" : @"ISTMainViewController",
@"label" : @"Main screen"}
],
@"trackedEvents" : @[
@{@"class" : @"ISTMainViewController",
@"sel" : @"loginWithFacebook:",
@"label" : @"Login with Facebook"},
@{@"class" : @"ISTFacebookViewController",
@"sel" : @"failedToConnect:",
@"label" : @"Facebook connection failure"},
]
};
}
69. ArchitectingModularCodebases
Thanks!
Resources:
* http://www.typhoonframework.org
* https://github.com/atomicobject/objection
* http://objc.io/issue-1/lighter-view-controllers.html
* https://github.com/joeldev/JLRoutes
* http://ddeville.me/2014/04/dynamic-linking/
* http://dev.hubspot.com/blog/architecting-a-large-ios-app-with-cocoapods
* http://landonf.bikemonkey.org/code/ios/Radar_15800975_iOS_Frameworks.20140112.html
* https://github.com/neonichu/BBUDebuggerTuckAway
* http://albertodebortoli.github.io/blog/2014/03/25/
an-aspect-oriented-approach-programming-to-ios-analytics/
* http://codeshaker.blogspot.co.at/2012/01/aop-delivered.html
* https://github.com/ReactiveCocoa/ReactiveCocoa/blob/master/
ReactiveCocoaFramework/ReactiveCocoa/NSObject+RACSelectorSignal.m
* https://github.com/ndcube/AOP-for-Objective-C
* http://codeshaker.blogspot.com.tr/2012/01/aop-delivered.html
Image Credits:
* Hollywood, Gavin Johnson, flickr.com/photos/23629211@N04/4569636150