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
96
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
280
スタートアップの急成長に寄り添うOn-Call体制構築とその変遷
horimislime
3
1.8k
サポート効率を上げるためのロギング環境構築
horimislime
7
3.9k
migrating-from-promise-to-reactive
horimislime
0
400
社内Swiftもくもく会成果発表
horimislime
0
140
Swift Optional Extension Tips
horimislime
1
1.6k
ios-internationalization
horimislime
2
8.9k
UI testing in XCode7
horimislime
3
800
UIテストをカジュアルに自動化 / UI Automation using Remote
horimislime
2
2.4k
Other Decks in Programming
See All in Programming
プロダクト志向なエンジニアがもう一歩先の価値を目指すために意識したこと
nealle
0
110
Team operations that are not burdened by SRE
kazatohiei
1
210
Create a website using Spatial Web
akkeylab
0
300
すべてのコンテキストを、 ユーザー価値に変える
applism118
2
820
Elixir で IoT 開発、 Nerves なら簡単にできる!?
pojiro
1
150
Webの外へ飛び出せ NativePHPが切り拓くPHPの未来
takuyakatsusa
2
370
第9回 情シス転職ミートアップ 株式会社IVRy(アイブリー)の紹介
ivry_presentationmaterials
1
240
datadog dash 2025 LLM observability for reliability and stability
ivry_presentationmaterials
0
110
Code as Context 〜 1にコードで 2にリンタ 34がなくて 5にルール? 〜
yodakeisuke
0
110
NPOでのDevinの活用
codeforeveryone
0
350
Cursor AI Agentと伴走する アプリケーションの高速リプレイス
daisuketakeda
1
130
関数型まつりレポート for JuliaTokai #22
antimon2
0
150
Featured
See All Featured
Building Better People: How to give real-time feedback that sticks.
wjessup
367
19k
Dealing with People You Can't Stand - Big Design 2015
cassininazir
367
26k
A better future with KSS
kneath
239
17k
Documentation Writing (for coders)
carmenintech
72
4.9k
We Have a Design System, Now What?
morganepeng
53
7.7k
Design and Strategy: How to Deal with People Who Don’t "Get" Design
morganepeng
130
19k
Making the Leap to Tech Lead
cromwellryan
134
9.3k
Side Projects
sachag
455
42k
Stop Working from a Prison Cell
hatefulcrawdad
270
20k
Designing for Performance
lara
609
69k
GitHub's CSS Performance
jonrohan
1031
460k
How to Create Impact in a Changing Tech Landscape [PerfNow 2023]
tammyeverts
53
2.8k
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ΞϓϦͱɺͦͷ։ൃελΠϧͷհ • ϥΠϒϥϦ࠾༻ͷҙͱɺগͳ͍ґଘͷϝϦοτ • ·ͩ·ͩ՝ࢁੵΈɺϥΠϒϥϦۙେվम!? ʘ"ຊ࣭"ʹϑΥʔΧε͍ͨ͠ΤϯδχΞɺͥͻฐࣾʹʂʗ