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
200
スタートアップの急成長に寄り添う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
sappoRo.R #12 初心者セッション
kosugitti
0
250
SwiftUIで単方向アーキテクチャを導入して得られた成果
takuyaosawa
0
270
お前もAI鬼にならないか?👹Bolt & Cursor & Supabase & Vercelで人間をやめるぞ、ジョジョー!👺
taishiyade
6
4k
Kubernetes History Inspector(KHI)を触ってみた
bells17
0
220
Honoのおもしろいミドルウェアをみてみよう
yusukebe
1
210
Domain-Driven Transformation
hschwentner
2
1.9k
Bedrock Agentsレスポンス解析によるAgentのOps
licux
3
840
Amazon S3 TablesとAmazon S3 Metadataを触ってみた / 20250201-jawsug-tochigi-s3tables-s3metadata
kasacchiful
0
160
法律の脱レガシーに学ぶフロントエンド刷新
oguemon
5
740
Open source software: how to live long and go far
gaelvaroquaux
0
630
Ruby on cygwin 2025-02
fd0
0
140
SRE、開発、QAが協業して挑んだリリースプロセス改革@SRE Kaigi 2025
nealle
3
4.3k
Featured
See All Featured
Designing for humans not robots
tammielis
250
25k
Java REST API Framework Comparison - PWX 2021
mraible
28
8.4k
Refactoring Trust on Your Teams (GOTO; Chicago 2020)
rmw
33
2.8k
The Success of Rails: Ensuring Growth for the Next 100 Years
eileencodes
44
7k
A Tale of Four Properties
chriscoyier
158
23k
Speed Design
sergeychernyshev
27
790
Easily Structure & Communicate Ideas using Wireframe
afnizarnur
193
16k
Into the Great Unknown - MozCon
thekraken
35
1.6k
Agile that works and the tools we love
rasmusluckow
328
21k
Site-Speed That Sticks
csswizardry
4
380
The Pragmatic Product Professional
lauravandoore
32
6.4k
The Cost Of JavaScript in 2023
addyosmani
47
7.3k
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ΞϓϦͱɺͦͷ։ൃελΠϧͷհ • ϥΠϒϥϦ࠾༻ͷҙͱɺগͳ͍ґଘͷϝϦοτ • ·ͩ·ͩ՝ࢁੵΈɺϥΠϒϥϦۙେվम!? ʘ"ຊ࣭"ʹϑΥʔΧε͍ͨ͠ΤϯδχΞɺͥͻฐࣾʹʂʗ