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
Dependency Injection on iOS
Search
Maciej Oczko
June 22, 2015
Programming
1
130
Dependency Injection on iOS
Given at Mobile Warsaw meet up.
Maciej Oczko
June 22, 2015
Tweet
Share
More Decks by Maciej Oczko
See All by Maciej Oczko
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
Depenedency Injection in iOS
maciejoczko
0
72
Other Decks in Programming
See All in Programming
CSC307 Lecture 06
javiergs
PRO
0
690
React 19でつくる「気持ちいいUI」- 楽観的UIのすすめ
himorishige
11
7.4k
Fragmented Architectures
denyspoltorak
0
160
izumin5210のプロポーザルのネタ探し #tskaigi_msup
izumin5210
1
130
CSC307 Lecture 09
javiergs
PRO
1
840
生成AIを使ったコードレビューで定性的に品質カバー
chiilog
1
270
16年目のピクシブ百科事典を支える最新の技術基盤 / The Modern Tech Stack Powering Pixiv Encyclopedia in its 16th Year
ahuglajbclajep
5
1k
副作用をどこに置くか問題:オブジェクト指向で整理する設計判断ツリー
koxya
1
610
FOSDEM 2026: STUNMESH-go: Building P2P WireGuard Mesh Without Self-Hosted Infrastructure
tjjh89017
0
170
AIエージェントのキホンから学ぶ「エージェンティックコーディング」実践入門
masahiro_nishimi
5
470
Raku Raku Notion 20260128
hareyakayuruyaka
0
320
CSC307 Lecture 02
javiergs
PRO
1
780
Featured
See All Featured
Fight the Zombie Pattern Library - RWD Summit 2016
marcelosomers
234
17k
A better future with KSS
kneath
240
18k
The #1 spot is gone: here's how to win anyway
tamaranovitovic
2
940
Measuring Dark Social's Impact On Conversion and Attribution
stephenakadiri
1
130
Conquering PDFs: document understanding beyond plain text
inesmontani
PRO
4
2.3k
How to train your dragon (web standard)
notwaldorf
97
6.5k
Effective software design: The role of men in debugging patriarchy in IT @ Voxxed Days AMS
baasie
0
230
Being A Developer After 40
akosma
91
590k
A Tale of Four Properties
chriscoyier
162
24k
Learning to Love Humans: Emotional Interface Design
aarron
275
41k
brightonSEO & MeasureFest 2025 - Christian Goodrich - Winning strategies for Black Friday CRO & PPC
cargoodrich
3
100
Getting science done with accelerated Python computing platforms
jacobtomlinson
2
120
Transcript
Dependency Injection on iOS #iOSDI
@MaciejOczko #iOSDI
Why DIY Frameworks #iOSDI
! #iOSDI
Tests #iOSDI
We all ❤ TDD #iOSDI
! #iOSDI
! #iOSDI
⏰ #iOSDI
Code changes ! #iOSDI
Mind changes ! #iOSDI
Design changes ! #iOSDI
DI is the ! pattern while reacting to a change
#iOSDI
Trust Driven Development? #iOSDI
! #iOSDI
Why #iOSDI
Example 1 class MusicAdvisor: NSObject { - (instancetype)init { self
= [super init]; if (self) { _albumProvider = [[AlbumProvider alloc] initWithMusicService:[MusicService new] artistsProvider:[ArtistsProvider new]]; } } } #iOSDI
! #iOSDI
Example 2 class MusicAdvisor: NSObject { - (instancetype)init { self
= [super init]; if (self) { self.musicService = [MusicService new]; self.artistsProvider = [ArtistsProvider new]; _albumProvider = [[AlbumProvider alloc] initWithMusicService:self.musicService artistsProvider:self.artistsProvider]; } } } #iOSDI
! #iOSDI
Example 3 class MusicAdvisor: NSObject { - (instancetype)init { self
= [super init]; if (self) { _albumProvider = [AlbumProvider sharedInstance]; } } } #iOSDI
Example 3 continues... class MusicAdvisor: NSObject { - (Soundtrack *)theBestSoundtrackEver
{ NSArray<Album *> *allAlbums = [[AlbumProvider sharedInstance] allAlbums]; ... return [Soundtrack withSongs: bestSongsEver]; } } #iOSDI
! #iOSDI
! #iOSDI
Solution class MusicAdvisor: NSObject { - (instancetype)initWithAlbumProvider:(id <AlbumProvider>)albumProvider { self
= [super init]; if (self) { _albumProvider = albumProvider; } } } #iOSDI
Solution continues... 1 class MusicAdvisor: NSObject { - (instancetype)initWithAlbumProvider:(id <AlbumProvider>)albumProvider
{ self = [super init]; if (self) { _albumProvider = albumProvider; } } } 2 id <AlbumProvider> albumProvider = [AlbumProvider new]; MusicAdvisor *musicAdvisor = [MusicAdvisor new]; musicAdviser.albumProvider = albumProvider; #iOSDI
Initializer approach » Immutability » Clear interface #iOSDI
! #iOSDI
Benefits » Easier to test classes » Tight-coupling removed »
Promotes composition and separation of concepts #iOSDI
DIY #iOSDI
Strategies 1.Pass objects around 2.Develop application context #iOSDI
App context usage @implementation AppDelegate - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{ self.window.rootViewController = [self.appContext rootViewController]; [self.window makeKeyAndVisible]; return YES; } ... @end #iOSDI
App context implementation @implementation AppContext ... - (AlbumsDataSource *)albumsDataSourceWithHTTPClient:(id <HTTPClient>)httpClient
{ return [[AlbumsDataSource alloc] initWithHTTPClient:httpClient]; } - (AlbumsViewController *)rootViewController { AlbumsDataSource *dataSource = [self albumsDataSourceWithHTTPClient:[self httpClient]]; AlbumsViewController *albumsViewController = [[AlbumsViewController alloc] initWithDataSource:dataSource]; albumsViewController.appContext = self; return albumsViewController; } @end #iOSDI
App context usage @implementation AlbumsViewController ... - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath
*)indexPath { ... AlbumDetailsViewController *advc = [self.appContext albumDetailsViewContorllerWithAlbum:album]; [self presentViewController:advc animated:YES completion:nil]; } @end #iOSDI
! #iOSDI
Frame works #iOSDI
Objection Typhoon #iOSDI
Objection » Lightweight » "Annotation" Based Dependency Injection » Bindings
» Lazily instantiates dependencies » Eager Singletons » Initializer Support #iOSDI
Key concepts » Injector » Module #iOSDI
Usage @interface MusicAdvisor @property(nonatomic, strong, readonly) AlbumProvider *albumProvider; @end @implementation
MusicAdvisor objection_requires_sel(@selector(albumProvider)) - (void)awakeFromObjection { // Initialize with injected properties } @end #iOSDI
Usage JSObjectionInjector *injector = [JSObjection createInjector]; MusicAdvisor *musicAdvisor = [injection
getObject:[MusicAdvisor class]]; #iOSDI
Advanced usage @interface MyModule : JSObjectionModule @end @implementation MyModule -
(void)configure { [self bindClass:[AlbumProvider class] toProtocol:@protocol(AlbumProvider)]; [self bindProvider:[MusicAdvisorProvider new] toClass:[MusicAdvisor class]]; [self bindBlock:^(JSObjectionInjector *context) { // Manual creation return soundTrack; } toClass:[Soundtrack class]]; [self bindClass:[MusicAdvisor class] inScope:JSObjectionScopeSingleton]; } @end #iOSDI
Advanced usage JSObjectionInjector *injector = [JSObjection createInjector:[MyModule new]]; MusicAdvisor *musicAdvisor
= [injection getObject:[MusicAdvisor class]]; #iOSDI
Try it » Lightweight » Simple » Flexbile #iOSDI
... or not? » Lightweight » Not Swift friendly »
Property injection approach » Invasive #iOSDI
Typhoon » All Objection's features » Life-cycle management » Circular
dependecies support » Refactorable » Storyboard integration » Swift support #iOSDI
Key concepts » Assembly » Definition #iOSDI
Usage class MusicAssembly: TyphoonAssembly { public dynamic func albumProvider() ->
AnyObject { return TyphoonDefinition.withClass(AlbumProvider.self) } ... } #iOSDI
Usage let assembly = MusicAssembly().activate() let albumProvider = assembly.albumProvider() as!
AlbumProvider #iOSDI
Usage class MusicAssembly: TyphoonAssembly { public dynamic func albumProvider() ->
AnyObject { return TyphoonDefinition.withClass(AlbumProvider.self) } public dynamic func musicAdvisor() -> AnyObject { return TyphoonDefinition.withClass(MusicAdvisor.self) { definition in definition.useInitializer("initWithAlbumProvider:") { initializer in initializer.injectParameterWith(self.albumProvider()) } definition.injectProperty("soundTrackComposer", self.soundtrackComposer()) definition.injectProperty("assembly") // definition.scope = TyphoonScope.Singleton } } } #iOSDI
Usage let assembly = MusicAssembly().activate() let musicAdvisor = assembly.musicAdvisor() as!
MusicAdvisor #iOSDI
Advanced usage public class MusicAssembly: TyphoonAssembly { public var themesAssembly:
ThemesAssembly! public dynamic func musicAdvisor(albumProvier: AlbumProvider) -> AnyObject { return TyphoonDefinition.withClass(MusicAdvisor.self) { definition in definition.useInitializer("initWithAlbumProvider:themesComposer:") { initializer in initializer.injectParameterWith(albumProvider) initializer.injectParameterWith(self.themesAssembly.defaultComposer) } } } public dynamic func appDelegate() -> AnyObject { return TyphoonDefinition.withClass(AppDelegate.self) { (definition) in definition.injectProperty("musicAdvisor", with: self.musicAdvisor()) definition.injectProperty("rootViewController", with: RootViewController()) definition.injectProperty("assembly") } } ... } #iOSDI
Advanced usage @UIApplicationMain class AppDelegate: UIResponder, UIApplicationDelegate { var musicAdvisor:
MusicAdvisor? var rootViewController: RootViewController? var assemby: MusicAssembly? func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool { // Actiation via Info.plist - InitialTyphoonAssemblies self.window?.rootViewController = self.rootViewController self.musicAdvisor?.adviseMe() { [unowned self] music in let musicPlayer = self.assembly.musicPlayer(music: music) ... } return true } } #iOSDI
Cons » May seem overwhelming » Complex » Learning curve
#iOSDI
Pros » Configurable » Promotes initializer-injection » Non-invasive » Supports
testing » Supports Swift #iOSDI
Recap » Dependency injection is powerful pattern » To learn
it, build your own injection mechanism » There are frameworks to help you » Objection » Typhoon #iOSDI
Thanks! @MaciejOczko #iOSDI