Upgrade to Pro — share decks privately, control downloads, hide ads and more …

How we build our app with minimum 3rd party dependencies

How we build our app with minimum 3rd party dependencies

bitFlyer Drink Meetup for iOSエンジニア 登壇資料
https://bitflyer.connpass.com/event/100661

503b1ca28b167b0d94a5992aad1a57b1?s=128

horimislime

October 04, 2018
Tweet

Transcript

  1. bitFlyerΞϓϦ͸ ͍͔ʹͯ͠ϥΠϒϥϦґଘΛ࠷খݶʹ ։ൃΛߦ͍ͬͯΔͷ͔  ໦  גࣜձࣾCJU'MZFS ງݟफҰ࿠

  2. ΞδΣϯμ • bitFlyer ͷΞϓϦͱ։ൃελΠϧͷ঺հ • ϥΠϒϥϦ࠾༻ͰؕΓ΍͍͢᠘ͱɺ
 bitFlyerͰͷΞϓϩʔν • ΞϓϩʔνΛৼΓฦͬͯ

  3. bitFlyer ͷΞϓϦͱ։ൃελΠϧͷ঺հ

  4. bitFlyer Wallet • ೔ຊ࠷େͷԾ૝௨՟औҾॴ  CJU'MZFSͷJ04޲͚ΞϓϦ • ೥͔Β։ൃ։࢝ • ίʔυϕʔε͸ສߦ௒

    *1 ௐࠪҕୗઌϚΫϩϛϧʢ2018 ೥ 2 ݄ɺΠϯλʔωοτௐࠪʮԾ૝௨՟ɾ҉߸௨՟औҾ
 αʔϏεʹؔ͢ΔΞϯέʔτʯʣɺBitcoin ೔ຊޠ৘ใαΠτௐ΂ɻ 2016 ೥ 4݄-2018 ೥ 4 ݄ɺࠃ಺औҾॴͷ૯݄ؒग़དྷߴʢݱ෺/ܾࠩۚࡁ/ઌ෺औҾΛؚΉʣ
  5. ͲͷΑ͏ʹ࡞͍ͬͯΔ͔ • ݱࡏ4ਓͰ։ൃத • ΞʔΩςΫνϟ͸جຊతʹMVC • 3rd partyϥΠϒϥϦʹཔΓ"͗ͣ͢"։ൃ͍ͯ͠Δ

  6. bitFlyerͰ࢖͍ͬͯΔϥΠϒϥϦ

  7. bitFlyerͰ࢖͍ͬͯΔϥΠϒϥϦ

  8. bitFlyerͰ࢖͍ͬͯΔϥΠϒϥϦ

  9. bitFlyerͰ࢖͍ͬͯΔϥΠϒϥϦ ͘͝Ұ෦Ͱ࢖༻ ࠷ۙ͸$PEBCMFʹ

  10. ຊ೔࿩͞ͳ͍͜ͱ • ඪ४SDKݪཧओٛతͳ࿩ɾOSSͷDisΓ • ϥΠϒϥϦʹͲΕ͘Β͍པΔ΂͖͔͸ঢ়گʹΑΔ • ੒௕ͷݟࠐΊΔαʔϏεɾҰఆͷ։ൃྗ͕͋ΔνʔϜ Ͱ͸ࣗલ࣮૷ͷํ͕ϝϦοτ͕ߴ͍

  11. ຊ೔࿩͢͜ͱ • ϥΠϒϥϦ͸ศར͕ͩɺΉ΍Έʹ࢖͏ͱ଍͔ͤʹ • Ͳ͜Ͱ᠘ʹؕΓ΍͍͢ͷ͔ʁ • ͦ͜ͰͲ͏࣮૷͍ͯ͠Δͷ͔ʁʹ͍ͭͯ঺հ͠·͢

  12. ϥΠϒϥϦ࠾༻ͰؕΓ΍͍͢᠘ͱ bitFlyerͰͷΞϓϩʔν

  13. ϥΠϒϥϦ࠾༻ͰؕΓ΍͍͢᠘ͱ bitFlyerͰͷΞϓϩʔν • UIKitͰϥΠϒϥϦΛ࢖͍ͨ͘ͳΔϙΠϯτ 1. UITableViewͷѻ͍ʹ͘͞ 2. ૒ํ޲όΠϯσΟϯά 3. Style΍ComponentͷऔΓѻ͍

    (Font, Color, IB) • Өڹൣғ͕ۃେԽ • ֶशίετɾϩοΫΠϯΛͲ͏ղܾ͢Δ͔ʁ
  14. 1. UITableViewͷѻ͍ʹ͘͞

  15. 1. UITableViewͷѻ͍ʹ͘͞ • ࣌୅͸એݴతUITableView / CollectionViewઓࠃ࣌୅ʁ • RxDataSources Λ࢝ΊɺiOSDCͰ΋࿩୊ʹ •

    ܾఆతͳఆ൪͸·ͩͳ͘ɺͦΕͧΕҰ௕Ұ୹ΞϦ • ѻ͍΍͢͞ͱҾ͖׵͑ʹύϥμΠϜ่յ͕ى͜Δ
  16. ബ͍ϑϨʔϜϫʔΫͷ੔උ • ͱ͸͍͑ૉͷUIKit͸ݫ͍͠ • ࣗલͰܰྔϑϨʔϜϫʔΫΛ࣮૷͍ͯ͠Δ • ࣮૷ίετ͸͔͔Δ͕ɺऔΓճ͠΍͘͢
 ඞཁʹԠ֦ͯ͡ு͠΍͍͢

  17. ബ͍ϑϨʔϜϫʔΫͷ࣮૷ 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() }
  18. ബ͍ϑϨʔϜϫʔΫͷ࣮૷ 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΁ͷڮ౉͠
  19. ബ͍ϑϨʔϜϫʔΫͷ࣮૷ 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΁ͷڮ౉͠
  20. ബ͍ϑϨʔϜϫʔΫͷ࣮૷ 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૬౰)
  21. ബ͍ϑϨʔϜϫʔΫͷྑ͞ • ࠷খݶͷ࣮૷ • ෆඞཁͳ֓೦͕ಋೖ͞Εͳ͍ • ཁ݅ɾ໰୊ҙࣝυϦϒϯͰਐԽͰ͖Δ

  22. 2. ૒ํ޲όΠϯσΟϯά

  23. 2. ૒ํ޲όΠϯσΟϯά • ೖྗΛ൐͏6*5BCMF7JFX • 5FYUϑΥʔϜɺબ୒ࢶεΠον • #JOEJOH͸ΧοίΑ͘ॻ͚Δ͕
 ࿩Λ΍΍͘͜͢͠Δ •

    ෳࡶͳؔ਺Λ࢖͍࢝ΊΔͱ
 σόοάࠔ೉ʹ
  24. TableViewCellͷ࠶ར༻Λ΍Ίͯγϯϓϧʹ • ΍Γ͍ͨͷ͸ೖྗʹԠͨ͡Πϕϯτॲཧɾೖྗ஋ࢀর • Cellͷ࠶ར༻Λ΍Ίͯ͠·͑͹ྑ͍ • ೖྗϑΥʔϜͱ͸༗ݶͳCellͰߏ੒͞Ε͍ͯΔ͸ͣ

  25. 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] } }
  26. 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] } }
  27. 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] } }
  28. 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ύʔπ͕͍࣋ͬͯΔσʔλΛݟʹ͍͘
  29. ࠶ར༻Λ΍ΊΔ͜ͱͷྑ͞ • γϯϓϧ • ૉ௚ͳ࣮૷ͳͷͰ୭͕ಡΜͰ΋෼͔Γ΍͍͢ • ొ৔ਓ෺Λݶఆ͢ΔͱϝϦοτ͕େ͖͍

  30. 3. Style΍ComponentͷऔΓѻ͍

  31. 3. Style΍
 ComponentͷऔΓѻ͍ • 5FYU $PMPSͳͲ޿ൣғʹӨڹ • /4"UUSJCVUFE4USJOH΍ 6*4UPSZCPBSE͕ΠϚΠν •

    ඪ४4%,͕ؤுͬͯ΄͍͠ • 044΋๲େͳ਺ ग़యWTPV[BBXFTPNFJPTUFYU
  32. Text, Color Styles • ΞϓϦશମʹίϯϙʔωϯτɾελΠϧΨΠυ੔උࡁ • NSAttributedStringͷextension initializerͰશςΩετʹ ৭΍ϑΥϯτ౳ͷελΠϧΛద༻

  33. 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() } … }
  34. 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() } … }
  35. 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() } … } ΞϓϦ಺Ͱ࢖͏৭
  36. 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() } … }
  37. 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() } … } ίϯςΩετͷఆٛ
  38. 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 // ϑΥϯτͷछྨ ...
  39. Text Stylesͷ࣮૷ extension TextStyle { static var body: TextStyle {

    return TextStyle("body", size: 17, color: UIColor.primaryTextColor(), weight: .normal, textAlignment: .left, kern: 0.2) } ... }
  40. Text Stylesͷ࣮૷ extension TextStyle { static var body: TextStyle {

    return TextStyle("body", size: 17, color: UIColor.primaryTextColor(), weight: .normal, textAlignment: .left, kern: 0.2) } ... }
  41. Text Stylesͷ࣮૷ extension TextStyle { static var body: TextStyle {

    return TextStyle("body", size: 17, color: UIColor.primaryTextColor(), weight: .normal, textAlignment: .left, kern: 0.2) } ... }
  42. 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() }
  43. 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() }
  44. 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() }
  45. 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Λੜ੒
  46. 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() }
  47. 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಺ͷΧελϚΠζΛద༻
  48. 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 { ... } }
  49. Localizable, Assets౳ • LocalizedString͸ී௨ʹจࣈྻࢦఆ • ิ׬͕ޮ͔ͳ͍ͷ͸ඍົ • վमը໘Ҏ֎Ͱ͸ࣄނΔϦεΫ΋গͳ͘ࠔΒͳ͍ • Α͘ࠔΔͱͨ͠Β։ൃϑϩʔࣗମʹ໰୊͕͋Δ͍ٙ

  50. ΞϓϩʔνΛৼΓฦͬͯ

  51. ΞϓϩʔνΛৼΓฦͬͯ • ଟ͘ͷϥΠϒϥϦ͸՝୊ʹରͯ͠ΦʔόʔΩϧͩͬͨ • ࣮͸ࣗલͰ࣮૷ͯ͠΋େม͡Όͳ͔ͬͨέʔε΋ଟ͍ • ߟ͑ൈ͚͹γϯϓϧɾϕετͳղ๏͕ݟ͔ͭΔ

  52. XcodeΞοϓσʔτָ͕ʹ • ϥΠϒϥϦͷϝϯςঢ়گʹ։ൃ͕ࠨӈ͞Εʹ͘͘ͳͬͨ • Xcode10ରԠ΋εϜʔζ

  53. ඪ४SDK΍ຊ࣭తͳઃܭʹٞ࿦ΛूதͰ͖Δ

  54. ϥΠϒϥϦ vs ಠ࣮ࣗ૷࿦͸ νʔϜ։ൃΛݟ௚͖͔͚ͬ͢ʹ΋ͳΔ • ϥΠϒϥϦΛ࠾༻ͯ͠ɺԿΛ໨ࢦ͔ͨͬͨ͠ͷ͔ • ෳࡶͳϥΠϒϥϦ͕ඞཁͳͷ͸࢓༷ʹ໰୊͕ͳ͍͔ʁ • σάϨͷසൃ͸ศརϥΠϒϥϦͰղܾͰ͖Δͷ͔ʁ

  55. ϥΠϒϥϦ࠾༻ͷצॴ • ͢΂ͯࣗલ࣮૷͢Δͷ͸ݱ࣮తͰͳ͍ • ͋ͱͰҾ͖͸͕͠΍͍͢Ϟϊ͸අ༻ରޮՌ͕ߴ͍ • ੹຿͕໌֬ͳAPIKitɾNukeɻRx΋࢖͍ํ࣍ୈ • ૊৫΍ΤίγεςϜͷਐԽʹద༻͠΍͍ͨ͘͢͠

  56. ·ͱΊ • bitFlyerΞϓϦͱɺͦͷ։ൃελΠϧͷ঺հ • ϥΠϒϥϦ࠾༻ͷ஫ҙ఺ͱɺগͳ͍ґଘͷϝϦοτ • ·ͩ·ͩ՝୊ࢁੵΈɺ಺੡ϥΠϒϥϦ΋ۙ೔େվम!? ʘ"ຊ࣭"ʹϑΥʔΧε͍ͨ͠ΤϯδχΞɺͥͻฐࣾʹʂʗ