iosやandroidアプリ開発のgoodpractice

46
iOS/Androidアプリ開発の Good Practice 20157ゆめみ 森下 1

Upload: ken-morishita

Post on 12-Jan-2017

9.637 views

Category:

Technology


0 download

TRANSCRIPT

Page 1: iOSやAndroidアプリ開発のGoodPractice

iOS/Androidアプリ開発のGood Practice

2015年7月ゆめみ森下

1

Page 2: iOSやAndroidアプリ開発のGoodPractice

はじめに

•ゆめみ内でのiOS/Androidアプリ開発におけるPractice(実践・慣習)をまとめたものです

•書きかけなので随時更新していきます

2

Page 3: iOSやAndroidアプリ開発のGoodPractice

用語の整理基本的な単語や、ゆめみ内で使う特殊な単語の定義もあるので整理しておきます

3

Page 4: iOSやAndroidアプリ開発のGoodPractice

プラットフォーム別の用語とこの資料の表記

iOSのclass,  protocol Androidのclass,  interface この資料の表記

AppDelegate/UIApplication Application Application

-­‐ Activity Activity

UIViewController Fragment ViewController   (略してVC)

NSUserDefaults Preference Preference

4

Page 5: iOSやAndroidアプリ開発のGoodPractice

同期/非同期な呼び出し-­‐リターン

• 「同期的な呼び出し-­‐リターン」• 呼び出し終了時に戻り値が戻ってくるタイプ• 構造上同期的であることが強制されている

• 「非同期的な呼び出し-­‐リターン/通知」• callbackや通知などで制御が戻ってくるタイプ• その制御がいつ戻ってくるかは実はわからない

• 呼び出し終了前に呼ばれるケースも有り得ることには注意

5

Page 6: iOSやAndroidアプリ開発のGoodPractice

非同期の「Callback的」「通知的」の違い

6

Page 7: iOSやAndroidアプリ開発のGoodPractice

非同期の「Callback的」「通知的」の違い

7

Page 8: iOSやAndroidアプリ開発のGoodPractice

非同期の「Callback的」「通知的」の違い

• 「通知的」な方には、 add/remove  というような明示的な通知の「受け取り開始」「受け取り解除」がある

• 「callback的」な方では、「解除」は難しい

• ライフサイクルが短いInstanceは「通知」を使うのが基本になる• そうしないと、自分が無効な状態なのにcallbackを受け取ってしまう• 例)  Fragment-­‐>Model,  ViewController-­‐>Model

• ライフサイクルが必ず長いInstanceはCallbackでもOK• 例)Model  -­‐>  API,  Service  -­‐>  API,  Model  -­‐>  Data系

8

Page 9: iOSやAndroidアプリ開発のGoodPractice

必須Practice

9

Page 10: iOSやAndroidアプリ開発のGoodPractice

必須Practice

• Gitによるバージョン管理• Crashレポートツールの導入• Debug/Releaseや環境別のビルドの切換をコードを書き換えないで行うこと

10

Page 11: iOSやAndroidアプリ開発のGoodPractice

Gitによるバージョン管理

• ソースコードのバージョン管理にはGitを使っています• ブランチの切りやすさや、大規模でも高速である、など利点が大きい

• 基本ルール• 少なくともリリースしたプロダクトは過去の任意のバージョンを再現可能なこと• 自動生成されるものはCommitしない

• .gitignoreをちゃんと設定する• *.classやビルドされたバイナリ等

• ビルドに必要な画像はOK、動画は・・・仕方ないときもある• Androidだと R.javaなどもコミット不要

• Git-­‐Flow的なブランチ運用にする• master:   リリースしたソースコードのブランチ (リリースしたらTAGを打つこと)• develop:    次期リリースするソースコードが合流するブランチ• feature/*:  特定の機能開発ブランチ。コミット単位でなるべく意味を持たせること。

11

Page 12: iOSやAndroidアプリ開発のGoodPractice

Crashレポートツールの導入

• リリース後の不具合を早急に発見するために必ず導入しましょう

•ゆめみでは Crashlyticsを標準的に使っています• https://try.crashlytics.com/

12

Page 13: iOSやAndroidアプリ開発のGoodPractice

Debug/Releaseや環境別のビルドの切換をコードを書き換えないで行うこと

• 例えば、通信先host名,  API  Key,  Log出力などの切換です

■標準的な実現方法

• Android• Gradle+BuildConfig+(環境変数)

• iOS• #ifdef DEBUG + #defineなどのプリプロセッサ+定数定義• 場合によっては、 *.h  ファイルの動的な生成

13

Page 14: iOSやAndroidアプリ開発のGoodPractice

推奨Practice

14

Page 15: iOSやAndroidアプリ開発のGoodPractice

推奨Practice

•内部の基本アーキテクチャはMVPパターンに近いものにする

• アプリケーションのアーキテクチャは徐々に発展させていく

•理想の開発に占める労力の割合は「View 80%」「その他 20%」

•定数は「環境情報」「Configuration情報」「内部定数」に分ける

15

Page 16: iOSやAndroidアプリ開発のGoodPractice

ViewController

内部の基本アーキテクチャはMVPパターン

Application

ActivityViewView

EntityModel  Container(for  DI)

APIPreference

ViewController

Model

DB/NoSQL

プレゼンテーション層

ビジネス層

データ層

PresenterVCとPresenterはわけない場合もある

StateMachine

StateMachine

通知的IF

File/Cache

set

複数のVCに跨る時がある各種Container

(for  DI)

16

Page 17: iOSやAndroidアプリ開発のGoodPractice

MVPパターン?

• MVCパターンみたいなものだけど、ViewがModelを直接参照しないで、PresenterがModelの値をViewにセットする(ようなイメージ)• http://tech.recruit-­‐mp.co.jp/mobile/android-­‐architecture/

•普通はViewがModelを直接参照しないので、この形式といえばこの形式になっているはず

•大事なこと• 「Model」が存在すること• 「ViewController」は肥大化するので、Presenterの機能を分離する設計があるということを知っておくこと

17

Page 18: iOSやAndroidアプリ開発のGoodPractice

MVPパターン、と言っても全体構成としては色々あります

http://tech.recruit-­‐mp.co.jp/mobile/android-­‐architecture/より18

Page 19: iOSやAndroidアプリ開発のGoodPractice

Modelってなんだ?

• とりあえずこれを読んでみて欲しい• 「iOS/Androidアプリエンジニアが理解すべきModelの振る舞い」• http://www.slideshare.net/mokemokechicken/iosandroidmodel

• 「プレゼンテーション層」から見れば、• データのCRUDや処理を依頼できる「サーバ」みたいなレイヤー• 非同期のリターンは「通知」でもらう• Modelは非常に長命でContainerのようなDI機能からもらうイメージ

• データや状態は公開されているが、カプセル化・抽象化されている• 安全な操作、状態の矛盾を防げるようにする

19※実際の名前はどうでも良い

Page 20: iOSやAndroidアプリ開発のGoodPractice

具体例

• Presentation層のAPI  callback実装→  Model通知実装への書き換え• Android

• https://github.com/mokemokechicken/android_refactor_training/blob/master/doc/example_1.md

20

Page 21: iOSやAndroidアプリ開発のGoodPractice

アプリケーションのアーキテクチャは徐々に発展させていく

21

Page 22: iOSやAndroidアプリ開発のGoodPractice

アプリケーションのアーキテクチャは徐々に発展させていく• アプリ開発においては次のようなライフサイクルを持つことが多い• 最初はViewだけのMockを作る• 次第に通信部分と連携するようになる• 徐々に仕様変更(View遷移、通信周り、キャッシュ周り)や微調整を行う

22

Page 23: iOSやAndroidアプリ開発のGoodPractice

最初はViewだけのMockを作る

ViewController

Application

ActivityViewViewViewControllerプレゼンテーション層

+(ダミーデータ)+(なんちゃってロジック)

この時は、ビジネス層なんて要らない

コアな画面を1つ2つちゃんと作って、他はパワポの画像を貼っておく、とかで良い

標準パーツとか使わない方が

完成品とのイメージの差がなくてむしろ良い

機能ではなく、見た目のゴールを再現する

空いた時間でアーキテクチャの基本を試行錯誤しておくアーキテクチャのプロトタイプを内部で考える

23

Page 24: iOSやAndroidアプリ開発のGoodPractice

次第に通信部分と連携するようになる

ViewController

Application

ActivityViewViewViewControllerプレゼンテーション層

+(ダミーデータ)+(なんちゃってロジック)

API通信機能

データ共有機能 データ操作機能

このまま拡張してはダメ、絶対。ここで建て付けは整理すること。

☓ ☓

☓ビジネスロジック☓

が、ここで注意

24

Page 25: iOSやAndroidアプリ開発のGoodPractice

次第に通信部分と連携するようになる

ViewController

Application

ActivityViewView

Entity

Model  Container(for  DI)

API

ViewController

Model

プレゼンテーション層

ビジネス層

データ層

通知的IF

+(なんちゃってロジック)

API通信機能

データ共有・操作機能

+(ダミーデータ)

VCをなるべく軽くする

ダミーなどは残ることもある

機能単位にModelを作る

set

ビジネスロジック

25

Page 26: iOSやAndroidアプリ開発のGoodPractice

徐々に仕様変更や微調整を行う

• 最初に(自分たちが)描いた図に近づいていくことになる

• 「全部盛り」になったときのクラス構成をイメージして、開発者同士が共有しておく• そうすれば「もうここは次の設計ステージに移行した方がいいね」と一言で通じる• そうでないとなかなか議論がまとまらない

• この辺りからReviewしたコードのみMergeOK、などのルールにすると良いかもしれない• 最初からそうでも良いですが• 人員に余裕がないと辛いですが

26

Page 27: iOSやAndroidアプリ開発のGoodPractice

理想の開発に占める労力の割合

27

Page 28: iOSやAndroidアプリ開発のGoodPractice

ViewController

理想の開発に占める労力の割合

Application

ActivityViewView

EntityModel  Container(for  DI)

APIPreference

ViewController

Model

DB/NoSQL

プレゼンテーション層

ビジネス層

データ層

PresenterVCとPresenterはわけない場合もある

StateMachine

StateMachine

通知的IF

File/Cache

set

80%

20%

アピールポイントパターン化が難しいよく変更がある

自動化やパターン化容易決まれば変更は少ない

28

Page 29: iOSやAndroidアプリ開発のGoodPractice

• 危険信号• 簡単な遷移やUIの変更が難しい• WiFiだと快適に動くのに3G回線だとすぐ固まる、落ちる• 画面がちらちらする。レイアウトが乱れている

• よくある原因• 画面ベースで開発しているため、画面の遷移や構成要素が変わると対応できない• 開発時はWifiで無計画に作っているので、非同期の問題に気がつかない• 要件や仕様を最低限満たすことに集中していて画面を見ていない。システム都合で画面がおまけになっている

• 解決する3つの手順• 通信と表示制御を分離して整理する

• これによりシステム都合で画面が制限されることが少なくなる• 画面単位でファイルを作るのではなく、機能単位になるように切り口を変え、パーツに分解する

• 隠れフラグ(static変数類、ローカルストレージ、ViewのIDや状態)を排除して、状態管理をまとめる

29

Page 30: iOSやAndroidアプリ開発のGoodPractice

定数の管理

30

Page 31: iOSやAndroidアプリ開発のGoodPractice

定数は「環境情報」「Configuration情報」「内部定数」に分ける•環境情報:デプロイ先などで変わる値• dev1,  staging1,  staging2…,  production  などの環境• 通信先URLなどのEndpoint• ID/PASS,  SNSや外部サービスのAPI-­‐Key• 署名の鍵

• Configuration情報: Release/Debugなどで変わる値• Log Levelなどの値• Debug機能のOn/Offなど

•内部の定数:単純に内部で使っている定数

31

Page 32: iOSやAndroidアプリ開発のGoodPractice

環境情報

• 環境情報はビルドやデプロイのタイミングで注入できるのがベスト• 事前に数種類(dev用,  内部staging用,  staging用,  production用)定義しておいて、ビルド時に選択できるというのでも良い

• Android• Gradle+  buildConfigField (+  環境変数)などが便利

• iOS• config_dev.h,  config_production.hなどを事前に定義して、ifdefでimportを分ける• もしくは、ビルド時に動的に.hを生成する

32

Page 33: iOSやAndroidアプリ開発のGoodPractice

Configuration情報

•下記のような仕組みをまとめられるようなものを準備する

• Android• 例えば、BuildConfig.DEBUGによって異なる値が返るような何かを作る

• iOS• 例えば、#ifdef DEBUGなどで異なる値が定義・returnされる何かを作る

33

Page 34: iOSやAndroidアプリ開発のGoodPractice

内部定数

• Android• static  public  final  で定数にするか、 getterで提供する

• iOS• #define  などで定義する

34

Page 35: iOSやAndroidアプリ開発のGoodPractice

具体例

•環境情報・Configuration情報の書き方• Android:  https://github.com/mokemokechicken/android_refactor_training/blob/master/doc/example_2.md

35

Page 36: iOSやAndroidアプリ開発のGoodPractice

実装が難しいので注意する点

36

Page 37: iOSやAndroidアプリ開発のGoodPractice

アプリケーションの起動時の処理

37

アプリの起動シーケンスは複雑になりやすい。

「初回起動時(ID取得・PushToken取得)」「2回目起動時」「Homeから復帰」「Push通知やIntent/URL  Schemaからの起動」での分岐や、

初期データを入力しているか(誕生日とか)どうかで画面を分岐データがDLされているか分岐

などがあり得る。

これらが非同期での処理になるので、実装が少しむずかしい。

→  StateMachineを使うとかなり改善する→  気合で実装することもあるが、このロジックをActivityに持たせない方が良い

Page 38: iOSやAndroidアプリ開発のGoodPractice

ViewControllerの状態は複雑になりやすい

38

一つの画面(ViewController/Presenter)で多数の非同期イベント(通信、UIイベント、Observeイベント)を制御するので。

→  これもStateMachineを使うと簡潔になる

Page 39: iOSやAndroidアプリ開発のGoodPractice

アンチパターンよく見かけるものたち・・・

39

Page 40: iOSやAndroidアプリ開発のGoodPractice

「Application/Activity/Fragment」や「AppDelegate/ViewController」に他のObjectから参照されるような値・状態を持っている。

• static  publicな何かになることが多い• ViewControllerをsingletonにしてしまう人もいる

■アンチパターン: 「いつも便利Global変数」

■問題点

■解決方法

何がいつ更新するかが発散していくので、保守性が著しく下がる。厳密な状態管理が難しいので、不整合が起こりやすい。ViewControllerは singletonにしてはいけない!

「Model」を作るViewの状態を共有したいなら、状態を「Presenter」に管理させ、Presenterを共有させる

40

Page 41: iOSやAndroidアプリ開発のGoodPractice

Application,  BaseActivity,  BaseViewControllerがどんどん多機能になっていく。通信機能やデータ保存機能などが実装されている。

■アンチパターン: 「万能Baseクラス」「Frameworkクラス肥大化」

■問題点

■解決方法

ActivityやViewControllerは、そういう役割を持つべきではない。通信機能を利用するために、他のObjectから ViewControllerの参照が欲しくなり、依存関係が破綻していく

余分な機能をApplicationやVCに持たせない。他のレイヤーのClass(Model,  API,  DB)に実装する。

41

Page 42: iOSやAndroidアプリ開発のGoodPractice

1Methodが200行を超える。(2000行を超えるものを見たことがある・・・)

■アンチパターン: 「大作書きおろしメソッド」

■問題点

■解決方法

非常に可読性や保守性が下がる。単体テストをまず書くことができない。

設計を見なおして、分割する。

42

Page 43: iOSやAndroidアプリ開発のGoodPractice

同じ switch  (mode/type/state) {  case  ...  

} が至る所にできる

■アンチパターン: 「増殖switch」

■問題点

■解決方法

可読性、保守性がすごい速度で下がっていく。mode/type/stateの追加・変更が難しい。全てを把握しないとコードが読めなくなる。「いつも便利Global変数」と合わさると絶大な破壊力になる。

modeやtypeなら:  Object指向的な解法stateなら: StateMachineの導入

43

Page 44: iOSやAndroidアプリ開発のGoodPractice

ActivityがたくさんあるActivityが消えることを考慮していない

■ Androidアンチパターン

■問題点

■解決方法

不具合が起こりやすい。

画面のViewはFragmentで管理する。(画面毎にActivityを作らない)。端末のDeveloperModeでHomeボタンを押したらActivityが必ず消えるOptionをOnに設定して開発する。

44

Page 45: iOSやAndroidアプリ開発のGoodPractice

他:警告・チェックポイント

45

Page 46: iOSやAndroidアプリ開発のGoodPractice

警告・チェックポイント

• Viewの状態で、Presenterなどの処理がわかれている• If  (view.isEnable())  とか…

• ViewController,  Presenterから直接APIやPreferenceを参照している• 設計思想に反している

• GAタグなどで(遷移元、遷移先)を記録するのは大変である• 仕様を調整してもらって、GAタグ(表示画面)にしてもらうのが吉• そうでない場合は、かなり大きくその工数を見積もる必要がある※良い方法があると良いですが

46