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
ダックタイピングとidでUserDefaultsをモック化する
Search
417.72KI
September 05, 2019
Programming
2.9k
2
Share
Embed
Copy iframe code
Copy JS code
Copy link
Start on current slide
ダックタイピングとidでUserDefaultsをモック化する
https://fortee.jp/iosdc-japan-2019/proposal/873b4cdb-4c92-4111-bf0b-67a67dbb242e
417.72KI
September 05, 2019
More Decks by 417.72KI
See All by 417.72KI
Comparing decimals in Swift Testing
417_72ki
0
250
Reboot a personal app abandoned for 10 years with recent techs
417_72ki
0
120
iTunes・おぼえていますか〜ScriptingBridge今昔物語〜
417_72ki
1
230
The history of entry-point in iOS app Development
417_72ki
0
540
R.swift to Asset Symbols
417_72ki
0
390
Refactor with using `available` and `deprecated`
417_72ki
3
830
CLIツールにSwift Concurrencyを適用させようとしている話
417_72ki
3
520
CI with Danger-Swift
417_72ki
1
280
Graduation from Playground beginner
417_72ki
3
1k
Other Decks in Programming
See All in Programming
Signal Forms: Details & Live Coding @enterJS 2026 in Mannheim
manfredsteyer
PRO
0
210
AIで効率化できた業務・日常
ochtum
0
160
Mujeres en SEO Summit 2026 - Greatest Disaster Hits en Web Performance
guaca
0
220
LaravelLive Japan の裏方のすべて — 第188回 PHP勉強会@東京 (2026-06-24)
suguruooki
2
140
作って学ぶ、 JSX (TSX) ランタイムの基本
syumai
7
1.7k
Dataformのリポジトリを立ち上げるときにまずやること / dataform-day0-2026
snhryt
0
200
過去最大のMCPアップデート! 2026-07-28 RC版の謎に迫る
licux
6
430
キャリア迷子上等 ─ "ない道"は自分で作ればいい
16bitidol
3
2.7k
分散システム、なんですぐ死んでしまうん?耐障害性を高めたいあなたのためのレジリエンスパターン入門
mshibuya
2
360
これからAgentCoreを触る方へトレンドはGatewayです
har1101
6
460
例外の正しい扱い方 そのエラー try-catchして大丈夫?
jinwatanabe
0
320
SLOをサービス品質の共通言語にするために 取り組んできたこと
wakana0222
0
260
Featured
See All Featured
The Myth of the Modular Monolith - Day 2 Keynote - Rails World 2024
eileencodes
28
3.5k
Exploring the Power of Turbo Streams & Action Cable | RailsConf2023
kevinliebholz
37
6.5k
The AI Revolution Will Not Be Monopolized: How open-source beats economies of scale, even for LLMs
inesmontani
PRO
3
3.5k
Chrome DevTools: State of the Union 2024 - Debugging React & Beyond
addyosmani
10
1.2k
Optimising Largest Contentful Paint
csswizardry
37
3.7k
I Don’t Have Time: Getting Over the Fear to Launch Your Podcast
jcasabona
34
2.8k
HU Berlin: Industrial-Strength Natural Language Processing with spaCy and Prodigy
inesmontani
PRO
0
430
Building Adaptive Systems
keathley
44
3.1k
Have SEOs Ruined the Internet? - User Awareness of SEO in 2025
akashhashmi
0
380
実際に使うSQLの書き方 徹底解説 / pgcon21j-tutorial
soudai
PRO
201
75k
First, design no harm
axbom
PRO
2
1.2k
Agile Leadership in an Agile Organization
kimpetersen
PRO
0
180
Transcript
μοΫλΠϐϯάͱJEͰ 6TFS%FGBVMUTΛϞοΫԽ͢Δ J04%$+BQBOEBZ5SBDL&
struct Me { let name = "Takuhiro Muta" let aka
= "417.72KI" let twitter = "417_72ki" let qiita = "417_72ki" let gitHub = "417-72KI" let products = [ "BuildConfig.swift", "MockUserDefaults", ] let workAt = [ "Rhizome Inc.": "2014/04~2018/01", "iRidge Inc.": "2018/02~2019/08", "WealthNavi Inc.": "2019/09~", ] } TFMGEFTDSJQUJPO
"HFOEB w ςετͱ6TFS%FGBVMUT w μοΫλΠϐϯά w JEܕ w .PDL6TFS%FGBVMUT
ςετͱ6TFS%FGBVMUT
ςετͱ6TFS%FGBVMUT w 6OJU5FTUΛߟ͑Δ্Ͱ6TFS%FGBVMUTͷѻ͍ॏཁ w 6TFS%FGBVMUTΛ5FTUBCMFʹ͍ͨ͠ w QSPUPDPMΛט·ͤΔ w TVJUF/BNFΛ͏
QSPUPDPMΛט·ͤΔ protocol UserDefaultsProtocol: class { func object(forKey defaultName: String) ->
Any? func set(_ value: Any?, forKey defaultName: String) func string(forKey defaultName: String) -> String? ɾ ɾ ɾ } extension UserDefaults: UserDefaultsProtocol {}
QSPUPDPMΛט·ͤΔ class MockUserDefaults { private var mockDictionary: [String: Any] =
[:] } extension MockUserDefaults: UserDefaultsProtocol { func object(forKey defaultName: String) -> Any? { return mockDictionary[defaultName] } func set(_ value: Any?, forKey defaultName: String) { mockDictionary[defaultName] = value } ɾ ɾ ɾ }
QSPUPDPMΛט·ͤΔ protocol UseCase { var userDefaults: UserDefaultsProtocol { get }
func doSomething() } extension UseCase { func doSomething() { userDefaults.set("bar", forKey: "foo") } } class MixInUseCase: UseCase { let userDefaults: UserDefaultsProtocol = UserDefaults.standard } class TestUseCase: UseCase { let userDefaults: UserDefaultsProtocol = MockUserDefaults() } "QQ 5FTU
TVJUF/BNFΛ͏ protocol UseCase { var userDefaults: UserDefaults { get }
func doSomething() } extension UseCase { func doSomething() { userDefaults.set("bar", forKey: "foo") } } class MixInUseCase: UseCase { let userDefaults: UserDefaults = .standard } class TestUseCase: UseCase { let userDefaults: UserDefaults = .init(suiteName: "Mock")! } "QQ 5FTU
ςετͱ6TFS%FGBVMUT w 6TFS%FGBVMUTΛ5FTUBCMFʹ͍ͨ͠ w QSPUPDPMΛט·ͤΔ w طʹ6TFS%FGBVMUTΛϓϩύςΟͱͯ͠༻͍ͯ͠Δ ߹ɺطଘίʔυʹखΛೖΕΔඞཁ͕͋Δ w TVJUF/BNFΛ͏
w /4)PNF%JSFDUPSZ -JCSBSZ1SFGFSFODFT TVJUF/BNF QMJTU͕࡞͞ΕΔ w ςετͰӬଓྖҬΛ͍͡Γͨ͘ͳ͍
ςετͱ6TFS%FGBVMUT w 6TFS%FGBVMUTΛ5FTUBCMFʹ͍ͨ͠ w QSPUPDPMΛט·ͤΔ w TVJUF/BNFΛ͏ w μοΫλΠϐϯάΛͬͨࠇຐज़
μοΫλΠϐϯά
μοΫλΠϐϯά w ܕ͚ʹؔ͢Δߟ͑ํͷҰͭ w ಈతܕ͚ݴޠͰΘΕΔ FH3VCZ 1ZUIPO w ΦϒδΣΫτࣗମ͕ϝιουϓϩύςΟΛ͔࣋ͭͰఆ
OPUܕ r%BWF5IPNBT l*GJUXBMLTMJLFBEVDLBOERVBDLTMJLFBEVDL JUNVTUCFBEVDLz
Ұൠతͳ੩తܕ͚ ී௨ʹΫϥεࢦఆ ݘʹରͯ͠ Εʂ ϫϯʂ
Ұൠతͳ੩తܕ͚ JOUFSGBDFʹΑΔநԽ ಈʹରͯ͠ Εʂ ϫϯʂ
μοΫλΠϐϯάʹΑΔ ಈతܕ͚ Կ͔ΒΜ͕ͱΓ͋͑ͣ Εʂ Կ͔͔Βͳ͍͚Ͳ ΕͬͯݴΘΕͨΒ Δͣ
ϫϯʂ μοΫλΠϐϯάʹΑΔ ಈతܕ͚ Կ͔ΒΜ͕ͱΓ͋͑ͣ Εʂ
0CKFDUJWF$ʹ͓͚Δ μοΫλΠϐϯά @interface Duck: NSObject - (void)walk; - (void)quack; @end
@implementation Duck - (void)walk { NSLog(@"Duck walking"); } - (void)quack { NSLog(@"Quack!!"); } @end @interface Dog: NSObject - (void)walk; - (void)bark; @end @implementation Dog - (void)walk { NSLog(@"Dog walking"); } - (void)bark { NSLog(@"Bark!!"); } @end void walkAndQuack(id duck) { [duck walk]; [duck quack]; }
0CKFDUJWF$ʹ͓͚Δ μοΫλΠϐϯά walkAndQuack([Duck new]); // Duck walking // Quack!! walkAndQuack([Dog
new]); // Dog walking // unrecognized selector sent to instance
0CKFDUJWF$ʹ͓͚Δ μοΫλΠϐϯά void walkAndQuack(id duck) { [duck walk]; [duck quack];
} walkAndQuack([Duck new]); walkAndQuack([Dog new]);
0CKFDUJWF$ʹ͓͚Δ μοΫλΠϐϯά void walkAndQuack(Duck *duck) { [duck walk]; [duck quack];
} walkAndQuack([Duck new]); walkAndQuack([Dog new]); *ODPNQBUJCMFQPJOUFSUZQFTQBTTJOH%PH UPQBSBNFUFSPGUZQF%VDL ܯࠂ͕ग़Δ͚ͩͰϏϧυ௨Δ
0CKFDUJWF$ͷܕγεςϜ w ऑ͍੩తܕ͚ w ࣮ߦ࣌ʹܕͷνΣοΫΛ͠ͳ͍ w ܕ͕Ұக͠ͳ͍ΦϒδΣΫτΛೖͯ͠ ίϯύΠϧΤϥʔʹͳΒͳ͍ ܯࠂͷΈ
w ϝιουΛݺͿࡍʹ͕͋ΔͱΫϥογϡ͢Δ w +BWB4XJGUڧ͍੩తܕ͚
ڧ͍ܕ͚ͱऑ͍ܕ͚ 4USJOH < ߴࡔึ೫Ռ Ҁֆཬ > ऑ͍ܕ͚
ڧ͍ܕ͚
ऑ͍ܕ͚ w ࣮ߦ࣌ʹܕͷνΣοΫΛ͠ͳ͍ w ܕͷνΣοΫΛͤͣʹϝιουΛ࣮ߦ͢Δ w ֨ೲ͞Ε͍ͯΔΦϒδΣΫτ͕ͦͷϝιουΛ࣮ߦͰ ͖Ε0, %FMFHBUF
@protocol UITableViewDataSource<NSObject> @required - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section; - (UITableViewCell *)tableView:(UITableView
*)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath; @optional - (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView; - (nullable NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section; - (nullable NSString *)tableView:(UITableView *)tableView titleForFooterInSection:(NSInteger)section; ɾ ɾ ɾ %FMFHBUFͱμοΫλΠϐϯά ࣮Λڧ੍͠ͳ͍ϝιου܈
@implementation UITableView ɾ ɾ ɾ - (NSInteger)numberOfSections { if ([self.dataSource
respondsToSelector:@selector(numberOfSectionsInTableView:)]) { return [self.dataSource numberOfSectionsInTableView:self]; } else { return 1; } } ɾ ɾ ɾ @end %FMFHBUFͱμοΫλΠϐϯά ϝιου͕࣮͞Ε͍ͯΔ͔νΣοΫ
%FMFHBUFͱμοΫλΠϐϯά @interface HogeViewController : UIViewController <UITableViewDataSource, UITableViewDelegate> @property (nonatomic, weak)
IBOutlet UITableView *tableView; @end @implementation HogeViewController - (void)viewDidLoad { [super viewDidLoad]; self.tableView.dataSource = self; self.tableView.delegate = self; } - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section { ɾɾɾ } - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { ɾɾɾ } @end ϓϩτίϧએݴ σϦήʔτϝιου
%FMFHBUFͱμοΫλΠϐϯά @interface HogeViewController : UIViewController <UITableViewDataSource, UITableViewDelegate> @property (nonatomic, weak)
IBOutlet UITableView *tableView; @end @implementation HogeViewController - (void)viewDidLoad { [super viewDidLoad]; self.tableView.dataSource = self; self.tableView.delegate = self; } - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section { ɾɾɾ } - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { ɾɾɾ } @end OVNCFS0G4FDUJPOT*O5BCMF7JFX͕࣮͞Ε͍ͯͳ͍ͷͰTFDUJPO
%FMFHBUFͱμοΫλΠϐϯά @interface HogeViewController : UIViewController <UITableViewDataSource, UITableViewDelegate> @property (nonatomic, weak)
IBOutlet UITableView *tableView; @end @implementation HogeViewController - (void)viewDidLoad { [super viewDidLoad]; self.tableView.dataSource = self; self.tableView.delegate = self; } - (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView { return 2 } - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section { ɾɾɾ } - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { ɾɾɾ } @end OVNCFS0G4FDUJPOT*O5BCMF7JFX͕࣮͞ΕͯΛฦ͢Α͏ʹ
@interface HogeViewController : UIViewController @property (nonatomic, weak) IBOutlet UITableView *tableView;
@end @implementation HogeViewController - (void)viewDidLoad { [super viewDidLoad]; self.tableView.dataSource = self; self.tableView.delegate = self; } - (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView { return 2 } - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section { ɾɾɾ } - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { ɾɾɾ } @end %FMFHBUFͱμοΫλΠϐϯά "TTJHOJOHUPJE6*5BCMF7JFX%BUB4PVSDF@/VMMBCMFGSPN JODPNQBUJCMFUZQF)PHF7JFX$POUSPMMFS DPOTU@@TUSPOH ϓϩτίϧએݴΛফͯ͠Έͨ߹ ܯࠂ͕ग़Δ͚ͩͰϏϧυ௨Δ Ϋϥογϡͤͣ5BCMF7JFXͱͯ͠ػೳ͢Δ
JEܕ
JEܕ w 0CKFDUJWF$ಠࣗͷ൚༻σʔλܕ w +BWBͰݴ͏0CKFDUɺ4XJGUͰݴ͏"OZ w ΦϒδΣΫτͷϙΠϯλΛอ࣋ w Ωϟετ͢Δ͜ͱͳ͘ೖϝιουݺͼग़͕͠Մೳ w
ܕ҆શੑΛࣺͯͯॊೈੑʹεςʔλεΛৼͬͯΔ
NSArray *array = @[ @{@"hoge":@"fuga"}, ]; for (NSObject *obj in
array) { NSLog([obj objectForKey:@"hoge"]); } JEܕͷԸܙ /PWJTJCMF!JOUFSGBDFGPS/40CKFDUEFDMBSFTUIFTFMFDUPSPCKFDU'PS,FZ
NSArray *array = @[ @{@"hoge":@"fuga"}, ]; for (NSObject *obj in
array) { NSLog([(NSDictionary *)obj objectForKey:@"hoge"]); } JEܕͷԸܙ Ωϟετ͢Δ͜ͱͰݺͼग़ͤΔΑ͏ʹ
NSArray *array = @[ @{@"hoge":@"fuga"}, ]; for (id obj in
array) { NSLog([obj objectForKey:@"hoge"]); } JEܕͷԸܙ Ωϟετͤͣʹ/4%JDUJPOBSZͱͯ͠ѻ͏͜ͱ͕Ͱ͖Δ
NSArray *array = @[ @{@"hoge":@"fuga"}, ]; for (NSDictionary *obj in
array) { NSLog([obj objectForKey:@"hoge"]); } JEܕͷԸܙ /4%JDUJPOBSZͱͯ͠औΓग़ͤΔ ॊೈੑͷߴ͍ίʔσΟϯά͕Մೳʹ
NSArray *array = @[ @{@"hoge":@"fuga"}, @"hogehoge" ]; for (NSDictionary *obj
in array) { NSLog([obj objectForKey:@"hoge"]); } JEܕͷԸܙ GVHB
NSArray *array = @[ @{@"hoge":@"fuga"}, @"hogehoge" ]; for (NSDictionary *obj
in array) { NSLog([obj objectForKey:@"hoge"]); } JEܕͷԸܙ VOSFDPHOJ[FETFMFDUPSTFOUUPJOTUBODF
NSArray <NSDictionary <NSString *, id> *> *array = @[ @{@"hoge":@"fuga"},
@"hogehoge" ]; for (NSDictionary <NSString *, id> *obj in array) { NSLog([obj objectForKey:@"hoge"]); } -JHIUXFJHIU(FOFSJDT 0CKFDUPGUZQF/44USJOH JTOPUDPNQBUJCMFXJUIBSSBZ FMFNFOUUZQF/4%JDUJPOBSZ/44USJOH JE
6TFS%FGBVMUTͱμοΫλΠϐϯά
w ऑ͍੩తܕ͚ w ࣮ߦ࣌ʹܕͷνΣοΫΛ͠ͳ͍ w ܕͷνΣοΫΛͤͣʹϝιουΛ࣮ߦ͢Δ w ֨ೲ͞Ε͍ͯΔΦϒδΣΫτ͕ͦͷϝιουΛ࣮ߦͰ͖Ε0, 6TFS%FGBVMUTͱμοΫλΠϐϯά /46TFS%FGBVMUT͕͍࣋ͬͯΔ
ϝιουΛͦͬ͘ΓਅࣅͨΫϥεΛ࡞ِͬͯͯ͋͛͠Δ
.PDL6TFS%FGBVMUT
.PDL6TFS%FGBVMUT IUUQTHJUIVCDPN,*.PDL6TFS%FGBVMUT
%FNP
ҙ w μοΫλΠϐϯάࣗମ͋͘·Ͱख๏ ߟ͑ํ Ͱ͋Γɺ ภʹࠇຐज़ͩͱݴ͍Εͳ͍ w ࣮ࡍɺ1ZUIPO1)1ͳͲͷಈతܕ͚ܥݴޠͰ͜ͷ ߟ͑ํʹج͍ͮͨϓϩάϥϛϯά͕ߦΘΕΔ w
ࢍ൱͋Δ͕ɺ͜ΕͰϙϦϞʔϑΟζϜΛ࣮ݱ͍ͯ͠Δ ͱ͍͏આ͋Δ
·ͱΊ w 0CKFDUJWF$ऑ͍ܕ͚ͳͷͰશؔ͘ແ͍ΫϥεͷΦ ϒδΣΫτΛೖͰ͖Δ w μοΫλΠϐϯάʹΑͬͯ/46TFS%FGBVMUTͱಉ͡ϝ ιουΛએݴͨ͠ผͷΫϥεΛએݴِ͕ͯ͠Մೳʹ w JEܕΛ͏͜ͱͰΩϟετෆཁ
5IBOLZPV IUUQTHJUIVCDPN,*.PDL6TFS%FGBVMUT
ࢀߟจݙ w μοΫɾλΠϐϯά 8JLJQFEJB IUUQTKBXJLJQFEJBPSHXJLJ &&&"'&##& #'&"&&#& #
w JEܕͱಈత݁߹ͷ֓೦ IUUQTKQSPHSBNFSDPNPCKFDUJWFD w ·ͭͱɹϓϩάϥϛϯάͷΦΩςୈճʢʣ IUUQTUFDIOJLLFJCQDPKQJUBSUJDMF$0-6./
ʲॏཁͳҙࣄ߲ʳ ɾຊࢿྉɺஅఆతஅΛఏڙ͢ΔͷͰͳ͘ɺใΛఏڙ͢Δ͜ͱͷΈΛతͱ͓ͯ͠Γɺ͍͔ͳΔछྨ ͷק༠͢ΔͷͰ͋Γ·ͤΜɻ࠷ऴతͳܾఆɺ͓٬༷ࣗͰஅ͢Δͷͱ͠ɺࣾ͜ΕʹҰ ؔ༩ͤͣɺ·ͨɺҰͷΛෛ͍·ͤΜɻ ɾຊࢿྉʹকདྷͷग़དྷࣄʹؔ͢Δ༧ؚ͕·Ε͍ͯΔ߹͕͋Γ·͕͢ɺͦΕΒ༧Ͱ͋Γɺ·ͨɺຊࢿ ྉͷ༰ͷਖ਼֬ੑɺ৴པੑɺશੑɺద࣌ੑΛҰอূ͢ΔͷͰ͋Γ·ͤΜɻຊࢿྉʹج͍ͮͯඃͬͨ ͍͔ͳΔଛʹ͍ͭͯɺࣾҰͷΛෛ͍·ͤΜɻ·ͨɺࣾɺ৽͍͠ใকདྷͷग़དྷࣄͦͷଞ ͷใʹ͍ͭͯɺߋ৽ຢగਖ਼͢ΔٛΛෛ͍·ͤΜɻ ɾຊࢿྉΛར༻͢Δ͜ͱʹΑΓ͓٬༷ʹੜͨ͡తଛɺؒతଛɺੜతଛͦͷଞ͍͔ͳΔଛʹͭ ͍ͯɺࣾҰͷΛෛ͍·ͤΜɻ
߸ɿΣϧεφϏגࣜձࣾۚ༥औҾۀऀؔ౦ࡒہʢۚʣୈ߸ Ճೖڠձɿຊূ݊ۀڠձҰൠࣾஂ๏ਓຊࢿސۀڠձ