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
How we build our app with minimum 3rd party dep...
Search
horimislime
October 04, 2018
Programming
0
87
How we build our app with minimum 3rd party dependencies
bitFlyer Drink Meetup for iOSエンジニア 登壇資料
https://bitflyer.connpass.com/event/100661
horimislime
October 04, 2018
Tweet
Share
More Decks by horimislime
See All by horimislime
PagerDuty を軸にした On-Call 構築と運用課題の解決 / PagerDuty Japan Community Meetup 4
horimislime
1
210
スタートアップの急成長に寄り添うOn-Call体制構築とその変遷
horimislime
3
1.6k
サポート効率を上げるためのロギング環境構築
horimislime
7
3.8k
migrating-from-promise-to-reactive
horimislime
0
370
社内Swiftもくもく会成果発表
horimislime
0
120
Swift Optional Extension Tips
horimislime
1
1.5k
ios-internationalization
horimislime
2
8.9k
UI testing in XCode7
horimislime
3
770
UIテストをカジュアルに自動化 / UI Automation using Remote
horimislime
2
2.3k
Other Decks in Programming
See All in Programming
Rubyと自由とAIと
yotii23
6
1.6k
Ruby on cygwin 2025-02
fd0
0
180
Honoのおもしろいミドルウェアをみてみよう
yusukebe
1
220
kintone開発を効率化するためにチームで試した施策とその結果を大放出!
oguemon
0
110
新宿駅構内を三人称視点で探索してみる
satoshi7190
2
120
クリーンアーキテクチャから見る依存の向きの大切さ
shimabox
5
960
未経験でSRE、はじめました! 組織を支える役割と軌跡
curekoshimizu
1
140
お前もAI鬼にならないか?👹Bolt & Cursor & Supabase & Vercelで人間をやめるぞ、ジョジョー!👺
taishiyade
7
4.2k
ファインディLT_ポケモン対戦の定量的分析
fufufukakaka
0
920
責務と認知負荷を整える! 抽象レベルを意識した関心の分離
yahiru
8
1.3k
Open source software: how to live long and go far
gaelvaroquaux
0
660
Domain-Driven Transformation
hschwentner
2
1.9k
Featured
See All Featured
How To Stay Up To Date on Web Technology
chriscoyier
790
250k
We Have a Design System, Now What?
morganepeng
51
7.4k
The MySQL Ecosystem @ GitHub 2015
samlambert
250
12k
Put a Button on it: Removing Barriers to Going Fast.
kastner
60
3.7k
I Don’t Have Time: Getting Over the Fear to Launch Your Podcast
jcasabona
32
2.1k
Building a Scalable Design System with Sketch
lauravandoore
461
33k
A designer walks into a library…
pauljervisheath
205
24k
Let's Do A Bunch of Simple Stuff to Make Websites Faster
chriscoyier
507
140k
Scaling GitHub
holman
459
140k
How GitHub (no longer) Works
holman
314
140k
ピンチをチャンスに:未来をつくるプロダクトロードマップ #pmconf2020
aki_iinuma
114
50k
Bootstrapping a Software Product
garrettdimon
PRO
306
110k
Transcript
bitFlyerΞϓϦ ͍͔ʹͯ͠ϥΠϒϥϦґଘΛ࠷খݶʹ ։ൃΛߦ͍ͬͯΔͷ͔ גࣜձࣾCJU'MZFS ງݟफҰ
ΞδΣϯμ • bitFlyer ͷΞϓϦͱ։ൃελΠϧͷհ • ϥΠϒϥϦ࠾༻ͰؕΓ͍͢᠘ͱɺ bitFlyerͰͷΞϓϩʔν • ΞϓϩʔνΛৼΓฦͬͯ
bitFlyer ͷΞϓϦͱ։ൃελΠϧͷհ
bitFlyer Wallet • ຊ࠷େͷԾ௨՟औҾॴ CJU'MZFSͷJ04͚ΞϓϦ • ͔Β։ൃ։࢝ • ίʔυϕʔεສߦ
*1 ௐࠪҕୗઌϚΫϩϛϧʢ2018 2 ݄ɺΠϯλʔωοτௐࠪʮԾ௨՟ɾ҉߸௨՟औҾ αʔϏεʹؔ͢ΔΞϯέʔτʯʣɺBitcoin ຊޠใαΠτௐɻ 2016 4݄-2018 4 ݄ɺࠃऔҾॴͷ૯݄ؒग़དྷߴʢݱ/ܾࠩۚࡁ/ઌऔҾΛؚΉʣ
ͲͷΑ͏ʹ࡞͍ͬͯΔ͔ • ݱࡏ4ਓͰ։ൃத • ΞʔΩςΫνϟجຊతʹMVC • 3rd partyϥΠϒϥϦʹཔΓ"͗ͣ͢"։ൃ͍ͯ͠Δ
bitFlyerͰ͍ͬͯΔϥΠϒϥϦ
bitFlyerͰ͍ͬͯΔϥΠϒϥϦ
bitFlyerͰ͍ͬͯΔϥΠϒϥϦ
bitFlyerͰ͍ͬͯΔϥΠϒϥϦ ͘͝Ұ෦Ͱ༻ ࠷ۙ$PEBCMFʹ
ຊ͞ͳ͍͜ͱ • ඪ४SDKݪཧओٛతͳɾOSSͷDisΓ • ϥΠϒϥϦʹͲΕ͘Β͍པΔ͖͔ঢ়گʹΑΔ • ͷݟࠐΊΔαʔϏεɾҰఆͷ։ൃྗ͕͋ΔνʔϜ Ͱࣗલ࣮ͷํ͕ϝϦοτ͕ߴ͍
ຊ͢͜ͱ • ϥΠϒϥϦศར͕ͩɺΉΈʹ͏ͱ͔ͤʹ • Ͳ͜Ͱ᠘ʹؕΓ͍͢ͷ͔ʁ • ͦ͜ͰͲ͏࣮͍ͯ͠Δͷ͔ʁʹ͍ͭͯհ͠·͢
ϥΠϒϥϦ࠾༻ͰؕΓ͍͢᠘ͱ bitFlyerͰͷΞϓϩʔν
ϥΠϒϥϦ࠾༻ͰؕΓ͍͢᠘ͱ bitFlyerͰͷΞϓϩʔν • UIKitͰϥΠϒϥϦΛ͍ͨ͘ͳΔϙΠϯτ 1. UITableViewͷѻ͍ʹ͘͞ 2. ํόΠϯσΟϯά 3. StyleComponentͷऔΓѻ͍
(Font, Color, IB) • Өڹൣғ͕ۃେԽ • ֶशίετɾϩοΫΠϯΛͲ͏ղܾ͢Δ͔ʁ
1. UITableViewͷѻ͍ʹ͘͞
1. UITableViewͷѻ͍ʹ͘͞ • ࣌એݴతUITableView / CollectionViewઓࠃ࣌ʁ • RxDataSources Λ࢝ΊɺiOSDCͰʹ •
ܾఆతͳఆ൪·ͩͳ͘ɺͦΕͧΕҰҰΞϦ • ѻ͍͢͞ͱҾ͖͑ʹύϥμΠϜ่յ͕ى͜Δ
ബ͍ϑϨʔϜϫʔΫͷඋ • ͱ͍͑ૉͷUIKitݫ͍͠ • ࣗલͰܰྔϑϨʔϜϫʔΫΛ࣮͍ͯ͠Δ • ࣮ίετ͔͔Δ͕ɺऔΓճ͘͢͠ ඞཁʹԠ֦ͯ͡ு͍͢͠
ബ͍ϑϨʔϜϫʔΫͷ࣮ final class HistoryViewController: UIViewController { @IBOutlet private weak var
tableView: UITableView! private var entities = [History]() private let dataSource = TableDataSource() private let entitiesProvider = EntitiesProvider<History>() override func viewDidLoad() { super.viewDidLoad() dataSource.mapper.register("HistoryCell") { (cell: HistoryCell, entity: History) in cell.update(with: entity) } dataSource.provider = entitiesProvider tableView.dataSource = dataSource tableView.delegate = self fetchData() }
ബ͍ϑϨʔϜϫʔΫͷ࣮ final class HistoryViewController: UIViewController { @IBOutlet private weak var
tableView: UITableView! private var entities = [History]() private let dataSource = TableDataSource() private let entitiesProvider = EntitiesProvider<History>() override func viewDidLoad() { super.viewDidLoad() dataSource.mapper.register("HistoryCell") { (cell: HistoryCell, entity: History) in cell.update(with: entity) } dataSource.provider = entitiesProvider tableView.dataSource = dataSource tableView.delegate = self fetchData() } EntitiesProvider: ίϯςϯπҰཡͷϚωʔδ TableDataSource: UITableViewDataSourceʹ४ڌ Provider -> TableViewͷڮ͠
ബ͍ϑϨʔϜϫʔΫͷ࣮ final class HistoryViewController: UIViewController { @IBOutlet private weak var
tableView: UITableView! private var entities = [History]() private let dataSource = TableDataSource() private let entitiesProvider = EntitiesProvider<History>() override func viewDidLoad() { super.viewDidLoad() dataSource.mapper.register("HistoryCell") { (cell: HistoryCell, entity: History) in cell.update(with: entity) } dataSource.provider = entitiesProvider tableView.dataSource = dataSource tableView.delegate = self fetchData() } EntitiesProvider: ίϯςϯπҰཡͷϚωʔδ TableDataSource: UITableViewDataSourceʹ४ڌ Provider -> TableViewͷڮ͠
ബ͍ϑϨʔϜϫʔΫͷ࣮ final class HistoryViewController: UIViewController { @IBOutlet private weak var
tableView: UITableView! private var entities = [History]() private let dataSource = TableDataSource() private let entitiesProvider = EntitiesProvider<History>() override func viewDidLoad() { super.viewDidLoad() dataSource.mapper.register("HistoryCell") { (cell: HistoryCell, entity: History) in cell.update(with: entity) } dataSource.provider = entitiesProvider tableView.dataSource = dataSource tableView.delegate = self fetchData() } reloadData()ͨ͠Βclosure͕ݺΕΔ (cellForRowAt૬)
ബ͍ϑϨʔϜϫʔΫͷྑ͞ • ࠷খݶͷ࣮ • ෆඞཁͳ֓೦͕ಋೖ͞Εͳ͍ • ཁ݅ɾҙࣝυϦϒϯͰਐԽͰ͖Δ
2. ํόΠϯσΟϯά
2. ํόΠϯσΟϯά • ೖྗΛ͏6*5BCMF7JFX • 5FYUϑΥʔϜɺબࢶεΠον • #JOEJOHΧοίΑ͘ॻ͚Δ͕ Λ͘͜͢͠Δ •
ෳࡶͳؔΛ͍࢝ΊΔͱ σόοάࠔʹ
TableViewCellͷ࠶ར༻ΛΊͯγϯϓϧʹ • Γ͍ͨͷೖྗʹԠͨ͡Πϕϯτॲཧɾೖྗࢀর • Cellͷ࠶ར༻ΛΊͯ͠·͑ྑ͍ • ೖྗϑΥʔϜͱ༗ݶͳCellͰߏ͞Ε͍ͯΔͣ
CellΛ࠶ར༻͠ͳ͍γϯϓϧͳTableView࣮ final class InputFormViewController: UIViewController { private lazy var userNameCell:
TextFieldCell = { ... }() private lazy var preferenceCell: SwitchCell = { ... }() private lazy var rows: [UITableViewCell] = { [userNameCell, preferenceCell] }() @IBAction private func doneTapped() { API.send( name: userNameCell.textField.text, preference: preferenceCell.toggle.isOn ) } } extension InputFormViewController: UITableViewDataSource { func tableView(…, cellForRowAt indexPath: IndexPath) -> UITableViewCell { return rows[indexPath.row] } }
CellΛ࠶ར༻͠ͳ͍γϯϓϧͳTableView࣮ final class InputFormViewController: UIViewController { private lazy var userNameCell:
TextFieldCell = { ... }() private lazy var preferenceCell: SwitchCell = { ... }() private lazy var rows: [UITableViewCell] = { [userNameCell, preferenceCell] }() @IBAction private func doneTapped() { API.send( name: userNameCell.textField.text, preference: preferenceCell.toggle.isOn ) } } extension InputFormViewController: UITableViewDataSource { func tableView(…, cellForRowAt indexPath: IndexPath) -> UITableViewCell { return rows[indexPath.row] } }
CellΛ࠶ར༻͠ͳ͍γϯϓϧͳTableView࣮ final class InputFormViewController: UIViewController { private lazy var userNameCell:
TextFieldCell = { ... }() private lazy var preferenceCell: SwitchCell = { ... }() private lazy var rows: [UITableViewCell] = { [userNameCell, preferenceCell] }() @IBAction private func doneTapped() { API.send( name: userNameCell.textField.text, preference: preferenceCell.toggle.isOn ) } } extension InputFormViewController: UITableViewDataSource { func tableView(…, cellForRowAt indexPath: IndexPath) -> UITableViewCell { return rows[indexPath.row] } }
CellΛ࠶ར༻͠ͳ͍γϯϓϧͳTableView࣮ final class InputFormViewController: UIViewController { private lazy var userNameCell:
TextFieldCell = { ... }() private lazy var preferenceCell: SwitchCell = { ... }() private lazy var rows: [UITableViewCell] = { [userNameCell, preferenceCell] }() @IBAction private func doneTapped() { API.send( name: userNameCell.textField.text, preference: preferenceCell.toggle.isOn ) } } extension InputFormViewController: UITableViewDataSource { func tableView(…, cellForRowAt indexPath: IndexPath) -> UITableViewCell { return rows[indexPath.row] } } ֤Form Cell໊લͷࢀরΛ͍࣋ͬͯΔ UIύʔπ͕͍࣋ͬͯΔσʔλΛݟʹ͍͘
࠶ར༻ΛΊΔ͜ͱͷྑ͞ • γϯϓϧ • ૉͳ࣮ͳͷͰ୭͕ಡΜͰ͔Γ͍͢ • ొਓΛݶఆ͢ΔͱϝϦοτ͕େ͖͍
3. StyleComponentͷऔΓѻ͍
3. Style ComponentͷऔΓѻ͍ • 5FYU $PMPSͳͲൣғʹӨڹ • /4"UUSJCVUFE4USJOH 6*4UPSZCPBSE͕ΠϚΠν •
ඪ४4%,͕ؤுͬͯ΄͍͠ • 044େͳ ग़యWTPV[BBXFTPNFJPTUFYU
Text, Color Styles • ΞϓϦશମʹίϯϙʔωϯτɾελΠϧΨΠυඋࡁ • NSAttributedStringͷextension initializerͰશςΩετʹ ৭ϑΥϯτͷελΠϧΛద༻
Color Paletteͷ࣮ extension UIColor { private static func bf_blueColor() ->
UIColor { return UIColor(red: 0.26, green: 0.52, blue: 0.75, alpha: 1.0) } private static func bf_lightBlueColor() -> UIColor { return UIColor(red: 0.16, green: 0.64, blue: 0.89, alpha: 1.0) } … static func primaryColor() -> UIColor { return bf_blueColor() } static func secondaryTextColor() -> UIColor { return bf_grayColor() } … }
Color Paletteͷ࣮ extension UIColor { private static func bf_blueColor() ->
UIColor { return UIColor(red: 0.26, green: 0.52, blue: 0.75, alpha: 1.0) } private static func bf_lightBlueColor() -> UIColor { return UIColor(red: 0.16, green: 0.64, blue: 0.89, alpha: 1.0) } … static func primaryColor() -> UIColor { return bf_blueColor() } static func secondaryTextColor() -> UIColor { return bf_grayColor() } … }
Color Paletteͷ࣮ extension UIColor { private static func bf_blueColor() ->
UIColor { return UIColor(red: 0.26, green: 0.52, blue: 0.75, alpha: 1.0) } private static func bf_lightBlueColor() -> UIColor { return UIColor(red: 0.16, green: 0.64, blue: 0.89, alpha: 1.0) } … static func primaryColor() -> UIColor { return bf_blueColor() } static func secondaryTextColor() -> UIColor { return bf_grayColor() } … } ΞϓϦͰ͏৭
Color Paletteͷ࣮ extension UIColor { private static func bf_blueColor() ->
UIColor { return UIColor(red: 0.26, green: 0.52, blue: 0.75, alpha: 1.0) } private static func bf_lightBlueColor() -> UIColor { return UIColor(red: 0.16, green: 0.64, blue: 0.89, alpha: 1.0) } … static func primaryColor() -> UIColor { return bf_blueColor() } static func secondaryTextColor() -> UIColor { return bf_grayColor() } … }
Color Paletteͷ࣮ extension UIColor { private static func bf_blueColor() ->
UIColor { return UIColor(red: 0.26, green: 0.52, blue: 0.75, alpha: 1.0) } private static func bf_lightBlueColor() -> UIColor { return UIColor(red: 0.16, green: 0.64, blue: 0.89, alpha: 1.0) } … static func primaryColor() -> UIColor { return bf_blueColor() } static func secondaryTextColor() -> UIColor { return bf_grayColor() } … } ίϯςΩετͷఆٛ
Text Stylesͷ࣮ class TextStyle { let styleName: String // ελΠϧ໊
var size: CGFloat // ϑΥϯταΠζ var color: UIColor // จࣈͷ৭ enum Weight { case thin, normal, bold } var weight: Weight // จࣈͷଠ͞ var textAlignment: NSTextAlignment // จࣈἧ͑ var kern: CGFloat // จࣈؒͷڑ enum FontType { case proportional, monospaced, compact, monospacedCompact } var fontType: FontType // ϑΥϯτͷछྨ ...
Text Stylesͷ࣮ extension TextStyle { static var body: TextStyle {
return TextStyle("body", size: 17, color: UIColor.primaryTextColor(), weight: .normal, textAlignment: .left, kern: 0.2) } ... }
Text Stylesͷ࣮ extension TextStyle { static var body: TextStyle {
return TextStyle("body", size: 17, color: UIColor.primaryTextColor(), weight: .normal, textAlignment: .left, kern: 0.2) } ... }
Text Stylesͷ࣮ extension TextStyle { static var body: TextStyle {
return TextStyle("body", size: 17, color: UIColor.primaryTextColor(), weight: .normal, textAlignment: .left, kern: 0.2) } ... }
Text Styleͷద༻ extension NSAttributedString { convenience init(string str: String, style:
TextStyle, tweak: (_ builder: TextStyle) -> Void) { tweak(style) self.init(string: str, attributes: style.build()) } } message.text = NSAttributedString(string: "Hello!", style: .body) name.text = NSAttributedString(string: "Taro", style: .body) { tweak in tweak.color = UIColor.secondaryTextColor() }
Text Styleͷద༻ extension NSAttributedString { convenience init(string str: String, style:
TextStyle, tweak: (_ builder: TextStyle) -> Void) { tweak(style) self.init(string: str, attributes: style.build()) } } message.text = NSAttributedString(string: "Hello!", style: .body) name.text = NSAttributedString(string: "Taro", style: .body) { tweak in tweak.color = UIColor.secondaryTextColor() }
Text Styleͷద༻ extension NSAttributedString { convenience init(string str: String, style:
TextStyle, tweak: (_ builder: TextStyle) -> Void) { tweak(style) self.init(string: str, attributes: style.build()) } } message.text = NSAttributedString(string: "Hello!", style: .body) name.text = NSAttributedString(string: "Taro", style: .body) { tweak in tweak.color = UIColor.secondaryTextColor() }
Text Styleͷద༻ extension NSAttributedString { convenience init(string str: String, style:
TextStyle, tweak: (_ builder: TextStyle) -> Void) { tweak(style) self.init(string: str, attributes: style.build()) } } message.text = NSAttributedString(string: "Hello!", style: .body) name.text = NSAttributedString(string: "Taro", style: .body) { tweak in tweak.color = UIColor.secondaryTextColor() } Attributes DictionaryΛੜ
Text Styleͷద༻ extension NSAttributedString { convenience init(string str: String, style:
TextStyle, tweak: (_ builder: TextStyle) -> Void) { tweak(style) self.init(string: str, attributes: style.build()) } } message.text = NSAttributedString(string: "Hello!", style: .body) name.text = NSAttributedString(string: "Taro", style: .body) { tweak in tweak.color = UIColor.secondaryTextColor() }
Text Styleͷద༻ extension NSAttributedString { convenience init(string str: String, style:
TextStyle, tweak: (_ builder: TextStyle) -> Void) { tweak(style) self.init(string: str, attributes: style.build()) } } message.text = NSAttributedString(string: "Hello!", style: .body) name.text = NSAttributedString(string: "Taro", style: .body) { tweak in tweak.color = UIColor.secondaryTextColor() } .bodyʹclosureͷΧελϚΠζΛద༻
Storyboard, Xibͷѻ͍ • SwiftGenͳͲΘͣɺఆ൪ͷprotocol४ڌͷΈ protocol StoryboardInitializable: class { static var
storyboardName: String { get } static func instantiateStoryboard(storyboardName: String?) -> Self } extension StoryboardInitializable where Self: UIViewController { static var storyboardName: String { return String(describing: self) } static func instantiateStoryboard() -> Self { ... } }
Localizable, Assets • LocalizedStringී௨ʹจࣈྻࢦఆ • ิ͕ޮ͔ͳ͍ͷඍົ • վमը໘Ҏ֎ͰࣄނΔϦεΫগͳ͘ࠔΒͳ͍ • Α͘ࠔΔͱͨ͠Β։ൃϑϩʔࣗମʹ͕͋Δ͍ٙ
ΞϓϩʔνΛৼΓฦͬͯ
ΞϓϩʔνΛৼΓฦͬͯ • ଟ͘ͷϥΠϒϥϦ՝ʹରͯ͠ΦʔόʔΩϧͩͬͨ • ࣮ࣗલͰ࣮ͯ͠େม͡Όͳ͔ͬͨέʔεଟ͍ • ߟ͑ൈ͚γϯϓϧɾϕετͳղ๏͕ݟ͔ͭΔ
XcodeΞοϓσʔτָ͕ʹ • ϥΠϒϥϦͷϝϯςঢ়گʹ։ൃ͕ࠨӈ͞Εʹ͘͘ͳͬͨ • Xcode10ରԠεϜʔζ
ඪ४SDKຊ࣭తͳઃܭʹٞΛूதͰ͖Δ
ϥΠϒϥϦ vs ಠ࣮ࣗ νʔϜ։ൃΛݟ͖͔͚ͬ͢ʹͳΔ • ϥΠϒϥϦΛ࠾༻ͯ͠ɺԿΛࢦ͔ͨͬͨ͠ͷ͔ • ෳࡶͳϥΠϒϥϦ͕ඞཁͳͷ༷ʹ͕ͳ͍͔ʁ • σάϨͷසൃศརϥΠϒϥϦͰղܾͰ͖Δͷ͔ʁ
ϥΠϒϥϦ࠾༻ͷצॴ • ͯࣗ͢લ࣮͢Δͷݱ࣮తͰͳ͍ • ͋ͱͰҾ͖͕͍͢͠Ϟϊඅ༻ରޮՌ͕ߴ͍ • ͕໌֬ͳAPIKitɾNukeɻRx͍ํ࣍ୈ • ৫ΤίγεςϜͷਐԽʹద༻͍ͨ͘͢͠͠
·ͱΊ • bitFlyerΞϓϦͱɺͦͷ։ൃελΠϧͷհ • ϥΠϒϥϦ࠾༻ͷҙͱɺগͳ͍ґଘͷϝϦοτ • ·ͩ·ͩ՝ࢁੵΈɺϥΠϒϥϦۙେվम!? ʘ"ຊ࣭"ʹϑΥʔΧε͍ͨ͠ΤϯδχΞɺͥͻฐࣾʹʂʗ