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
72
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
130
Working with Legacy Code (Łódź)
maciejoczko
0
56
iOS TDD Workshop (Gdańsk)
maciejoczko
1
82
UICollectionView Basics and Flow Layout
maciejoczko
0
240
UICollectionView Introduction
maciejoczko
0
63
Working With Legacy Code - iOS TDD Workshop
maciejoczko
0
170
Other Decks in Programming
See All in Programming
SourceGeneratorのススメ
htkym
0
200
CSC307 Lecture 02
javiergs
PRO
1
780
例外処理とどう使い分ける?Result型を使ったエラー設計 #burikaigi
kajitack
16
6.1k
16年目のピクシブ百科事典を支える最新の技術基盤 / The Modern Tech Stack Powering Pixiv Encyclopedia in its 16th Year
ahuglajbclajep
5
1k
「ブロックテーマでは再現できない」は本当か?
inc2734
0
1k
AI Schema Enrichment for your Oracle AI Database
thatjeffsmith
0
310
Smart Handoff/Pickup ガイド - Claude Code セッション管理
yukiigarashi
0
140
HTTPプロトコル正しく理解していますか? 〜かわいい猫と共に学ぼう。ฅ^•ω•^ฅ ニャ〜
hekuchan
2
690
高速開発のためのコード整理術
sutetotanuki
1
400
Apache Iceberg V3 and migration to V3
tomtanaka
0
160
IFSによる形状設計/デモシーンの魅力 @ 慶應大学SFC
gam0022
1
310
Honoを使ったリモートMCPサーバでAIツールとの連携を加速させる!
tosuri13
1
180
Featured
See All Featured
Fireside Chat
paigeccino
41
3.8k
RailsConf & Balkan Ruby 2019: The Past, Present, and Future of Rails at GitHub
eileencodes
141
34k
Mind Mapping
helmedeiros
PRO
0
88
Avoiding the “Bad Training, Faster” Trap in the Age of AI
tmiket
0
77
JAMstack: Web Apps at Ludicrous Speed - All Things Open 2022
reverentgeek
1
350
Art, The Web, and Tiny UX
lynnandtonic
304
21k
What the history of the web can teach us about the future of AI
inesmontani
PRO
1
430
Redefining SEO in the New Era of Traffic Generation
szymonslowik
1
220
The AI Revolution Will Not Be Monopolized: How open-source beats economies of scale, even for LLMs
inesmontani
PRO
3
3k
Six Lessons from altMBA
skipperchong
29
4.2k
Building Experiences: Design Systems, User Experience, and Full Site Editing
marktimemedia
0
410
Designing for Timeless Needs
cassininazir
0
130
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