Upgrade to Pro
— share decks privately, control downloads, hide ads and more …
Speaker Deck
Features
Speaker Deck
PRO
Sign in
Sign up for free
Search
Search
Depenedency Injection in iOS
Search
Maciej Oczko
December 17, 2013
Programming
0
69
Depenedency Injection in iOS
Talk given in Łódź and Lublin, Poland, during the Mobile Warsaw on Tour meetups.
Maciej Oczko
December 17, 2013
Tweet
Share
More Decks by Maciej Oczko
See All by Maciej Oczko
Dependency Injection on iOS
maciejoczko
1
120
Working with Legacy Code (Łódź)
maciejoczko
0
53
iOS TDD Workshop (Gdańsk)
maciejoczko
1
81
UICollectionView Basics and Flow Layout
maciejoczko
0
240
UICollectionView Introduction
maciejoczko
0
62
Working With Legacy Code - iOS TDD Workshop
maciejoczko
0
170
Other Decks in Programming
See All in Programming
Azure SRE Agentで運用は楽になるのか?
kkamegawa
0
2.5k
Ruby Parser progress report 2025
yui_knk
1
460
MCPとデザインシステムに立脚したデザインと実装の融合
yukukotani
4
1.5k
CJK and Unicode From a PHP Committer
youkidearitai
PRO
0
110
私の後悔をAWS DMSで解決した話
hiramax
4
210
はじめてのMaterial3 Expressive
ym223
2
890
Namespace and Its Future
tagomoris
6
710
プロポーザル駆動学習 / Proposal-Driven Learning
mackey0225
2
1.3k
The Past, Present, and Future of Enterprise Java with ASF in the Middle
ivargrimstad
0
160
パッケージ設計の黒魔術/Kyoto.go#63
lufia
3
440
🔨 小さなビルドシステムを作る
momeemt
4
690
デザイナーが Androidエンジニアに 挑戦してみた
874wokiite
0
550
Featured
See All Featured
Navigating Team Friction
lara
189
15k
Put a Button on it: Removing Barriers to Going Fast.
kastner
60
4k
4 Signs Your Business is Dying
shpigford
184
22k
The Art of Delivering Value - GDevCon NA Keynote
reverentgeek
15
1.7k
GitHub's CSS Performance
jonrohan
1032
460k
We Have a Design System, Now What?
morganepeng
53
7.8k
Visualizing Your Data: Incorporating Mongo into Loggly Infrastructure
mongodb
48
9.7k
The Language of Interfaces
destraynor
161
25k
Imperfection Machines: The Place of Print at Facebook
scottboms
268
13k
A Tale of Four Properties
chriscoyier
160
23k
Statistics for Hackers
jakevdp
799
220k
Helping Users Find Their Own Way: Creating Modern Search Experiences
danielanewman
29
2.9k
Transcript
What about DI on iOS?
Who am I? Software Engineer at Polidea Maciej Oczko @maciejoczko
What about DI on iOS?
What about DI on iOS?
Aims • To talk about what DI is • To
encourage you to proceed with DI pattern • To make your coding easier with Objection or Typhoon
What is it all about? You always deal with dependency
management somehow…
What is it all about? You always deal with dependency
management somehow… …but not necessarily in a good way.
Example 1 - (id)init { self = [super init]; if
(self) { _weatherClient = [[MYWeatherClient alloc] initWithY:y andX:x]; } return self; }
Example 1 - (id)init { self = [super init]; if
(self) { _weatherClient = [[MYWeatherClient alloc] initWithY:y andX:x]; } return self; }
Example 2 - (id)init { self = [super init]; if
(self) { _weatherClient = [MYWeatherClient sharedInstance]; } return self; }
Example 3 - (id)initWithWeatherClient:(id <MYWeatherClientProtocol>)client { self = [super init];
if (self) { _weatherClient = client; } return self; }
Example 3 - (id)initWithWeatherClient:(id <MYWeatherClientProtocol>)client { self = [super init];
if (self) { _weatherClient = client; } return self; } DI
Why DI? Pros • Classes are easier to test (or
even possible) • It promotes separation of concerns (single responsibility principle) • Open-closed principle • Makes app maintenance easier • Makes introducing new features less painful
Example 1 << 2 describe(@"Super spec", ^{ it(@"should always pass",
^{ ! ! ! ! ! ! ! ! ! ! ! ! ! ! }); });
Example 1 << 2 describe(@"Super spec", ^{ it(@"should always pass",
^{ ! ! ! ! ! ! ! ! ! ! ! ! ! ! }); }); id <MYAPIAccessorProtocol> accessor = [KWMock mockForProtocol:@protocol(MYAPIAccessorProtocol)]; ! id <MYCacheManagerProtocol> cache = [MYCacheManager new];
Example 1 << 2 describe(@"Super spec", ^{ it(@"should always pass",
^{ ! ! ! ! ! ! ! ! ! ! ! ! ! ! }); }); id <MYAPIAccessorProtocol> accessor = [KWMock mockForProtocol:@protocol(MYAPIAccessorProtocol)]; ! id <MYCacheManagerProtocol> cache = [MYCacheManager new]; . . . ! MYWeatherClient *client = [[MYWeatherClient alloc] initWithX:accessor andY:cache];
Example 1 << 2 describe(@"Super spec", ^{ it(@"should always pass",
^{ ! ! ! ! ! ! ! ! ! ! ! ! ! ! }); }); id <MYAPIAccessorProtocol> accessor = [KWMock mockForProtocol:@protocol(MYAPIAccessorProtocol)]; ! id <MYCacheManagerProtocol> cache = [MYCacheManager new]; . . . ! MYWeatherClient *client = [[MYWeatherClient alloc] initWithX:accessor andY:cache]; [client getWeather]; [[[client.weatherItems should] have:4] elements];
Cons • Life cycle management • Objects’ creation get more
complex • Sometimes more code
Problems? ImageManager CoreDataStorage APIAccessor Cache UserManager ImageValidator FileManager TaskRunner
Problems? - (id)initWithImageValidator:(id <MYImageValidatorProtocol>)imageValidator cacheManager:(id <MYCacheProtocol>)cacheManager userManager:(id <MYUserManagerProtocol>)userManager apiAccessor:(id <MYAPIProtocol>)apiAccessor
coreDataManager:(id <MYCoreDataProtocol>)coreDataManager { self = [super init]; if (self) { ... } return self; } id <MYFileManagerProtocol> fileManager = [[MYFileManager alloc] init]; id <MYImageValidatorProtocol> imageValidator = [[MYImageValidator alloc] initWithFileManager:fileManager]; ! id <MYCacheProtocol> cacheManager = [[MYCacheManager alloc] initWithFileManager:fileManager]; id <MYCoreDataProtocol> coreDataManager = [[MYCoreDataManager alloc] init]; ! id <MYAPIProtocol> apiAccessor = [[MYAPIAccessor alloc] initWith:...]; ... MYImageManager *imageManager = [[MYImageManager alloc] initWithImageValidator:imageValidator cacheManager:cacheManager userManager:userManager apiAccessor:apiAccessor coreDataManager:coreDataManager];
None
Frameworks
Objection
• ‘Annotation’ based DI • Initializer support • Proporties auto-wiring
• Lazily instantiated dependencies • Custom providers • Class bindings • Protocol bindings • Instance bindings • Life cycle management • Cyclic dependencies
How it works, quickly Injector id obj = [injector getObject:@protocol(FancyProtocol)];
Module [self bindProtocol:@protocol(FancyProtocol) toClass:[MYFancy class]];
Initializer @implementation MYObject ! objection_initializer_sel(@selector(initWithClient:)) ! - (id)initWithClient:(id <MYClientProtocol>)client {
self = [super init]; if (self) { _client = client; } return self; } ! @end ! ! ! ! !
Initializer @implementation ! objection_initializer_sel ! - ( _client } !
@end ! ! ! ! ! id obj = [injector getObject:[MYObject class] argumentList:@[ client ]];
Properties @interface MYObject @property(nonatomic, readonly) id <MYClientProtocol> client; @property(nonatomic, readonly)
id <MYAPIProtocol> apiAccessor; @end ! @implementation MYObject ! objection_requires_sel(@selector(client), @selector(apiAccessor)) ! - (id)init { self = [super init]; if (self) { return self; } ! - (void)awakeFromObjection { } ! @end ! ! !
Properties @interface @property @property @end ! @implementation ! objection_requires_sel !
- (id } ! - (void ! @end ! ! ! id obj = [injector getObject:[MYObject class]];
Pros & Cons • Easy to use • Fairly simple
• Configuration in place • Invasive • Properties driven! • A few defects
Typhoon
• All Objection features • No macros or XML required
but both supported • Any order of dependencies • Supports configuration management • Supports injection of view controllers and storyboard integration
How it works, quickly Component Factory Assembly MYObject *object =
[componentFactory componentForType:[MYObject class]; - (id)myObject { return [TyphoonDefinition withClass:[MYObject Class]]; }
Assembly ! @interface MYAssemby : TyphoonAssembly ! @implementation MYAssembly !
- (id)imageManager { return [TyphoonDefinition withClass:[MYImageManager class]
Assembly ! @interface MYAssemby : TyphoonAssembly ! @implementation MYAssembly !
- (id)imageManager { return [TyphoonDefinition withClass:[MYImageManager class] initialization:^(TyphoonInitializer* initializer) { ! initializer.selector = @selector(initWithClient:); [initializer injectWithDefinition:[self defaultClient]]; ! }
Assembly ! @interface MYAssemby : TyphoonAssembly ! @implementation MYAssembly !
- (id)imageManager { return [TyphoonDefinition withClass:[MYImageManager class] initialization:^(TyphoonInitializer* initializer) { ! initializer.selector = @selector(initWithClient:); [initializer injectWithDefinition:[self defaultClient]]; ! } properties:^(TyphoonDefinition* definition) { ! [definition injectProperty:@selector(api) withDefinition:[self api]]; [definition injectProperty:@selector(task) withValueAsText:@"${tasks.first}"]; ! [definition setAfterPropertyInjection:@selector(configureBeforeUse)]; [definition setScope:TyphoonScopeSingleton]; }]; }
Pros & Cons • One configuration place • Configuration in
place • Not invasive • Lots of features • More complicated code base • Clarity
None
None
Summary • DI makes testing a lot easier • DI
improves your code effectiveness • DI makes your classes cleaner and more reusable • DI frameworks usage makes your life easier
Thanks!
[email protected]
@maciejoczko