$30 off During Our Annual Pro Sale. View Details »
Speaker Deck
Features
Speaker Deck
PRO
Sign in
Sign up for free
Search
Search
Dependency Injection FTW
Search
0xc010d
February 18, 2014
Programming
2
870
Dependency Injection FTW
cocoaheadsNL@Utrecht/Atos
0xc010d
February 18, 2014
Tweet
Share
More Decks by 0xc010d
See All by 0xc010d
Doing CI like a boss
0xc010d
2
380
Reversing 101
0xc010d
6
410
Other Decks in Programming
See All in Programming
認証・認可の基本を学ぼう後編
kouyuume
0
240
Canon EOS R50 V と R5 Mark II 購入でみえてきた最近のデジイチ VR180 事情、そして VR180 静止画に活路を見出すまで
karad
0
130
tparseでgo testの出力を見やすくする
utgwkk
2
240
AIコーディングエージェント(Manus)
kondai24
0
200
Developing static sites with Ruby
okuramasafumi
0
310
ViewファーストなRailsアプリ開発のたのしさ
sugiwe
0
500
UIデザインに役立つ 2025年の最新CSS / The Latest CSS for UI Design 2025
clockmaker
18
7.5k
実は歴史的なアップデートだと思う AWS Interconnect - multicloud
maroon1st
0
220
ローカルLLMを⽤いてコード補完を⾏う VSCode拡張機能を作ってみた
nearme_tech
PRO
0
110
Full-Cycle Reactivity in Angular: SignalStore mit Signal Forms und Resources
manfredsteyer
PRO
0
150
開発に寄りそう自動テストの実現
goyoki
2
1.1k
Context is King? 〜Verifiability時代とコンテキスト設計 / Beyond "Context is King"
rkaga
10
1.3k
Featured
See All Featured
How STYLIGHT went responsive
nonsquared
100
6k
Claude Code のすすめ
schroneko
65
200k
StorybookのUI Testing Handbookを読んだ
zakiyama
31
6.5k
Embracing the Ebb and Flow
colly
88
4.9k
What’s in a name? Adding method to the madness
productmarketing
PRO
24
3.8k
"I'm Feeling Lucky" - Building Great Search Experiences for Today's Users (#IAC19)
danielanewman
231
22k
Helping Users Find Their Own Way: Creating Modern Search Experiences
danielanewman
31
3k
Responsive Adventures: Dirty Tricks From The Dark Corners of Front-End
smashingmag
254
22k
Raft: Consensus for Rubyists
vanstee
141
7.2k
Git: the NoSQL Database
bkeepers
PRO
432
66k
Bioeconomy Workshop: Dr. Julius Ecuru, Opportunities for a Bioeconomy in West Africa
akademiya2063
PRO
0
25
Building a Modern Day E-commerce SEO Strategy
aleyda
45
8.3k
Transcript
Dependency Injection FTW Ievgen Solodovnykov @0xc010d
I… • work at 2dehands.be • do iOS stuff during
the daylight
Inversion of Control • M. Fowler, 1988 (http://martinfowler.com/bliki/ InversionOfControl.html) •
Don't call us, we'll call you • Dependency Injection is one of possible implementations
Dependency Injection • M. Fowler, 2004 (http://www.martinfowler.com/ articles/injection.html) • Helps
to get rid of hard coupling • Easier unit testing and motivates to write testable code • Improves modularity and reusability
Dependency Injection • Constructor injection • Property injection • Interface
injection + Automatic injection
Constructor injection @interface APIClient : NSObject - (void)loadDataWithCompletionBlock:(void(^)(NSError *))block; @end
! //... ! @implementation ViewController { APIClient *_apiClient; } ! - (instancetype)initWithAPIClient:(APIClient *)apiClient { self = [super init]; _apiClient = apiClient; return self; } ! - (void)viewDidLoad { [super viewDidLoad]; [_apiClient loadDataWithCompletionBlock:^(NSError *) { //... }]; } ! @end
Property injection @interface ViewController : UIViewController ! @property (nonatomic, weak)
APIClient *apiClient; ! @end ! //... ! @implementation ViewController ! - (void)viewDidLoad { [super viewDidLoad]; [self.apiClient loadDataWithCompletionBlock:^(NSError *) { //... }]; } ! @end
Method injection @implementation ViewController ! - (void)viewDidLoad { [super viewDidLoad];
[self loadDataWithAPIClient:[[APIClient alloc] init]]; } ! - (void)loadDataWithAPIClient:(APIClient *)apiClient { [apiClient loadDataWithCompletionBlock:^(NSError *) { //... }]; } ! @end
Automatic injection with BloodMagic • https://github.com/railsware/BloodMagic • Runtime-based • Less
code • Custom initializers • And more…
BMLazy @interface ViewController () <BMLazy> ! @property (nonatomic, weak) APIClient
*apiClient; ! @end ! ! @implementation ViewController ! @dynamic apiClient; ! - (void)viewDidLoad { [super viewDidLoad]; [self.apiClient loadDataWithCompletionBlock:^(NSError *) { //... }]; } ! @end
BMLazy [[APIClient alloc] init] @interface ViewController () <BMLazy> ! @property
(nonatomic, weak) APIClient *apiClient; ! @end ! ! @implementation ViewController ! @dynamic apiClient; ! - (void)viewDidLoad { [super viewDidLoad]; [self.apiClient loadDataWithCompletionBlock:^(NSError *) { //... }]; } ! @end
Custom initializer __attribute__((constructor)) void configureAPIClient(void) { BMInitializer *initializer = [BMInitializer
lazyInitializer]; initializer.propertyClass = [APIClient class]; initializer.initializer = ^id (id sender) { static dispatch_once_t onceToken; static APIClient *client; dispatch_once(&onceToken, ^{ client = [[APIClient alloc] init]; }); return client; }; [initializer registerInitializer]; }
Custom initializer { static dispatch_once_t onceToken; static APIClient *client; dispatch_once(&onceToken,
^{ client = [[APIClient alloc] init]; }); return client; }; @interface ViewController () <BMLazy> ! @property (nonatomic, weak) APIClient *apiClient; ! @end ! ! @implementation ViewController ! @dynamic apiClient; ! - (void)viewDidLoad { [super viewDidLoad]; [self.apiClient loadDataWithCompletionBlock:^(NSError *) { //... }]; } ! @end
Protocol binding @protocol APICLient <NSObject> ! - (void)loadDataWithCompletionBlock:(void (^)(NSError *))completionBlock;
! @end ! //--- ! @interface ProductionAPIClient : NSObject <APICLient> @end ! //--- ! @interface ViewController () <BMLazy> ! @property (nonatomic, weak) id<APICLient> apiClient; ! @end ! //--- ! @dynamic apiClient; ! - (void)viewDidLoad { [super viewDidLoad]; [self.apiClient loadDataWithCompletionBlock:^(NSError *) { //... }]; }
Protocol initializer __attribute__((constructor)) void configureProductionAPIClient(void) { BMInitializer *initializer = [BMInitializer
lazyInitializer]; initializer.protocols = @[ @protocol(APICLient) ]; initializer.initializer = ^id (id sender){ static dispatch_once_t onceToken; static ProductionAPIClient *client; dispatch_once(&onceToken, ^{ client = [[ProductionAPIClient alloc] init]; }); return client; }; [initializer registerInitializer]; } !
Container initializer @interface DemoViewController : ViewController @end ! @interface DemoAPIClient
: NSObject <APICLient> @end ! __attribute__((constructor)) void configureDemoAPIClient(void) { BMInitializer *initializer = [BMInitializer lazyInitializer]; initializer.protocols = @[ @protocol(APICLient) ]; initializer.containerClass = [DemoViewController class]; initializer.initializer = ^id (id sender){ static dispatch_once_t onceToken; static DemoAPIClient *client; dispatch_once(&onceToken, ^{ client = [[DemoAPIClient alloc] init]; }); return client; }; [initializer registerInitializer]; }
Disadvantages • Runtime injecting requires time • Runtime errors •
Requires some intelligence
Alternatives • Typhoon (http://www.typhoonframework.org) • Objection (http://objection-framework.org) • Xider a.k.a
LightMagic (@0xc010d)
Xider vs BloodMagic • Almost as fast as ivar assignment
• Less flexible implementation :(
Unit testing / with BloodMagic
Return value tests • Works fine with functions, often doesn’t
require injection • Call function/method with different parameters • Assert return value
State tests • Might require injection • Setup object •
Call method • Assert object property / other method return value
Interaction tests • Most often requires injection • Setup object
• Call method • Assert other object property / method return value
Demo
Thank you! https://speakerdeck.com/0xc010d/dependency-injection-ftw @0xc010d