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
73
0
Share
Embed
Copy iframe code
Copy JS code
Copy link
Start on current slide
Depenedency Injection in iOS
Talk given in Łódź and Lublin, Poland, during the Mobile Warsaw on Tour meetups.
Maciej Oczko
December 17, 2013
More Decks by Maciej Oczko
See All by Maciej Oczko
Dependency Injection on iOS
maciejoczko
1
130
Working with Legacy Code (Łódź)
maciejoczko
0
59
iOS TDD Workshop (Gdańsk)
maciejoczko
1
85
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
コンパウンドプロダクト開発のためのローカルプロセスマネージャー再発明 #layerxgo
izumin5210
0
520
VibeCodingからAgenticWorkflowへ
starfish719
0
950
<title><a id="</title>君はこのHTMLをパースできるか"></a></title> #雑LT_study
pizzacat83
0
190
承認済みなのに差戻しできてしまうバグ、型で潰せます
shinchit
0
120
Introducing Stack Pull Request in GitHub
kkamegawa
0
110
30年振りにコンパイラの定数整数除算を改善した
herumi
9
4.3k
Hello, Hiroshima Geospatial Data! — Exploring DoboX with Python
ra0kley
0
120
自動化したのに回らない テスト運用の壁―AI時代の品質責任と生産性
mfunaki
0
540
Discordを用いたラボオートメーション関連情報収集の自動化
noguhiro2002
0
460
仕様駆動開発へのトライを機に チームに適合する手法を模索し続けている話
freee
PRO
0
660
KotlinConf Extended South Korea 2026 Keynote
l2hyunwoo
0
130
実装をデザインガイドラインに追従させるための取り組み / 260731-dip-mosh-design-system
dachi023
0
7.9k
Featured
See All Featured
Mozcon NYC 2025: Stop Losing SEO Traffic
samtorres
1
510
Chasing Engaging Ingredients in Design
codingconduct
0
290
Bioeconomy Workshop: Dr. Julius Ecuru, Opportunities for a Bioeconomy in West Africa
akademiya2063
PRO
1
310
The Pragmatic Product Professional
lauravandoore
37
7.4k
Applied NLP in the Age of Generative AI
inesmontani
PRO
4
2.4k
We Have a Design System, Now What?
morganepeng
55
8.3k
Thoughts on Productivity
jonyablonski
76
5.3k
Believing is Seeing
oripsolob
1
200
Java REST API Framework Comparison - PWX 2021
mraible
34
9.7k
Stop Working from a Prison Cell
hatefulcrawdad
274
21k
Dominate Local Search Results - an insider guide to GBP, reviews, and Local SEO
greggifford
PRO
0
310
Introduction to Domain-Driven Design and Collaborative software design
baasie
1
950
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