Upgrade to Pro
— share decks privately, control downloads, hide ads and more …
Speaker Deck
Sign up for free
Menu
Search
Features
All features
Private URLs
Password Protection
Custom URLS
Scheduled publishing
Remove Branding
Restrict embedding
Deck Collections
Notes
Features
All features
Private URLs
Password Protection
Custom URLS
Scheduled publishing
Remove Branding
Restrict embedding
Deck Collections
Notes
Explore
Featured decks
Featured speakers
Programming
Technology
Storyboards
Explore
Featured decks
Featured speakers
Programming
Technology
Storyboards
Pricing
Search
Sign in
Sign up for free
ダックタイピングとidでUserDefaultsをモック化する
Search
417.72KI
September 05, 2019
Programming
3k
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
iOSDC 2026
417_72ki
0
20
Comparing decimals in Swift Testing
417_72ki
0
260
Reboot a personal app abandoned for 10 years with recent techs
417_72ki
0
130
iTunes・おぼえていますか〜ScriptingBridge今昔物語〜
417_72ki
1
240
The history of entry-point in iOS app Development
417_72ki
0
540
R.swift to Asset Symbols
417_72ki
0
400
Refactor with using `available` and `deprecated`
417_72ki
3
850
CLIツールにSwift Concurrencyを適用させようとしている話
417_72ki
3
530
CI with Danger-Swift
417_72ki
1
280
Other Decks in Programming
See All in Programming
個人開発基盤をまるごとCloudflareに引っ越して爆速で総合的体験を向上させた話
tinykitten
0
160
GKE アップグレード前に知っておきたい Blue/Green と PDB の関係
stkk
0
160
go-spidermonkeyでAIエージェントのCode Modeを実装する
syumai
3
1.5k
巨大モノリシックアプリ モダン化大作戦
ktcryomm
0
890
モバイル交通系ICへのチャージ実例から考える、クロスプラットフォーム開発におけるiOS実機テスト設計とCI運用
yusuga
1
360
GraphRAGのKnowledge Graphを 直接!見る/View-GraphRAG's-KnowledgeGraph-directly!
tyumugi1113
1
290
LLMは4年分のCompose移行を再現できるのか?実プロダクト279件のXMLで探る自動化の境界線
makun
0
560
AWS Step Functions 大規模並列の壁を越える / jaws-sonic-2026-niigata-step-functions
kasacchiful
PRO
1
440
数年滞っていたダークモード対応をおよそ2週間で完了させる
chigichan24
0
700
Intent as Code
shoppingjaws
6
940
ゲームコントローラやキーボードのファームウェアをSwiftで書く
kishikawakatsumi
1
210
世界の中心で、AI(App Intents)をさけぶ ー App Intents中心設計の実践ガイド
touyou
0
400
Featured
See All Featured
AI Search: Implications for SEO and How to Move Forward - #ShenzhenSEOConference
aleyda
1
1.4k
Agile Leadership in an Agile Organization
kimpetersen
PRO
0
230
Utilizing Notion as your number one productivity tool
mfonobong
4
590
Why You Should Never Use an ORM
jnunemaker
PRO
61
10k
Gemini Prompt Engineering: Practical Techniques for Tangible AI Outcomes
mfonobong
2
520
YesSQL, Process and Tooling at Scale
rocio
174
15k
Evolving SEO for Evolving Search Engines
ryanjones
0
280
Have SEOs Ruined the Internet? - User Awareness of SEO in 2025
akashhashmi
0
500
Leo the Paperboy
mayatellez
9
2.3k
The MySQL Ecosystem @ GitHub 2015
samlambert
251
13k
Bootstrapping a Software Product
garrettdimon
PRO
306
120k
A brief & incomplete history of UX Design for the World Wide Web: 1989–2019
jct
2
500
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./
ʲॏཁͳҙࣄ߲ʳ ɾຊࢿྉɺஅఆతஅΛఏڙ͢ΔͷͰͳ͘ɺใΛఏڙ͢Δ͜ͱͷΈΛతͱ͓ͯ͠Γɺ͍͔ͳΔछྨ ͷק༠͢ΔͷͰ͋Γ·ͤΜɻ࠷ऴతͳܾఆɺ͓٬༷ࣗͰஅ͢Δͷͱ͠ɺࣾ͜ΕʹҰ ؔ༩ͤͣɺ·ͨɺҰͷΛෛ͍·ͤΜɻ ɾຊࢿྉʹকདྷͷग़དྷࣄʹؔ͢Δ༧ؚ͕·Ε͍ͯΔ߹͕͋Γ·͕͢ɺͦΕΒ༧Ͱ͋Γɺ·ͨɺຊࢿ ྉͷ༰ͷਖ਼֬ੑɺ৴པੑɺશੑɺద࣌ੑΛҰอূ͢ΔͷͰ͋Γ·ͤΜɻຊࢿྉʹج͍ͮͯඃͬͨ ͍͔ͳΔଛʹ͍ͭͯɺࣾҰͷΛෛ͍·ͤΜɻ·ͨɺࣾɺ৽͍͠ใকདྷͷग़དྷࣄͦͷଞ ͷใʹ͍ͭͯɺߋ৽ຢగਖ਼͢ΔٛΛෛ͍·ͤΜɻ ɾຊࢿྉΛར༻͢Δ͜ͱʹΑΓ͓٬༷ʹੜͨ͡తଛɺؒతଛɺੜతଛͦͷଞ͍͔ͳΔଛʹͭ ͍ͯɺࣾҰͷΛෛ͍·ͤΜɻ
߸ɿΣϧεφϏגࣜձࣾۚ༥औҾۀऀؔ౦ࡒہʢۚʣୈ߸ Ճೖڠձɿຊূ݊ۀڠձҰൠࣾஂ๏ਓຊࢿސۀڠձ