AndroidーiOS開発比較〜iOSエンジニアから見たAndroidのアレコレ〜

Preview:

Citation preview

Developers.IO 2016

2

田中 孝明 モバイルアプリサービス部

Ⓒ Classmethod, Inc.

2016年04月23日

AndroidーiOS開発比較~iOSエンジニアから見たAndroidのアレコレ~

1

自己紹介• 田中孝明 • http://dev.classmethod.jp/author/tanaka-takaaki/

• モバイルアプリサービス部所属 • iOS開発担当

2Ⓒ Classmethod, Inc.

愛知県に住んでいたことあります

3Ⓒ Classmethod, Inc.

弊社のよくある要件

• Android / iOS並行開発 • 同時リリース • iOS先行リリースという案件もあり • Appleの審査期間を踏まえてiOSを前倒しで開発するということもあり

4Ⓒ Classmethod, Inc.

弊社のよくある開発体制

5Ⓒ Classmethod, Inc.

Android / iOS開発者間でコードレビューを行うこともある

本題

6Ⓒ Classmethod, Inc.

モバイル開発において注意する点

• 対応OSバージョンについて • 共通のドキュメントについて • 実装方針について

7Ⓒ Classmethod, Inc.

モバイル開発において注意する点

• 対応OSバージョンについて • 共通のドキュメントについて • 実装方針について

8Ⓒ Classmethod, Inc.

対応OSバージョンについて• Android

9Ⓒ Classmethod, Inc.

http://developer.android.com/intl/ja/about/dashboards/index.html

対応OSバージョンについて• Android • 4.x系はシェアが20%程度あるのでサポートせざるを得ない状況が多い

• 4.4からはWebViewがChoromeベースに変更されているため、WebViewを利用する案件は注意が必要

10Ⓒ Classmethod, Inc.

対応OSバージョンについて• iOS

11Ⓒ Classmethod, Inc.

https://developer.apple.com/support/app-store/jp/

対応OSバージョンについて• iOS • Swiftで新規で作成する場合はiOS 8以上推奨 • Swift+CocoaPodsでEmbed frameworkが利用できるため

• ひとつ前のバージョン(iOS 8)対応で95%のシェアは確保できる

12Ⓒ Classmethod, Inc.

対応OSバージョンについて• iOS • Xcode のバージョンアップ対応には注意 • XcodeのバージョンによってサポートされるSwiftのバージョンが違う • Xcode 6.0 -> Swift 1.0 • Xcode 6.1 -> Swift 1.1 • Xcode 6.3 -> Swift 1.2 • Xcode 7.0 -> Swift 2.0 • Xcode 7.2 -> Swift 2.1 • Xcode 7.3 -> Swift 2.2

13Ⓒ Classmethod, Inc.

対応OSバージョンについて• Android • 対応OSは多い • WebView対応に注意 • BLE案件に注意

• iOS • Swiftのバージョンに注意 • ATS対応

14Ⓒ Classmethod, Inc.

モバイル開発において注意する点

• 対応OSバージョンについて • 共通のドキュメントについて • 実装方針について

15Ⓒ Classmethod, Inc.

共通のドキュメントについて

16Ⓒ Classmethod, Inc.

• お客様と開発側間でアプリの機能を実現するために必要なものを洗い出しておく

• Android / iOS開発者の間でも共通の認識を持てるドキュメントを作成しておく

共通のドキュメントについて

17Ⓒ Classmethod, Inc.

モバイル開発において注意する点

• 対応OSバージョンについて • 共通のドキュメントについて • 実装方針について

19Ⓒ Classmethod, Inc.

実装方針について• 開発環境、実装方針については初期段階で決定しておく

• 決定した内容はドキュメントとして残しておく • 開発環境→GitHubのREADMEに記載 • 実装方針→Wikiに記載

• 新たなチームメンバーが加わった際に一覧で確認できるドキュメントを整備しておく(Wiki等を活用) • API仕様や要件定義、設計の資料が一覧で確認できるようにしておく

20Ⓒ Classmethod, Inc.

実装方針の共有

21Ⓒ Classmethod, Inc.

パッケージ名

ui viewなどの画面表示に関わるクラスをまとめる

domainviewの表示に関わるロジックや、エンティティなど

のモデルクラスをまとめる

infra API通信、DBアクセス、データ保存をまとめる

• パッケージ分類の一例(※あくまで一例です)

それぞれの部品をどこに配置するか…?

22Ⓒ Classmethod, Inc.

ui

23Ⓒ Classmethod, Inc.

ui

24Ⓒ Classmethod, Inc.

domain

25Ⓒ Classmethod, Inc.

domain

26Ⓒ Classmethod, Inc.

infra

27Ⓒ Classmethod, Inc.

infra

28Ⓒ Classmethod, Inc.

実装方針について• 画面の作成方法に関してはOSに依存しているところが大きいので、レビューできるようになるまで時間がかかる

• プルリクエストには修正した画面のスクリーンショットを貼ってもらう

• Android • XMLファイルをゴリゴリ編集する

• iOS • StoryBoardを使う

29Ⓒ Classmethod, Inc.

実装方針について

30Ⓒ Classmethod, Inc.

実装方針について

31Ⓒ Classmethod, Inc.

実装方針について• 各部品の担当範囲を明確にすることでコードレビューの際に部品を理解するまでのコストを軽減する

• ロジック、モデルを分離することでAndroid / iOS開発者がコードレビューできる範囲を増やす

• 実装漏れを無くす

32Ⓒ Classmethod, Inc.

アレコレありますが 結局は自ら手を動かして実装して

いかないと身につかない

33Ⓒ Classmethod, Inc.

自ら手を動かす

34

Push通知を AndroidとiOSで 実装してみる

35

36

Push通知を実装してみる• Pushサービスの登録 • GCM(Google Cloud Messaging) • APNS(Apple Push Notification Service)

• Push通知の作成 • Pushメッセージを作成

• Push通知の受信 • OS毎に受信処理を行う

37Ⓒ Classmethod, Inc.

Push通知を実装してみる

38Ⓒ Classmethod, Inc.

https://aws.amazon.com/jp/sns/

Amazon Simple Notification Service (SNS)

Android側の作業

39Ⓒ Classmethod, Inc.

• GCMへの登録 • ライブラリのインポート • パーミッションの設定 • Push受信処理の実装 • Amazon Cognito IDの取得 • Amazon SNSへトークンの送信 • 受信時の処理の実装

iOS側の作業

40Ⓒ Classmethod, Inc.

• APNSへの登録 • ライブラリのインポート • Amazon Cognito IDの取得 • Amazon SNSへトークンの送信 • 受信時の処理

AWS側の作業

41Ⓒ Classmethod, Inc.

• Amazon Cognito で Identity pool を作成 • Amazon SNS で Google Android の Platformを作成

• Amazon SNS で Apple iOS Dev の Platformを作成

• Amazon SNS で Topicを作成

開発環境• Android • Android Studio 2.0 • Java

• iOS • Xcode 7.3 • CocoaPods or Carthage • Swift 2.2

42Ⓒ Classmethod, Inc.

Android側の作業

43Ⓒ Classmethod, Inc.

• GCMへの登録 • ライブラリのインポート • 設定ファイルのインポート • パーミッションの設定 • Push受信処理の実装 • Amazon Cognito IDの取得 • Amazon SNSへトークンの送信 • 受信時の処理の実装

GCMへの登録

44Ⓒ Classmethod, Inc.

https://developers.google.com/cloud-messaging/

GCMへの登録

45Ⓒ Classmethod, Inc.

• google-services.jsonをダウンロードする

GCMへの登録

46Ⓒ Classmethod, Inc.

• google-services.jsonをプロジェクトに追加する

ライブラリのインポート

47Ⓒ Classmethod, Inc.

… android { …} dependencies {

… compile 'com.amazonaws:aws-android-sdk-cognito:2.+' compile 'com.amazonaws:aws-android-sdk-sns:2.+'}

• build.gradleを編集 • AWSのSDKを指定する

ライブラリのインポート

48Ⓒ Classmethod, Inc.

apply plugin: 'com.google.gms.google-services'android { …} dependencies { … compile "com.google.android.gms:play-services:8.3.0" … }

• build.gradleを編集 • GCM用のライブラリを適用する

ライブラリのインポート

49Ⓒ Classmethod, Inc.

• Android Studioからライブラリをインポートする • 「Sync Project with Gradle Files」を実行する

パーミッションの設定

50Ⓒ Classmethod, Inc.

<?xml version="1.0" encoding="utf-8"?><manifest package="jp.co.kongmings.android.app" xmlns:android="http://schemas.android.com/apk/res/android"> <uses-permission android:name="android.permission.WAKE_LOCK" /> <uses-permission android:name="android.permission.INTERNET"/> <uses-permission android:name="com.google.android.c2dm.permission.RECEIVE" /> <uses-permission android:name="com.google.android.c2dm.permission.REGISTER" /> </manifest>

• AndroidManifest.xmlを編集 • パーミッションの設定

Push受信処理の実装

51Ⓒ Classmethod, Inc.

<application … <receiver android:name="com.google.android.gms.gcm.GcmReceiver" android:exported="true" android:permission="com.google.android.c2dm.permission.SEND" > <intent-filter> <action android:name="com.google.android.c2dm.intent.RECEIVE" /> <category android:name="jp.co.kongmings.android.app" /> </intent-filter> </receiver> <service android:name=".infra.service.MsgGcmListenerService" android:exported="false" > <intent-filter> <action android:name="com.google.android.c2dm.intent.RECEIVE" /> </intent-filter> </service> <service android:name=".infra.service.RegistrationIntentService" android:exported="false" /> <service android:name=".infra.service.MsgInstanceIDListenerService" android:exported="false"> <intent-filter> <action android:name="com.google.android.gms.iid.InstanceID"/> </intent-filter> </service> </application>

• AndroidManifest.xmlを編集

Push受信処理の実装

52Ⓒ Classmethod, Inc.

public class MsgGcmListenerService extends GcmListenerService { static final String TAG = MsgGcmListenerService.class.getSimpleName(); @Override public void onMessageReceived(String from, Bundle data) { String message = data.getString("message"); sendNotification(message); sendBroadcast(message); } void sendNotification(String message) { Intent intent = new Intent(this, MainActivity.class); intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP); PendingIntent pendingIntent = PendingIntent.getActivity(this, 0, intent, PendingIntent.FLAG_ONE_SHOT); Uri defaultSoundUri = RingtoneManager.getDefaultUri(RingtoneManager.TYPE_NOTIFICATION); NotificationCompat.Builder notificationBuilder = new NotificationCompat.Builder(this) .setSmallIcon(R.mipmap.ic_launcher) .setContentTitle("Message received!") .setContentText(message) .setAutoCancel(true) .setSound(defaultSoundUri) .setContentIntent(pendingIntent); NotificationManager manager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE); manager.notify(0, notificationBuilder.build()); } void sendBroadcast(String message) { Intent broadcastIntent = new Intent(); broadcastIntent.putExtra("message", message); broadcastIntent.setAction("MESSAGE_RECEIVED"); getBaseContext().sendBroadcast(broadcastIntent); }}

• GCMからメッセージが返ってきた時の処理

Push受信処理の実装

53Ⓒ Classmethod, Inc.

… void sendNotification(String message) { Intent intent = new Intent(this, MainActivity.class); intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP); PendingIntent pendingIntent = PendingIntent.getActivity(this, 0, intent, PendingIntent.FLAG_ONE_SHOT); Uri defaultSoundUri = RingtoneManager.getDefaultUri(RingtoneManager.TYPE_NOTIFICATION); NotificationCompat.Builder notificationBuilder = new NotificationCompat.Builder(this) .setSmallIcon(R.mipmap.ic_launcher) .setContentTitle("Message received!") .setContentText(message) .setAutoCancel(true) .setSound(defaultSoundUri) .setContentIntent(pendingIntent); NotificationManager manager = (NotificationManager)getSystemService(Context.NOTIFICATION_SERVICE); manager.notify(0, notificationBuilder.build()); } ...

• 通知センターに表示させる

Push受信処理の実装

54Ⓒ Classmethod, Inc.

public class MsgGcmListenerService extends GcmListenerService { … void sendBroadcast(String message) { Intent broadcastIntent = new Intent(); broadcastIntent.putExtra("message", message); broadcastIntent.setAction("MESSAGE_RECEIVED"); getBaseContext().sendBroadcast(broadcastIntent); }}

• メッセージを受信したことを通知する

Push受信処理の実装

55Ⓒ Classmethod, Inc.

public class RegistrationIntentService extends IntentService { public RegistrationIntentService() { super("RegistrationIntentService"); } @Override protected void onHandleIntent(Intent intent) { if (intent != null) { try { InstanceID instanceID = InstanceID.getInstance(this); String token = instanceID.getToken( getString(R.string.gcm_defaultSenderId), GoogleCloudMessaging.INSTANCE_ID_SCOPE, null); Prefs.put("token", token); } catch (IOException e) { e.printStackTrace(); } } }}

• トークン取得処理にてトークンを保持しておく

Amazon Cognito IDの取得

56Ⓒ Classmethod, Inc.

void getCognitoId() { new Thread(new Runnable() { @Override public void run() { CognitoCachingCredentialsProvider provider =          new CognitoCachingCredentialsProvider( self, COGNITO_IDENTITY_POOL_ID, AWS_REGION ); final String identityId = provider.getIdentityId(); Prefs.put("cognito_id", identityId); } }).start();}

• CognitoCachingCredentialsProviderにCognito Identity Pool IDを指定する

Amazon SNSへトークンの送信

57Ⓒ Classmethod, Inc.

void sendToken() { AmazonSNSClient snsClient = new AmazonSNSClient(mProvider); snsClient.setRegion(Region.getRegion(AWS_REGION)); CreatePlatformEndpointRequest createRequest = new CreatePlatformEndpointRequest(); createRequest.setPlatformApplicationArn(AWS_SNS_APPLICATION_ARN); String token = Prefs.get("token"); createRequest.setToken(token); CreatePlatformEndpointResult platformEndpoint = snsClient.createPlatformEndpoint(createRequest); String endpointArn = platformEndpoint.getEndpointArn(); snsClient.subscribe(AWS_SNS_SUBSCRIBE_TOPIC_ARN, "application", endpointArn);}

• 指定したTopicをSuscribeする

受信時の処理の実装

58Ⓒ Classmethod, Inc.

private MessageBroadcastReceiver mReceiver; public class MessageBroadcastReceiver extends BroadcastReceiver { @Override public void onReceive(Context context, Intent intent) { Bundle bundle = intent.getExtras(); String message = bundle.getString("message"); Toast.makeText(context, message, Toast.LENGTH_LONG).show(); }} @Overrideprotected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); … mReceiver = new MessageBroadcastReceiver(); IntentFilter intentFilter = new IntentFilter(); intentFilter.addAction("MESSAGE_RECEIVED"); registerReceiver(mReceiver, intentFilter);}

iOS側の作業

59Ⓒ Classmethod, Inc.

• APNSへの登録 • ライブラリのインポート • Amazon Cognito IDの取得 • Amazon SNSへトークンの送信 • 受信時の処理

APNSへの登録

60Ⓒ Classmethod, Inc.

https://developer.apple.com/jp/documentation/RemoteNotificationsPG.pdf

APNSへの登録

61Ⓒ Classmethod, Inc.

• キーチェーンアクセスを起動して認証局に証明書を要求する

APNSへの登録

62Ⓒ Classmethod, Inc.

APNSへの登録

63Ⓒ Classmethod, Inc.

APNSへの登録

64Ⓒ Classmethod, Inc.

https://developer.apple.com/library/ios/documentation/NetworkingInternet/Conceptual/RemoteNotificationsPG/

Chapters/ApplePushService.html

APNSへの登録

65Ⓒ Classmethod, Inc.

• Provisioning Profileが正しく指定されているか確認する

ライブラリのインポート

66Ⓒ Classmethod, Inc.

platform :ios, '8.0' use_frameworks!

target 'App' do pod 'AWSSNS' end

• CocoaPods経由でインポートする • Podfileを編集

ライブラリのインポート

67Ⓒ Classmethod, Inc.

• CocoaPods経由でインポートする • pod installコマンドを実行

➜ app git:(develop) ✗ bundle exec pod install

Amazon Cognito IDの取得

68Ⓒ Classmethod, Inc.

func application( application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool { let credentialsProvider = AWSCognitoCredentialsProvider( regionType: AWSRegionType.APNortheast1, identityPoolId: CognitoIdentityPoolId) let defaultServiceConfiguration = AWSServiceConfiguration( region: AWSRegionType.APNortheast1, credentialsProvider: credentialsProvider) AWSServiceManager.defaultServiceManager().defaultServiceConfiguration = defaultServiceConfiguration

self.registerForRemoteNotifications() return true }

• AWSServiceManagerの設定を実施 • CognitoIdentityPoolIdはAWSで作成したものを指定する

Amazon SNSへトークンの送信

69Ⓒ Classmethod, Inc.

func application( application: UIApplication, didRegisterForRemoteNotificationsWithDeviceToken deviceToken: NSData) { let deviceTokenString = "\(deviceToken)" .stringByTrimmingCharactersInSet(NSCharacterSet(charactersInString:"<>")) .stringByReplacingOccurrencesOfString(" ", withString: "") …

}

• トークン取得時に呼ばれるメソッドからトークンを取得する

Amazon SNSへトークンの送信

70Ⓒ Classmethod, Inc.

… let sns = AWSSNS.defaultSNS() let request = AWSSNSCreatePlatformEndpointInput() request.token = deviceTokenString request.platformApplicationArn = AWSSNSApplicationArn sns.createPlatformEndpoint(request).continueWithExecutor( AWSExecutor.mainThreadExecutor(), withBlock: { (task: AWSTask!) -> AnyObject! in if task.error != nil { print("Error: \(task.error)") } else { let createEndpointResponse = task.result as! AWSSNSCreateEndpointResponse let subscribeRequest = AWSSNSSubscribeInput() subscribeRequest.topicArn = self.AWSSNSSubscribeTopicArn subscribeRequest.endpoint = createEndpointResponse.endpointArn subscribeRequest.protocols = "Application" sns.subscribe(subscribeRequest) } return nil }) }

• 指定したTopicをSuscribeする

受信時の処理

71Ⓒ Classmethod, Inc.

func application( application: UIApplication, didReceiveRemoteNotification userInfo: [NSObject : AnyObject], fetchCompletionHandler completionHandler: (UIBackgroundFetchResult) -> Void) { if application.applicationState == .Active { if let aps = userInfo["aps"] { if let alert = aps["alert"] { let alertController = UIAlertController( title: nil, message: alert, preferredStyle: .Alert) alertController.addAction( UIAlertAction( title: "OK", style: .Default, handler: nil)) self.window?.rootViewController?.presentViewController( alertController, animated: true, completion: nil) } } } }

• Pushを受信した時の処理を記載する

AWS側の作業

72Ⓒ Classmethod, Inc.

• Amazon Cognito で Identity pool を作成 • Amazon SNS で Google Android の Platformを作成

• Amazon SNS で Apple iOS Dev の Platformを作成

• Amazon SNS で Topicを作成

Amazon Cognito で Identity pool を作成

73Ⓒ Classmethod, Inc.

http://aws.amazon.com/jp/cognito/

Amazon Cognito で Identity pool を作成

74Ⓒ Classmethod, Inc.

Amazon SNS で Google Android の Platformを作成

75Ⓒ Classmethod, Inc.

https://aws.amazon.com/jp/sns/

Amazon SNS で Apple iOS Dev の Platformを作成

76Ⓒ Classmethod, Inc.

https://aws.amazon.com/jp/sns/

Amazon SNS で Topicを作成

77Ⓒ Classmethod, Inc.

https://aws.amazon.com/jp/sns/

DEMO

78Ⓒ Classmethod, Inc.

79Ⓒ Classmethod, Inc.

80Ⓒ Classmethod, Inc.

まとめ

81Ⓒ Classmethod, Inc.

• 共通のドキュメントを用意する • ノイズを減らして開発者同士がコードレビューできる範囲を増やす

• 手を動かす

Developers.IO 2016

ご静聴ありがとうございました。 スライドは後日ブログで公開します。

82

A-1

Ⓒ Classmethod, Inc.

#cmdevio2016

Recommended