Slide 1

Slide 1 text

Minami Baba 1

Slide 2

Slide 2 text

2020年11月にリリースされました 2

Slide 3

Slide 3 text

※0→1開発とは(今回の話では)開発スタートからローンチまでを指します 今日話すこと 3

Slide 4

Slide 4 text

プロの による指導を で に受けられる 毎日・アプリで 毎週・ビデオ通話で 4

Slide 5

Slide 5 text

(delight-ventures.com) ・DeNA発のベンチャーキャピタル ・スタートアップへの投資 ・早期独立を目指した  ・常時5~10プロジェクトが進行中 5

Slide 6

Slide 6 text

開発開始から 正式版リリースまで 約7ヶ月間 6

Slide 7

Slide 7 text

トレーナーのみ なさん 開発にご協力 いただいてる エンジニアのみ なさん Delight Venturesの みなさん and more…! 7

Slide 8

Slide 8 text

今日話すこと 8

Slide 9

Slide 9 text

今日話すこと 9 様々な技術や開発手法をご紹介していき ますが、詳細な導入手順や実装等は割 愛させていただきます。 専門知識がなくても理解いただける 内容 になっています。

Slide 10

Slide 10 text

今日伝えたいこと 10

Slide 11

Slide 11 text

11

Slide 12

Slide 12 text

導入 12

Slide 13

Slide 13 text

13

Slide 14

Slide 14 text

・より良いものをリリースできる ・リリース後の改善サイクルに繋がる 14

Slide 15

Slide 15 text

・より良いものをリリースできる ・リリース後の改善サイクルに繋がる ・ユーザーインタビュー ・オンラインでのトレーナー指導を体験 ・開発段階でのこまめなデモ / 試用 15

Slide 16

Slide 16 text

16

Slide 17

Slide 17 text

17

Slide 18

Slide 18 text

・初期要件の実装に時間がかかってしまう ・機能改善案に対応することが難しい 18

Slide 19

Slide 19 text

・初期要件の実装に時間がかかってしまう ・機能改善案に対応することが難しい "プロダクト改善を支える技術"について バックエンド / フロントエンド の両面からご紹介 19

Slide 20

Slide 20 text

前編 20

Slide 21

Slide 21 text

※XaaSとは、情報処理に用いられるコンピューティング資源を、 インターネットを通じて提供しているサービスの総称。 (例:IaaS, mBaaS, SaaS) 21

Slide 22

Slide 22 text

データベース 画像ストレージ ユーザー認証 バックエンド処理 決済機能          データ暗号化 APIキー管理 分析データ収集 分析データ集計 分析データ可視化 22

Slide 23

Slide 23 text

時系列に沿って各サービスの 具体的な活用事例をご紹介 23

Slide 24

Slide 24 text

ベータ版を利用してもらい コア体験の満足度を検証したい 24

Slide 25

Slide 25 text

多くのプロダクトに必要な 日々の取り組みをトレーナーに報告 トレーナーへいつでも質問できる 25

Slide 26

Slide 26 text

多くのプロダクトに必要な 日々の取り組みをトレーナーに報告 トレーナーへいつでも質問できる 26

Slide 27

Slide 27 text

多くのプロダクトに必要な 日々の取り組みをトレーナーに報告 トレーナーへいつでも質問できる 27

Slide 28

Slide 28 text

多くのプロダクトに必要な 日々の取り組みをトレーナーに報告 トレーナーへいつでも質問できる 28

Slide 29

Slide 29 text

7月に納得できる状態で リリースするためには 1ヶ月以内にはざっくり動 くもの作りたいな... 実装が重そうなのは ユーザー認証・画像アッ プロード・双方向通信あ たりだな... 29

Slide 30

Slide 30 text

7月に納得できる状態で リリースするためには 1ヶ月以内にはざっくり動 くもの作りたいな... 実装が重そうなのは ユーザー認証・画像アッ プロード・双方向通信あ たりだな... 30

Slide 31

Slide 31 text

7月に納得できる状態で リリースするためには 1ヶ月以内にはざっくり動 くもの作りたいな... 実装が重そうなのは ユーザー認証・画像アッ プロード・双方向通信あ たりだな... 31

Slide 32

Slide 32 text

32 データベース https://firebase.google.com/docs/firestore 画像ストレージ https://firebase.google.com/docs/storage ユーザー認証 https://firebase.google.com/docs/auth

Slide 33

Slide 33 text

33 データベース https://firebase.google.com/docs/firestore 画像ストレージ https://firebase.google.com/docs/storage ユーザー認証 https://firebase.google.com/docs/auth

Slide 34

Slide 34 text

データの読み書き db.collection("users").document().setData(["name": "minami"]) db.collection("users").document("0123").getDocument {} 34 データベース https://firebase.google.com/docs/firestore 画像ストレージ https://firebase.google.com/docs/storage ユーザー認証 https://firebase.google.com/docs/auth

Slide 35

Slide 35 text

画像動画のやり取り データの読み書き db.collection("users").document().setData(["name": "minami"]) db.collection("users").document("0123").getDocument {} let riversRef = storage.reference().child("images/rivers.jpg") riversRef.putData(data, metadata: nil) {} 35 データベース https://firebase.google.com/docs/firestore 画像ストレージ https://firebase.google.com/docs/storage ユーザー認証 https://firebase.google.com/docs/auth

Slide 36

Slide 36 text

会員登録・ログイン 画像動画のやり取り データの読み書き db.collection("users").document().setData(["name": "minami"]) db.collection("users").document("0123").getDocument {} let riversRef = storage.reference().child("images/rivers.jpg") riversRef.putData(data, metadata: nil) {} Auth.auth().createUser(withEmail: email, password: password) {} Auth.auth().signIn(withEmail: email, password: password) {} 36 データベース https://firebase.google.com/docs/firestore 画像ストレージ https://firebase.google.com/docs/storage ユーザー認証 https://firebase.google.com/docs/auth

Slide 37

Slide 37 text

Cloud Firestore・Cloud Storage・Firebase Authentication 37

Slide 38

Slide 38 text

会員登録・ログイン 画像動画のやり取り データの読み書き ・環境構築にかかる工数を削減できる ・1つのプロジェクト内での実装に集中できる データベース 画像ストレージ ユーザー認証 38

Slide 39

Slide 39 text

・パスワード ・Google ・Facebook ・Apple ・Twitter ・会員登録 ・ログイン ・本人確認メール送信 ・メールアドレス再設定 ・パスワード再発行 ・必要な実装は提供されたAPIを呼び出すのみ 39

Slide 40

Slide 40 text

Cloud Firestore・Cloud Storage・Firebase Authentication 40

Slide 41

Slide 41 text

・悪意のあるデータ操作が行われないように  データ毎にアクセス権限を制御する必要がある ・NoSQLであるというだけでなく、”サブコレクション”  といった概念やクエリの特徴を踏まえた設計が必要 41 “自分が書いたレポートは その本人のみ読み込める ” “システム設定値は 管理者しか追加できない ” 例 例 何回も作っては壊して を繰り返しました...

Slide 42

Slide 42 text

・悪意のあるデータ操作が行われないように  データ毎にアクセス権限を制御する必要がある ・NoSQLであるというだけでなく、”サブコレクション”  といった概念やクエリの特徴を踏まえた設計が必要 何回も作っては壊して を繰り返しました... 42 “自分が書いたレポートは その本人のみ読み込める ” “システム設定値は 管理者しか追加できない ” 例 例

Slide 43

Slide 43 text

・悪意のあるデータ操作が行われないように  データ毎にアクセス権限を制御する必要がある ・NoSQLであるというだけでなく、”サブコレクション”  といった概念やクエリの特徴を踏まえた設計が必要 何回も作っては壊して を繰り返しました... 43 “自分が書いたレポートは その本人のみ読み込める ” “システム設定値は 管理者しか追加できない ” 例 例

Slide 44

Slide 44 text

Cloud Firestore・Cloud Storage・Firebase Authentication 44

Slide 45

Slide 45 text

45

Slide 46

Slide 46 text

月額課金プラン契約で指導が受けられる チケットを使ってオンラインレッスン 46

Slide 47

Slide 47 text

月額課金プラン契約で指導が受けられる チケットを使ってオンラインレッスン 47

Slide 48

Slide 48 text

月額課金プラン契約で指導が受けられる チケットを使ってオンラインレッスン 48

Slide 49

Slide 49 text

開発工数が大きくて 障害発生時のリスク が 高そうな機能ばかりだ ... 49 実装が重そうなのは 決済処理・サブスク管理 ・チケット管理あたりだ な...

Slide 50

Slide 50 text

開発工数が大きくて 障害発生時のリスク が 高そうな機能ばかりだ ... 50 実装が重そうなのは 決済処理・サブスク管理 ・チケット管理あたりだ な...

Slide 51

Slide 51 text

開発工数が大きくて 障害発生時のリスク が 高そうな機能ばかりだ ... 51 実装が重そうなのは 決済処理・サブスク管理 ・チケット管理あたりだ な...

Slide 52

Slide 52 text

バックエンド処理 https://firebase.google.com/docs/functions 決済機能 https://stripe.com/jp 52

Slide 53

Slide 53 text

バックエンド処理 https://firebase.google.com/docs/functions 決済機能 https://stripe.com/jp 53 HTTPS通信 データ 読み書き 決済処理 Stripe

Slide 54

Slide 54 text

バックエンド処理 https://firebase.google.com/docs/functions 決済機能 https://stripe.com/jp 54 HTTPS通信 データ 読み書き 決済処理 “テクノロジーファースト ” を掲げている点が 採用を決定した決め手! Stripe

Slide 55

Slide 55 text

Cloud Functions・Stripe 55

Slide 56

Slide 56 text

バックエンド処理 HTTPS通信 ・Stripeを始めとする外部サービスとの連携 ・データ整合性を安全に守ることができる  ・”レッスン予約と同時にチケットを使用済みに” データベース データ操作 サーバーレスで 任意処理を実行 56

Slide 57

Slide 57 text

バックエンド処理 HTTPS通信 ・Stripeを始めとする外部サービスとの連携 ・データ整合性を安全に守ることができる  ・”レッスン予約と同時にチケットを使用済みに” データベース データ操作 サーバーレスで 任意処理を実行 57

Slide 58

Slide 58 text

バックエンド処理 HTTPS通信 ・Stripeを始めとする外部サービスとの連携 ・データ整合性を安全に守ることができる  ・”レッスン予約と同時にチケットを使用済みに” データベース データ操作 サーバーレスで 任意処理を実行 58

Slide 59

Slide 59 text

・月毎に自動的に決済 ・複雑な料金プランに対応 ・有効期限や使用回数の制限 ・手動でもAPIからでも発行可能 59 Stripe

Slide 60

Slide 60 text

・月毎に自動的に決済 ・複雑な料金プランに対応 ・有効期限や使用回数の制限 ・手動でもAPIからでも発行可能 60 Stripe

Slide 61

Slide 61 text

・月毎に自動的に決済 ・複雑な料金プランに対応 ・有効期限や使用回数の制限 ・手動でもAPIからでも発行可能 61 Stripe

Slide 62

Slide 62 text

・月毎に自動的に決済 ・複雑な料金プランに対応 ・有効期限や使用回数の制限 ・手動でもAPIからでも発行可能 62 Stripe

Slide 63

Slide 63 text

63 ・多様な料金プランやクーポンによる割引は  有料会員を増やすために取り得る施策の一つ Stripe

Slide 64

Slide 64 text

64 Stripe

Slide 65

Slide 65 text

・発生した全ての処理をAPIベースで閲覧できる ・ほとんどの機能の操作をGUI上から行える 65 Stripe

Slide 66

Slide 66 text

・発生した全ての処理をAPIベースで閲覧できる ・ほとんどの機能の操作をGUI上から行える 66 ※開発環境のデータ ※開発環境のデータ Stripe

Slide 67

Slide 67 text

・発生した全ての処理をAPIベースで閲覧できる ・ほとんどの機能の操作をGUI上から行える 67 Stripe

Slide 68

Slide 68 text

・本番と同等機能をすぐに使い始められる ・様々なパターンを想定したテスト用カード番号を用意 68 Stripe

Slide 69

Slide 69 text

・本番と同等機能をすぐに使い始められる ・様々なパターンを想定したテスト用カード番号を用意 69 Stripe

Slide 70

Slide 70 text

・本番と同等機能をすぐに使い始められる ・様々なパターンを想定したテスト用カード番号を用意 70 https://stripe.com/docs/testing Stripe

Slide 71

Slide 71 text

・複雑な処理フローも非常に理解しやすい ・障害発生リスク軽減にも繋がる 71 Stripe

Slide 72

Slide 72 text

Cloud Functions 72

Slide 73

Slide 73 text

HTTPS通信 ・どんなデータが書き込まれているか全て把握するためには  フロントエンドとCloud Functionsの両方を追う必要がある ・チーム内での分業が進んでくると開発しにくくなる データベース データ読み書き バックエンド処理 データ読み書き 73

Slide 74

Slide 74 text

最近、開発チームにジョインしてくれたAさん Cloud Functions に関わる開発をお願いしました そのデータはフロントエンドから 直接Firestoreへ書き込んでいたのでした。 WITH Fitness エピソード 74

Slide 75

Slide 75 text

Stripe・Cloud Functions 75

Slide 76

Slide 76 text

76

Slide 77

Slide 77 text

お客様の名前を安全に管理したい Stripeへの権限を安全に管理したい リリース後の改善のために 77

Slide 78

Slide 78 text

リリースはもう目の前! 限 られた工数の中でもしっ かりと非機能要件を実現 していきたい... 78

Slide 79

Slide 79 text

79 データ暗号化 https://cloud.google.com/security-key-management 分析データ収集 https://firebase.google.com/docs/analytics APIキー管理 https://cloud.google.com/secret-manager 分析データ集計 https://cloud.google.com/bigquery/?hl=ja 分析データ可視化 https://datastudio.google.com/

Slide 80

Slide 80 text

await client.encrypt({ name: keyName, plaintext: text }) await client.decrypt({ name: keyName, ciphertext: text }) 80 データ暗号化 https://cloud.google.com/security-key-management 分析データ収集 https://firebase.google.com/docs/analytics APIキー管理 https://cloud.google.com/secret-manager 分析データ集計 https://cloud.google.com/bigquery/?hl=ja 分析データ可視化 https://datastudio.google.com/

Slide 81

Slide 81 text

await client.encrypt({ name: keyName, plaintext: text }) await client.decrypt({ name: keyName, ciphertext: text }) await client.accessSecretVersion({ name: keyName }) 81 データ暗号化 https://cloud.google.com/security-key-management 分析データ収集 https://firebase.google.com/docs/analytics APIキー管理 https://cloud.google.com/secret-manager 分析データ集計 https://cloud.google.com/bigquery/?hl=ja 分析データ可視化 https://datastudio.google.com/

Slide 82

Slide 82 text

分析基盤に関して詳しくは DeNA Advent Calendar 2020 24日目 をご覧ください await client.encrypt({ name: keyName, plaintext: text }) await client.decrypt({ name: keyName, ciphertext: text }) await client.accessSecretVersion({ name: keyName }) 82 データ暗号化 https://cloud.google.com/security-key-management 分析データ収集 https://firebase.google.com/docs/analytics APIキー管理 https://cloud.google.com/secret-manager 分析データ集計 https://cloud.google.com/bigquery/?hl=ja 分析データ可視化 https://datastudio.google.com/

Slide 83

Slide 83 text

83

Slide 84

Slide 84 text

 自前実装を始める前に”XaaS”の存在を思い出してみる  ”XaaS”を使いこなすためにもスキルが必要なので  「フロントエンドさえ分かればアプリが作れる」訳ではない 84

Slide 85

Slide 85 text

85

Slide 86

Slide 86 text

後編 86

Slide 87

Slide 87 text

後編 87 WITH Fitness iOSアプリでの 取り組みをご紹介します。 AndroidやWebにも応用できる 内容になっています。

Slide 88

Slide 88 text

88

Slide 89

Slide 89 text

全ての型の基礎となる 機能毎に実装を分割 身に付けた型を素早く生み出す         89 ❶ ❷ ❸

Slide 90

Slide 90 text

時系列に沿って"開発の型"の 具体的な導入事例をご紹介 90

Slide 91

Slide 91 text

91

Slide 92

Slide 92 text

急いでプロダクトを 作り上げないと! 設計に時間かける? すぐ手を動かす? 採用するなら どのアーキテクチャ? Firebase使うと フロントエンド責務が大 きくなりそう... 92

Slide 93

Slide 93 text

急いでプロダクトを 作り上げないと! 設計に時間かける? すぐ手を動かす? 採用するなら どのアーキテクチャ? Firebase使うと フロントエンド責務が大 きくなりそう... 93

Slide 94

Slide 94 text

急いでプロダクトを 作り上げないと! 設計に時間かける? すぐ手を動かす? 採用するなら どのアーキテクチャ? Firebase使うと フロントエンド責務が大 きくなりそう... 94

Slide 95

Slide 95 text

ビジネスロジック Interactor バックエンド通信 Repository 画面遷移 Router 表示ロジック Presenter UI実装 View VIPERを基にした アーキテクチャ 95

Slide 96

Slide 96 text

ビジネスロジック Interactor バックエンド通信 Repository 画面遷移 Router 表示ロジック Presenter UI実装 View 食事レポート 保存ボタン押さ れた VIPERを基にした アーキテクチャ 96

Slide 97

Slide 97 text

ビジネスロジック Interactor バックエンド通信 Repository 画面遷移 Router 表示ロジック Presenter UI実装 View 食事レポート 保存ボタン押さ れた VIPERを基にした アーキテクチャ 97

Slide 98

Slide 98 text

ビジネスロジック Interactor バックエンド通信 Repository 画面遷移 Router 表示ロジック Presenter UI実装 View 食事レポート 保存ボタン押さ れた ①ボタン押された VIPERを基にした アーキテクチャ 98

Slide 99

Slide 99 text

ビジネスロジック Interactor バックエンド通信 Repository 画面遷移 Router 表示ロジック Presenter UI実装 View 食事レポート 保存ボタン押さ れた ①ボタン押された ②保存処理せよ VIPERを基にした アーキテクチャ 99

Slide 100

Slide 100 text

ビジネスロジック Interactor バックエンド通信 Repository 画面遷移 Router 表示ロジック Presenter UI実装 View 食事レポート 保存ボタン押さ れた ①ボタン押された ②保存処理せよ ③データ作成せよ VIPERを基にした アーキテクチャ 100

Slide 101

Slide 101 text

責務が明確なアーキテクチャ 101

Slide 102

Slide 102 text

View ビジネスロジック Interactor 表示ロジック Presenter UI実装 View どの粒度でメ ソッド分割す る? これは何をし てる実装だっ け? どういう方針 で実装しよ う? 実装を分割 する粒度も明 確だな 通信関連の 処理はこの 辺りにある パターンに 沿って実装し ていこう 102

Slide 103

Slide 103 text

View ビジネスロジック Interactor 表示ロジック Presenter UI実装 View どの粒度でメ ソッド分割す る? これは何をし てる実装だっ け? どういう方針 で実装しよ う? 実装を分割 する粒度も明 確だな 通信関連の 処理はこの 辺りにある パターンに 沿って実装し ていこう 103

Slide 104

Slide 104 text

View ビジネスロジック Interactor 表示ロジック Presenter UI実装 View どの粒度でメ ソッド分割す る? これは何をし てる実装だっ け? どういう方針 で実装しよ う? 実装を分割 する粒度も明 確だな 通信関連の 処理はこの 辺りにある パターンに 沿って実装し ていこう 104

Slide 105

Slide 105 text

View ビジネスロジック Interactor 表示ロジック Presenter UI実装 View データベース に書き込ん で... 完了したら画 面遷移して... 保存ボタンが 押されたら... 送信処理を 呼び出す データベース 処理を呼び 出す 保存ボタンが 押された 105

Slide 106

Slide 106 text

View ビジネスロジック Interactor 表示ロジック Presenter UI実装 View データベース に書き込ん で... 完了したら画 面遷移して... 保存ボタンが 押されたら... 送信処理を 呼び出す データベース 処理を呼び 出す 保存ボタンが 押された 106

Slide 107

Slide 107 text

View ビジネスロジック Interactor 表示ロジック Presenter UI実装 View データベース に書き込ん で... 完了したら画 面遷移して... 保存ボタンが 押されたら... 送信処理を 呼び出す データベース 処理を呼び 出す 保存ボタンが 押された 107

Slide 108

Slide 108 text

責務が明確なアーキテクチャ 108

Slide 109

Slide 109 text

・曖昧なまま実装を進めていくことは迷いを生むことに繋がる ・開発が進むにつれて最適なルールが定まっていくはず ビジネスロジック Interactor UI実装 View Date型のデータを 文字列に変換するのは どこでやればいい...? 表示ロジック Presenter 109

Slide 110

Slide 110 text

責務が明確なアーキテクチャ 110

Slide 111

Slide 111 text

111

Slide 112

Slide 112 text

VIPER導入してるけど、 それでも各クラスの記述 量が増えてきたな... 一画面に色んな機能が あるから、どれがどの 実装か分かりづらくなっ てきたな... 112

Slide 113

Slide 113 text

VIPER導入してるけど、 それでも各クラスの記述 量が増えてきたな... 一画面に色んな機能が あるから、どれがどの 実装か分かりづらくなっ てきたな... 113

Slide 114

Slide 114 text

VIPER導入してるけど、 それでも各クラスの記述 量が増えてきたな... 一画面に色んな機能が あるから、どれがどの 実装か分かりづらくなっ てきたな... 114

Slide 115

Slide 115 text

View Presenter Interactor View Presenter Interactor UI実装から ビジネスロジックまで まるっと処理を切り出す 親コンポーネントは 子コンポーネントを 生成して配置するだけ 115

Slide 116

Slide 116 text

116

Slide 117

Slide 117 text

積極的なコンポーネント化 117

Slide 118

Slide 118 text

View Feature A View Feature B View Feature C Presenter Feature A Presenter Feature B Presenter Feature C Interactor Feature A Interactor Feature B Interactor Feature C これは何をし てる実装だっ け? どういう方針 で実装しよ う? View Feature A Feature B Feature C Presenter Feature A Feature B Feature C Interactor Feature A Feature B Feature C この機能のこ の処理はここ にある 実装を分割 する粒度が 明確だな 118

Slide 119

Slide 119 text

View Feature A View Feature B View Feature C Presenter Feature A Presenter Feature B Presenter Feature C Interactor Feature A Interactor Feature B Interactor Feature C これは何をし てる実装だっ け? どういう方針 で実装しよ う? View Feature A Feature B Feature C Presenter Feature A Feature B Feature C Interactor Feature A Feature B Feature C この機能のこ の処理はここ にある 実装を分割 する粒度が 明確だな 119

Slide 120

Slide 120 text

View Feature A View Feature B View Feature C Presenter Feature A Presenter Feature B Presenter Feature C Interactor Feature A Interactor Feature B Interactor Feature C これは何をし てる実装だっ け? どういう方針 で実装しよ う? View Feature A Feature B Feature C Presenter Feature A Feature B Feature C Interactor Feature A Feature B Feature C この機能のこ の処理はここ にある 実装を分割 する粒度が 明確だな 120

Slide 121

Slide 121 text

121

Slide 122

Slide 122 text

122

Slide 123

Slide 123 text

123

Slide 124

Slide 124 text

124

Slide 125

Slide 125 text

125

Slide 126

Slide 126 text

積極的なコンポーネント化 126

Slide 127

Slide 127 text

127

Slide 128

Slide 128 text

VIPERやコンポーネント 化は便利だけどファイル 生成量やテンプレコード 量が多くなってきたな... 開発の型が決まってき たおかげで似たような コードを何度も書いて いるな... 128

Slide 129

Slide 129 text

VIPERやコンポーネント 化は便利だけどファイル 生成量やテンプレコード 量が多くなってきたな... 開発の型が決まってき たおかげで似たような コードを何度も書いて いるな... 129

Slide 130

Slide 130 text

VIPERやコンポーネント 化は便利だけどファイル 生成量やテンプレコード 量が多くなってきたな... 開発の型が決まってき たおかげで似たような コードを何度も書いて いるな... 130

Slide 131

Slide 131 text

$ generamba gen ReportList viper $ generamba gen [コンポーネント名] [テンプレート名] generambaを利用 import UIKit import RxSwift class ReportListViewController: UIViewController { var presenter: ReportListPresenter! override func viewDidLoad() { super.viewDidLoad() } } View Presenter Interactor Router これはほんの一部で 共通化できそうな部分は 積極的にテンプレートに追加 用途に応じて テンプレートを 複数用意している 131

Slide 132

Slide 132 text

$ generamba gen ReportList viper $ generamba gen [コンポーネント名] [テンプレート名] generambaを利用 import UIKit import RxSwift class ReportListViewController: UIViewController { var presenter: ReportListPresenter! override func viewDidLoad() { super.viewDidLoad() } } View Presenter Interactor Router これはほんの一部で 共通化できそうな部分は 積極的にテンプレートに追加 用途に応じて テンプレートを 複数用意している 132

Slide 133

Slide 133 text

$ generamba gen ReportList viper $ generamba gen [コンポーネント名] [テンプレート名] generambaを利用 import UIKit import RxSwift class ReportListViewController: UIViewController { var presenter: ReportListPresenter! override func viewDidLoad() { super.viewDidLoad() } } View Presenter Interactor Router これはほんの一部で 共通化できそうな部分は 積極的にテンプレートに追加 用途に応じて テンプレートを 複数用意している 133

Slide 134

Slide 134 text

134 // // {{ module_info.file_name }} // {{ module_info.project_name }} // // Created by {{ developer.name }} on {{ date }}. // Copyright © {{ year }} {{ developer.company }}. All rights reserved. // import UIKit import RxSwift import RxCocoa class {{ module_info.name }}ViewController: UIViewController { var presenter: {{ module_info.name }}Presenter! let disposeBag = DisposeBag() public static func get() -> {{ module_info.name }}ViewController { return UIStoryboard(name: "{{ module_info.name }}", bundle: nil) .instantiateInitialViewController() as! {{ module_info.name }}ViewController } override func viewDidLoad() { super.viewDidLoad() setupButtonAction() bindToPresenter() observePresenter() } // private func setupCustomComponent() { // let view = presenter.getCustomComponentCreator().create() // customComponentContainerView.addSubview(view) // view.adjustParentConstraint(parent: customComponentContainerView) // } private func setupButtonAction() {} private func bindToPresenter() {} private func observePresenter() {} }

Slide 135

Slide 135 text

135 // // {{ module_info.file_name }} // {{ module_info.project_name }} // // Created by {{ developer.name }} on {{ date }}. // Copyright © {{ year }} {{ developer.company }}. All rights reserved. // import Foundation import RxSwift import RxCocoa class {{ module_info.name }}Presenter { private let router: {{ module_info.name }}Router private let interactor: {{ module_info.name }}Interactor init(router: {{ module_info.name }}Router, interactor: {{ module_info.name }}Interactor) { self.router = router self.interactor = interactor } // func getCustomComponentCreator() -> CustomComponentCreator { // return CustomComponentCreator() // } }

Slide 136

Slide 136 text

136 // // {{ module_info.file_name }} // {{ module_info.project_name }} // // Created by {{ developer.name }} on {{ date }}. // Copyright © {{ year }} {{ developer.company }}. All rights reserved. // import Foundation import RxSwift class {{ module_info.name }}Interactor { }

Slide 137

Slide 137 text

137 // // {{ module_info.file_name }} // {{ module_info.project_name }} // // Created by {{ developer.name }} on {{ date }}. // Copyright © {{ year }} {{ developer.company }}. All rights reserved. // import UIKit class {{ module_info.name }}Router: Router { static func assembleModules() -> UIViewController { let view = {{ module_info.name }}ViewController.get() let router = {{ module_info.name }}Router(viewController: view) let interactor = {{ module_info.name }}Interactor() let presenter = {{ module_info.name }}Presenter(router: router, interactor: interactor) view.presenter = presenter return view } }

Slide 138

Slide 138 text

自分たちに特化したコード自動生成 138

Slide 139

Slide 139 text

View Presenter Interactor View Presenter Interactor View Presenter Interactor Cell Cell ViewModel DataSource あとは残り 必要な部分を 穴埋めしていく だけ これまで一機能2 週間かかってたと ころが 平均3日に! コマンドを 1行打つだけで 7割は実装できて いる感覚 139

Slide 140

Slide 140 text

ユーザー視点に立つためにチームメンバーみんなで トレーナーによるオンライン指導を3週間体験してみることに でもリリース予定日まであと1ヶ月。 納得いく形でベータ版リリースを迎えることできた WITH Fitness エピソード 140

Slide 141

Slide 141 text

自分たち特化のコード自動生成 141

Slide 142

Slide 142 text

142

Slide 143

Slide 143 text

 自分のサービス、自分の開発スタイルに合った  ”開発の型”を作り上げていきましょう  安定するまでは一定時間がかかると思います。  3ヶ月以上開発する予定があるサービスであれば  最初1ヶ月は試行錯誤していても良いと思います。 143

Slide 144

Slide 144 text

144

Slide 145

Slide 145 text

バックエンド開発 フロントエンド開発 145

Slide 146

Slide 146 text

今日伝えたいこと 146

Slide 147

Slide 147 text

147

Slide 148

Slide 148 text

オンライン指導に興味を持った方! 無料体験レッスンでお待ちしております with-fit.com 148

Slide 149

Slide 149 text

プロダクト開発に興味を持った方! ご連絡お待ちしております with-fit.com 149

Slide 150

Slide 150 text

150

Slide 151

Slide 151 text

151