View
295
Download
4
Category
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.
共通のドキュメントについて
18Ⓒ 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