やらなイカ?

たぶん、iOS/Androidアプリの開発・テスト関係。

Google Cloud Messaging (GCM) の新機能 Topic Messagingを試したメモ

Google I/O 2015で発表された Google Cloud Messaging(以下GCM)の新機能、Topic Messagingを試してみました。

環境、前提条件などは、先の記事を参照してください。

nowsprinting.hatenablog.com

Topic Messaging とは

Topic Messagingとは、従来のGCMおよびApple Push Notification Service(以下APNs)のような送信先デバイスのトークンを指定してのPush通知ではなく、Publish/subscribeモデルのPush送信を行なう機能です。

Publish/subscribeモデルとは、各デバイスから購読(subscribe)したいトピックをあらかじめ登録することで、メッセージ送信(publish)側では個々のデバイスを意識することなく、トピックに対してメッセージを送ることで購読デバイスすべてにメッセージを配信できるものです。

これによって、例えば、

  • ニュースの更新などをPush通知したいだけであれば、サーバアプリケーションでは受信デバイスのRegistration Tokenを管理する必要がない
  • 送信機能もクライアントアプリに持たせる場合、サーバアプリケーション自体を立てる必要がない

といった構成が実現できるようになります。

制約・制限

GCMのTopic Messagingには、以下の制約・制限があります。

  • トピックのキーは、/topics/[a-zA-Z0-9-_.~%]+の書式
  • アプリあたり*1の購読数は100万件まで
  • トピックに送信できるメッセージのペイロードは2KBまで

購読数については、ユーザが1トピックづつ購読して100万ユーザまで、平均2トピックなら50万ユーザまで…となるので、インストール数が見込まれるアプリでは注意が必要です。

トピックの購読

以下、前回同様 Try Google Cloud Messaging for iOS のサンプルを使用して試していきます。

トピックの購読は、次のメソッドで行ないます。(メインスレッドから呼ぶ必要があります)

[GCMPubSub subscribeWithToken:topic:options:handler:]

サンプルのGcmExampleでは、起動時にトピック/topics/globalを購読するようになっています。

トピックへのメッセージの送信

続いて、同じく"Get started"のサンプルプロジェクトにあるGcmServerDemoを起動すると次のウィンドウが表示されます。

f:id:nowsprinting:20150607043716p:plain

apiKey欄にGCMのAPI Keyを入力し、"Send to topic"ボタンをクリックすると、送信メッセージの(本来、宛先のRegistrationTokenを設定する)toに、/topics/globalを設定して送信が行われます。

具体的には、以下のメッセージが送られます。

[
    "to": "/topics/global",
    "notification": [
        "body": "Hello from GCM"
    ]
]

これだけで、トピック/topics/globalを購読しているデバイスすべてにPush通知が送信されます。

トピックメッセージの受信

受信は通常のPush通知と同様です。受信側でトピックメッセージ固有の処理を行いたい場合は、 [UIApplicationDelegate application:didReceiveRemoteNotification:]メソッドで以下のように判定できます。

-(void)application:(UIApplication)application
        didReceiveRemoteNotification:(NSDictionary)notification {
    if ([notification[@"from"] hasPrefix:@"/topics/"]) {
        // トピックメッセージを受信した
    } else {
        // 通常のメッセージを受信した
    }
}

なお、今のところ、購読されているトピックのリストを取得するようなインタフェースは、サーバ側にもクライアント側にも定義されていないようです。

その他、エラーハンドリングなど詳細は公式のガイドを参照してください。

Implementing Topic Messaging

App nameとBundle ID, Androidアプリとの関連

GCMの利用登録を行なうページでは、"App name"と"iOS Bundle ID"を入力します。ここで、"App name"は、Google Developer Console(API Console)の"Project name"となります。

f:id:nowsprinting:20150615112845p:plain

これに対し、"iOS Bundle ID"は1:nの関係で登録することが可能です。 同様にAndroidアプリ場合、Bundle IDの代わりに"Android package name"欄にパッケージ名(ApplicationID)を登録しますが、これも1:nの関係になります。

トピックメッセージを送信するためのAPI Key、およびSender IDは、"App name"単位に割り当てられます。これに、複数の"iOS Bundle ID", "Android package name"を紐付けられますので、iOS版とAndroid版、また、Lite版やOEMしたアプリなどに対し、一括してトピックメッセージを送信することができます。

なお、iOSアプリ向け設定ファイルであるGoogleService-Info.plistにはSender IDしか書かれていませんので、複数のビルドターゲットで共有することができます。

Androidアプリ向けのgoogle-services.jsonには、Sender IDのほかに登録されたパッケージ名が書かれますが、登録されたパッケージ名が全て配列で書かれるようです。 そのため、フレーバーを定義して複数のアプリをビルドする場合でも、ひとつのgoogle-services.jsonを共有して問題ありませんでした。

*1:おそらく、Google APIs ConsoleのProject単位のこと

#TwitterFlock でFabricの話を聞いてきたメモ

Twitter社主催のアプリ開発者向けイベント"Twitter Flock Tokyo"に行ってきました。このイベントはTwitter社が提供しているFabricについてのもので、各都市で開催されています。

f:id:nowsprinting:20150520075937j:plain

Fabricではいくつかのサービスが提供されています。これまで、ベータ版配布サービス"Beta"、クラッシュログ解析サービス"Crashlytics"については導入事例も聞いており、興味を持っていました。

しかしそれだけでなく、ユーザ認証、Twitter連携、広告までひとまとめのSDKで提供されています。むしろ逆に、『広告SDKに、"Beta"、"Crashlytics"、ユーザ認証などの機能が無償でついてくる』くらいの印象を持ちました。

それぞれ(有償の)競合サービスが出ている中、これら機能を無償で提供して広告でマネタイズするということなので、頭ひとつ抜けたというか、もう別格と考えて良さそうです。

以下、セッションの内容のメモ。

Keynote

はじめに、サービスの全体像、Fabricの機能を一通り紹介。

実装サンプル"Cannonball"は、iOS/AndroidそれぞれGitHubにプロジェクトが公開されている。iOS版はSwiftで書かれている。

github.com

github.com

iOS/Androidとも、SDKの組み込みは非常に簡単。

日本企業の導入事例として、Cyber Agentさん、cameranさん、OneNewsさんの紹介。

Twitter主催のスタートアップコンペ"Hatch"の紹介。

DIGITS

  • DIGITSは、電話番号認証。Fabricの"Twitter Kit"に含まれる。
  • モバイルを前提としたサービスでは、電話番号認証はとても有効
  • これまでも類似サービスは存在したが、以下の理由で難しく、サービス利用も高額だった
    • SMS APIは国・キャリアによって異なる
    • 電話番号からキャリアを判断する必要もある
  • Twitterでは独自にこの機能を構築しており、9年の実績がある。DIGITSはこれを一般の開発者に無償で提供するもの。
  • 認証用ボタン、認証画面はSDKで提供。コールバックを書くだけで使える
  • 認証画面のbackgroundColor, accentColorなどカスタマイズ可能
  • userID, authToken, phoneNumberを取得可能
  • SMSメッセージはローカライズされる
  • AndroidではINTERNETパーミッションが必要
  • Androidで以下のパーミッションを許可すれば、1タップでログインも可能
    • READ_PHONE_STATE
    • RECEIVE_SMS
  • iOS/Androidネイティブアプリだけでなく、html5でも利用可能
  • digits.comで、電話番号変更やTwitterアカウントとの統合などを行える
  • 電話帳マッピングで友達のアカウントを探す

Crashlytics Kit

  • stability, testing, analyticsにまつわるサービスが統合されている
  • デバイス、OSバージョン、3G/WiFi、メモリなどの情報も収集
  • ユーザセションに属性をセットすることで、セグメント分析も可能
  • answersは、リアルタイムの分析エンジン。セットアップするだけでいくつかの指標で分析できる
    • ダッシュボードで見るほか、Emailでレポート
    • stability alert。安定度がしきい値を割るとアラートメールを送ってくれる
    • カスタムイベント(beta)
    • アプリのバージョンアップ当日、以前のバージョンとのクラッシュ率などを比較

Twitter Kit

  • "Fablic.with(TwitterKit)"で利用可能。様々なモジュールがあるので、"with"で宣言。"start"なども検討したけど"with"に。
  • Twitterアカウントによるシングルサインオン
  • シェア
    • TweetComposerクラスにチェインで指定。text().url().image().show()
    • 埋め込みツイート。リアルタイム、コンテンツの補完
    • TWTRTweetViewで表示もViewレベルで提供。サイズ、テーマなど設定が可能
    • TweetViewDelegete
      • willShareTweet
      • didSelectTweet
      • etc..
  • ゲスト認証(limitはある)
  • REST API Client
  • 使いかたはCannonballを参照

広告

  • オーガニック
    • Twitter Card。いくつか種類がある中に、App card。
    • アプリのページにmeta tagで書く
  • deep link
    • アプリのURLスキーム起動、Twitterカードからインストール通知を受けられる
    • カード表示のとき、URLスキームを見て「ストアで開く」か「アプリ起動」ボタンになる
  • full deep link
    • URLスキームに続けてパラメタを渡す
  • 分析
    • analytics.twitter.com
    • アプリのコンバージョン測定
    • Twitter card経由のインストールも計測できる
    • cardのインプレッション、アクティビティ
    • インフルエンサーと人気コンテンツを計測(インプレッション、インストール)。インフルエンサーに直接コンタクトできるのがTwitterの強み
    • CrashlyticsのアカウントとTwitterアカウントを連携することで、cardの計測が可能
  • プロモーション(paid)
    • セグメントを絞って広告を出せる。id指定もできるので、既存ユーザのみ、既存ユーザを除く、といった指定も可能。
    • ペイド:オーガニックには相関関係がある。ペイドの3.9〜5.2倍のオーガニック(インプレッションやクリック)が得られる

MoPub

  • 広告SDK
  • AdMob, iAdなど、他の広告ネットワークにも接続できる
  • 表示される広告は、MoPubと広告ネットワーク混在でオークションされ、高値の広告を表示できる

所感

評判の良い"Beta"、"Crashlytics"に、Analyticsの"Answers"もセットになってるなら使ってみよう、くらいに思っていましたが、他の機能も強力。

MoPubも筋が良さそうな印象で、広告SDKに対する締め付けも色々出てきている中、単に「セットだから」でなく、これ単体でも十分検討に値するのではないかと。

また、Twitter連携、DIGITSは、MBaaS各社とも競合しそうですね。この先もしPush通知などをサポートしてくると、MBaaSでなくFabricだけで足りるというアプリも出てきそう。 (Twitter社としては、Push相当のことをTwitterでやらせたいような気もするので、実現性は低いかも)

第1回 #DroidKaigi に行ってきました

AndroidエンジニアのAndroidエンジニアによるAndroidエンジニアのためのカンファレンス、第1回*1 DroiKaigiに参加してきました。

f:id:nowsprinting:20150427003737j:plain

発表資料は公開されていますので、簡単なメモと感想だけ残します。

droidkaigi.github.io

基調講演(@yanzmさん)

  • マッチョなActivity
    • Android 2.2までは個人開発者の時代
    • Android 4.3まで、アプリベンチャーの時代
    • Android 5.0はインフラ化の時代。Material Designはアニメーションに本気で取り組むと大変
    • Activityにすべて書いていたら破綻する
    • Activityはテストしにくい
  • 分割先の選択肢として、FragmentとCustomView
    • FragmentはActivity寄り
    • CustomViewについては『Android Pattern Cookbook』の第6章参照
  • Activityがマッチョになる要因と対策
    • 画面回転のときの値保持は、CustomViewのonSave/RestoreInstanceState()に実装する(Activityに書かない)
    • データとViewのマッピングもCustomView内に実装する
    • バリデーションも同様
  • Fragmentではまらないために
    • FragmentへのコールバックはsetTargetFragment()を使う
    • バックスタックは難しい
    • FragmentからstartActivity()ははまる。startActivityForResult()はもっと危険。
    • Fragment in Fragment、Loaderとの組み合わせは危険。startActivityForResult()も危険。

Fragmentは手を出すのが遅かったので未だに恐る恐る使っていますが、このセッションを聴いて恐怖++と同時に、このままFragmentを使っていくという方向は腹をくくっていいのかな、と。

データとViewのマッピングやバリデーションは、私はViewModel的なクラスに実装することが多いかも。分量次第ですが。

参考書籍。『Master of Fragment』は、ベータ版を脱するための加筆候補がたくさんあるそうです。

tatsu-zine.com

CardboardのUXをカメラで向上する(@ken1_takaさん / Room B)

  • Oculus Riftはケーブルとか面倒
  • Cardboardはお手軽。でもタッチパネルが使えないので操作に難あり
  • Cardboardにはカメラ穴があいてるので利用したい
  • Cardboard SDK: 複眼ビュー、樽ゆがみ、マグネットボタン
  • OpenCVでカメラ画像を認識させて、ジェスチャー操作させる

デモアプリが(高速化に失敗して)動かなくなったとのことで残念。OpenCVで指の形状・ジェスチャーを認識できるデモは見ることができました。

なお、OpenCVで頑張るほか、Leap MotionAndroid SDK(今はアルファ版)を待つという選択肢もありますね。

www.leapmotion.com

あるゲームアプリケーションの構成とアップデートサイクル(@iizukakさん / Room B)

  • KLabさんの某音ゲーAndroid 2.3以降対応、2〜3ヶ月に一度アップデートしている
  • ビルド〜デプロイプロセスを「パイプライン」と呼んでいる。GitHub, Jenkins, API, アセット類はAmazon S3にデプロイ
  • 50MBが非Wi-Fiでのapkサイズ限界。ゲームでは足りないので、リソースは追加ダウンロード
  • アップデート、Google Play(apk)は2〜3ヶ月ごとに、追加DLで済むものは数週間ごとに実施。
  • 開発後半は、apkはデイリーで作成してテストエンジニアに配る
    • リリース向けのDL版でなく、アセットを全部バンドルした「フル版」数百M〜数GBのapkを使う
    • 開発終盤はDL版でテスト
    • 実装者は思い込みや見落としがあるので、テストエンジニア重要
  • クラッシュレポート、Developer Consoleはあまり参考にならないので、ほかのサービスを使う
    • 遅延なく監視できるのか、NDK部分を分析できるか
    • Crashlytics, SmartBeat

ビルド〜デプロイプロセスの自動化は、テスト自動化を伴わないとしても開発効率を上げるものなので、KLabさんのように専業のパイプラインエンジニア(ビルド職人)がいなくても、ちょっと時間を取って整備するといいですね。

内部テスト向けの配布はDL版でなくフル版を配る、というのは良さそう。ぜひ真似したい。

紹介されていた書籍。ビルド職人の端くれ(兼業)として、ポチりました。

ゲーム・映像制作パイプライン構築マニュアル

ゲーム・映像制作パイプライン構築マニュアル

ソフトウェア一般としてはこちらもオススメ。

継続的デリバリー 信頼できるソフトウェアリリースのためのビルド・テスト・デプロイメントの自動化

継続的デリバリー 信頼できるソフトウェアリリースのためのビルド・テスト・デプロイメントの自動化

アプリの企画、プロトタイプからリリースに至るまで(@__chocomelonさん / Room A)

  • pixivマンガ。少人数のチーム、ファーストリリースまで3ヶ月
  • 以下の順で開発
    1. ストアの紹介文をメンバー全員で持ち寄って大喜利。アプリの売りを明確化
    2. 雑なプロトタイプ。機能ごとに、雑に。メンバーが物理的に近いことが大事。
    3. イメージ共有後、デザイナーがSketchでライブデザイン
    4. アプリのプロトタイプを作る
      • これもコードは捨てる前提。言語、ライブラリなどを試す。
      • DeployGateで社内で使ってもらう
      • バグの印象がユーザに残ってしまうので、雑過ぎなのもよくない
      • 毎日やっていると、バグフィックスなどに追われて本来のプロトタイプとして機能しなかったので、3日サイクルくらいに。
      • ふりかえり
    5. コーディング。プロトは捨てる
      • サービスで使う言葉を統一する("編集部オススメ"→"EditorsPick"とか)
      • CircleCIでdevelopブランチの変更ごとにビルドして、DeployGateにcurlで上げる
      • 新規開発なのでPull Requestが巨大になりがち。こまめにコミット、こまめにPR
      • ちゃんとKPTでふりかえるようにした。週一。
      • リリース前2weekをバグフィクスにあてる。機能の多さよりクオリティを担保。
  • 以降のリリースサイクルは2week、月曜から段階的に20% → 50% → 100%
  • リリース前週の水曜にコードフリーズ、金曜にチェックシート
  • 朝会後、5〜10分チームメンバー全員でドッグフーディング(立ったまま)
  • 毎週、KPTとはべつに、アプリの今後を全員で考える「ヴィジョナリータイム」実施。エモい。
  • チャットツール(idobata)にcrash hook。Crashlytics連携でメインのチャットに投げられる
    • Play Storeのレビューもhook。gsutilで取得する。ただし2日遅れ
    • ご意見フォーム(id入力とかの要らないカジュアルなもの)もhook

ストアの紹介文から入る点、機能ごとの「雑なプロトタイプ」を作る点と、それでイメージを共有した上でデザイナさんがデザインを作っていく、というプロセスは面白そう。

チャットにクラッシュやユーザの反応を流していくのも良いアイデアだと思いますが、それをissue化する作業が誰か(当事者意識を持った人)に依存してしまう恐れがありそう。真似るなら、KPTや「ヴィジョナリータイム」もセットでやらないと危険かも。

大容量データのダウンロード戦略(@misyobunさん / Room A)

  • android.app.DownloadManagerを使う選択肢。ただし、
    • アプリ内でやりたい。統一されたUIを提供したい。
    • Googleのダウンロードアプリに状況を出したくない
  • 自力で実装。android.app.DownloadManagerのソースを参考にする
  • DLタスクの状態を管理するContentProvider
  • Serviceを使う。Activityをまたぐ処理を行なう場合
    • Serviceから、startForeground()でNotificationを出せる。プライオリティも上がるので死ににくい
    • Activityとプロセスを分けると、ヒープも別になり死ににくい。AndroidManifestに書ける
    • IntentServiceはシリアル実行なので、パラレルしたければ自力でServiceから書く。
    • 生死ハンドリング、プロセス名を使って生存確認
      • onTaskRemoved()は機種によっては呼ばれないので注意

進化するランタイムART(@kmt-tさん / Room A)

  • ARTを理解するには、まず『Android仮想マシン Dalvik編』を読むべき
  • LollipopからはART
  • OATファイル、OATコンパイラが吐く
  • DEXファイルは、OATファイルに埋め込まれる。Annotationなどのメタデータ
  • OAT
    • ELF形式、共有ライブラリ(.so)と同じ
    • リンカ、ローダはOAT独自
    • .rodataセクション:Linux標準のものに加え、DEXそのもの、GCガイド情報など
    • .oat_patchesセクション:OAT独自。イメージの再配置情報
  • QuickコンパイラJITコンパイラベース(だけどARTでは高速化されている)
  • ほかのコンパイラは最新のmasterでは削除されている
  • Quickは、LIR層までCPU依存でない(DalvikはLIRはCPU依存だった)

他のセッションと比較して、はじめからハードル高めな低レイヤの話でした。まずDalvikを知るべし。

tatsu-zine.com

買ってあるけど読めてません…

ARTのメモリ管理(@haru067さん / Room A)

  • GC ルートスキャンと再マークの一部を並行実行されるので、停止は3msくらい
  • LOS(large object space): 大きいオブジェクト専用スペースを設け、フラグメントを抑える
  • 並行(concurrent)GCの実行タイミングが賢くなった
  • 並列(parallel)GC、マルチコアな端末に向けて
  • 世代別GC
  • アロケータ、ResAlloc。並行メモリ割り当てが改善され、ロック不要になった
  • 質疑応答
    • コンパクションは基本やらない。backgroundのアプリを対象

GC(Garbage Collection)の話。Androidはmark and sweepだという認識を持ったままでしたが、色々進化していることを知りました。

つかえるGradleプロジェクトの作り方(@zaki50さん / Room A)

  • 設定するとできること
    • buildToolsVersionなどの一元管理
    • デバッグ証明書をプロジェクトに含めて管理
    • リリースapkへの署名
    • バージョンコード設定
    • git hashを埋め込む
  • Android StudioGUIでもかなり色々設定できる(build.gradleに反映される)
  • 記述方法は、『Android実践プログラミング』第5章および、GitHubで公開しているandroid_gradle_templateリポジトリを参照

techbooster.booth.pm

github.com

アプリを公開する前に、最低限知っておきたいセキュリティ事項(@tao_gakuさん / Room A)

Android Security 安全なアプリケーションを作成するために

Android Security 安全なアプリケーションを作成するために

Material Design を取り入れたデザインリニューアル(@ninjinkunさん、@yuki930さん / Room A)

Fril 3.xにおける、マテリアルデザインのキャッチアップから実装まで。

design

  • キャッチアップ。Feedlyが参考になる。Googleガイドラインは頻繁に更新されている。
  • ユーザテストを実施。既存ユーザの体験を損なう変更があった。
    • 画面下にあったタブをドロアに入れたため、
      • お知らせを開くのに2タップ必要になった
      • 未読バッジが見えなくなった
    • ActionButtonでお知らせに遷移するように変更した。標準的なUIから外れるが、ユーザテストの結果を尊重した
    • ユーザの動線。タイムラインを見た後、お知らせがあれば見て終了、という流れだった
  • 標準のタイポグラフィに合わせると日本語フォントに合わないので調整した
  • Sketch向けUIパーツがGoogleから配布されている
  • アイコンはsvgをIcoMoonというサイトでフォント化して使用。アプリサイズも削減できる。
  • textAppearanceを活用。styleの切り分けが楽に。

code

  • Support Libraryが出る前だったので、UI部品も自力で実装。後のバージョンでSupport Libraryに切り替え
  • リニューアルと同時に構造もモダンに。Fragment, Retrofit, RxJavaを導入
  • calligraphy。textViewに外部フォントを読み込み可能にしている
  • ListViewスクロールに合わせてActionBarを隠す
  • @yanzmさんに加わってもらったことを契機に、メンバー増加に備えた
    • コーディング規約などを整備、CONTRIBUTIONS.mdに集約した
    • JavaDocはちゃんと書くようにした。API Clientは特に丁寧に書く。
    • ActivityやFragmentの継承(BaseActivity/Fragment)をどうすべきか問題。Frilでは使わないこととした
  • リニューアル後、滞在時間2倍くらい、継続率も伸びてる、売買の成約率も上昇。
  • 登録の動線も見直し、離脱率も削減。
  • Googleの2014 Best Appに選ばれた
  • Android 4.0以上にした。2.3向けに旧バージョンを提供はしている。

今ならSupport Libraryがあるとは言え、「導入しました」では済まないところなので大変参考になりました。

なかでも、ガイドラインユーザビリティのくだりは身につまされました。ActionButtonをタブのように使うのは気持ち悪いだろうな、と。でも、既存ユーザの慣れの問題なのか、動線の問題なのかまで踏み込んでいるので、気持ち悪いながらも決断したというところでしょうか。

所感

connpassによる募集は瞬殺、大量のキャンセル待ちを抱えたイベントでした。通常、首都圏のこの手の参加費無料イベントは当日キャンセルも多く、6〜7割くらいの入りになってしまうものですが、今回は10:00の開始時点でかなり席が埋まっているという状態。しかも年齢層が若い。

Android関係の開発者コミュニティの活動が鈍っている、という話はかなり前から聞き、また実感していたところですが、これを見ると需要はあって、個々のコミュニティでうまく世代交代ができずに失速しただけなのかも。

またスタッフの人数も多く、とても至れり尽くせりなイベントでした。スケジュールアプリの提供と、アプリからアンケートフォームを開き、回答してくれた人にステッカーを提供、という流れはとても良いですね*2。 アンケートの回答率がどれくらいだったか、大本営発表が楽しみです。

主催の@mhidakaさん、スタッフ、登壇者の皆さん、また会場提供のサイバーエージェントさんに感謝。 次はDroidcon Tokyoを開催してJeke神を呼ぶ、という話がかなり現実的に聞こえて(見えて)いるので、引き続き期待しています。

*1:と言っていい気がしたけど、次はDroidcon Tokyoになる可能性も?

*2:この手のイベントを主催するとだいたいいつも問題になるので、できれば真似したい

testotips.io(Appium回)に行ってきました #testotipsio

クックパッド株式会社さんで開催された、testotips.io 第1回*1に行ってきました。

connpass.com

testotips.ioは、iOS/Androidの開発系Tips共有会として続けられているpotatotipsのテスト版を指向していて、s.io==塩味*2という意味がある(かもしれない)とのこと。

今回のお題はAppium。スライドは追々connpassに張られていくと思うので、以下メモのみ。

Introduce Appium(松尾さん)

  • Appiumはnodeベースのツールで、iOS/Androidのネイティブアプリおよびブラウザの操作を自動化、GUIテストができる
  • GUIのテストについて。E2E test、Feature testなどと呼ばれているもの。テストケースの量は、Unit test > Integration test > GUI Test
  • 内容はappium.jsを追っていけばだいたいわかる
  • Capabilities*3でAppiumサーバの振る舞いを指定する
    • platform: ios, android, selendroid, chrome, safari, firefoxos
    • iOS: calendarFormat, sendKeyStrategyとかも指定できる
    • Android: unicodeKeyboard(マルチバイト文字を使う場合)
  • Tips
    • Appium 1.3.6では、iOSのplistがバイナリだと文字化けしていた。1.3.7betaで修正
    • Viewの取得、XPathだけでなく、UI Automation(iOS)/uiautomator(Android)の書式でも取得可能
    • Appiumサーバは複数実行できる(listen portを分ける)。ただしiOSはひとつしか実行できない
  • 実行には時間がかかるので、夜間にまわして翌朝チェックしている
  • DroidDriver

テスト結果レポートをSahaginで作ってみる(伊藤さん)

  • Sahagin==さはぎん。伊藤さんが作った
  • 今のところJavaのみ対応。もともとSelenium WebDriverのテスト結果のレポーターで、Appiumでも使用できる
  • テストコードから、日本語+画面キャプチャのレポートをhtmlで出力
    • スクショはステップごとに自動で撮ってくれる
    • findとかasertも日本語に置き換えて出力している
  • PageObjectパターンを使う場合、setterに@TestDocでコメントを書くとそれが表示に使用される
  • Jenkins Sahagin pluginで、Jenkinsのジョブ画面に表示できる
  • Selendroidだとスクショのタイミングがちょっとずれる
  • Tips
    • Jenkinsで実行するとき、テストの前後にAppiumサーバの起動と停止をすると安定する
    • Android emulatorのブラウザでスクショが取れない(Chrom Driverの問題?)
    • Xcode 6.2ではiOSシミュレータのSafariが使えない
    • Appiumのキャプチャが遅い
    • Appiumサーバはrootで起動できないので注意

AppiumのPHPクライアントを使う上で行っている工夫(高橋さん)

  • Yahoo!社内でPHPクライアントを使った自動化の試み
  • JavaScriptはコールバックが多くて嫌だった。社内のサーバサイドで多く使われていたPHPを選択。でも他のクライアントライブラリに比べて機能が足りない
  • テストフレームワークにはPHPUnit
  • Appium PHP Clientを継承したHelperクラスを作り、それを継承した個々のTestCase作成。Helperには足りない機能を書いている
  • コンストラクタでsessionStrategy=isolatedにされていたので、テストケースごとにアプリインストールが走っていた
  • sleepを極力しない工夫。findをラッピングして、例外を返さず、waitしてリトライすることをHelperに実装。テストコードをシンプルに保つ
  • swipeが座標指定なのをラッピング
  • UI操作のスモークテストにAppiumを使用。細かい操作のテストにはEspressoを使用している

Appiumを使ってiPhone Safari、Andorid Chromiumを使ってアプリをテストするTips(浅黄さん)

  • ブラウザのテストにAppiumを使用している
  • Androidは、SelendroidだとWebViewで実行されるので、最近はChromeDriverで直接Chromeを使用するようにしている
  • Selenium Grid Serverを使って実行。ただし、Appiumはキックできるけど、ChromeDriverはキックできないのでGrid Severを経由していない。
  • テストケースは、Excelの表形式で書いたものからJavaのコードを生成する。共通処理(ログイン等)、シナリオ、PageObject、それぞれExcelファイルで書ける

Running appium tests on Jenkins(Vishal Banthiaさん)

  • AppiumによるiOS/AndroidテストをJenkinsから実行するデモ
  • テストはRSpecで、失敗したらスクショを撮っている
  • buildとtestはジョブをわけて、pipelineでつなぐ
  • テストは機能ごとにジョブを分けている(これは増えていくとつらいと思うので私は否定派)
  • デモのリポジトリにはHeplerも入っている
    • servermanager.rb: Appiumサーバの管理。startで、前のプロセスをkillしたりとか。
    • screenshot_embedded_html_formatter.rb: スクリーンショット

所感

懇親会も込みで、他の会社で実際どのように運用しているとか、という話が聞けるのはとても有益でした。Tips紹介だけだとこれは得られないので、テスト自動化まわりはこんな感じに(ツール縛りくらいで)情報交換できる場はいいですね。

*1:とは言っていませでしたが続くといいですね!

*2:プロレスファンとしては「しょっぱい」に余り良いイメージを持てませんが、それはそれ。これはこれ。

*3:Capability==ケーパビリティ。Capacityはキャパシティなのに!

モバイル向けテスト手法勉強会に行ってきました #33testing

先週、クラウド名刺管理のSansan株式会社さんで開催された『最新事例から学ぶ!モバイル向けテスト手法勉強会』に参加させていただき、また「テストの種類とBDD」と題してお話してきました。

テストの種類とBDD

どうしても前提となる話が増えてしまってテスト実装の話まで入れられませんでした。このプレゼンがつまらなくても、テストのことは嫌いにならないでください。

後半は、BDDか、テクニック的な話(テストダブル、テストファースト、TDDとか)にするか迷ったのですが、今回の感触だとテクニックの話のほうがよかったかも知れません。しかし元々20分ギリギリ話すつもり&電エースの話をしすぎた結果、BDDの部分は全部飛ばすはめになった今となってはどちらでもよかったですね。

テストのテクニックに関しては、拙著『iOSアプリテスト自動化入門』の2.3、2.4に書いてありますので、そちらを参照頂けましたら幸いです。

なお、BDDのところで紹介しているテスト自動化ツールの連載は、昨日第6回が公開されました。

ほかの方々の講演

聴講メモが混ざってしまっていたので、以下まとめて全体の話として書きます。

テストを書いてるひとはまだ少ない
  • UIのテストを過剰に自動化しようとする(そして挫折する)よりはいいと思います
  • 岸川さんの話にあったように、モデル(MVCのM)を中心に、ユニットテストは自動化していったほうがいいでしょう
    • ただし、「6〜7割実装してからテストを書きはじめる」のはテストを書いたことが無い人が真似しようとしても難しいです。テストしやすい実装になっていないと、テストを後から書くのは困難なので。
  • ユニットテストのメリット、運用(岸川さん)
    • 変更を入れる際の安心感
    • テストはgreenな状態を維持する
    • カバレジはあまり気にしすぎない。テストを書くモチベーションになるなら使う
      • ユビレジさんの場合、サーバサイドで80%、クライアントは全体で15%、ViewControllerを抜いて25%くらい
ビルド、AdHoc配布までの自動化は進んでいる
  • テストはまだなくても、CIサービスを使ってビルド、AdHocでテスター向け配布までは自動化している
  • ブランチモデルにgit-flowを使い、developにマージされるごとにビルド・AdHoc配布
    • スプリントのQA期間など、5〜10/day以上になり、ビルド待ちが発生することも多い
  • CIサービスとしては、Circle CIが多く、次いでTravis CI
    • Circle CIはタスクの並列化が難しいので、例えばテスト実行と平行してAdHoc配布を行なうといったことはTravis CIのほうが向いている
    • Greenhouse CIは、スクリプトを書かずにできる範囲が広く、ほぼスクリプトレスで構築できる。ただし高度なことを行なうには壁がある
    • Jenkinsでもいいけれど、プラグインに頼りすぎずRakefileMakefileに書くように(岸川さん)
    • Rakefileが巨大になったので、XCJobsを作った(岸川さん)
  • AdHoc配布は、Fabric.ioが多い
    • 最近までTestFlightを使い、Fabric.ioの"Beta by Crashlytics"に乗り換えた
      • TestFlightはiOS8以上なのがネック
    • Crashlytics(クラッシュログ解析)を利用していたのでそのまま
    • iOS/Androidで同じサービスのほうが、テストする人はありがたい
    • 弊社はTestFlightがAndroidを切ったタイミングでDeployGateに移ったのですが、もう少し様子見。
  • 自動化にはfastlaneが便利
    • ビルド、テスト実行(xctool)、署名、AdHoc配布などのコマンドは、fastlaneを使うと便利
    • KrauseFx/fastlane · GitHub
  • チャットでコントロール
    • Hubotに"release"を指示すると、リリースブランチを切り、PR、Travisでビルド、配布が流れる
    • チャットにログが残るので、可視化、かつ、作業単位での分担・引き継ぎが容易
Xcodeプロジェクト
  • プロジェクトをcheckoutしたらすぐビルドできるように。プロジェクト固有ルールでの初期設定はなくす。CocoaPodsのようなデファクトを積極的に使う
  • Target、Configurationがたくさんあるとどれを使うべきかわからない。Schemeもたくさんあるとわからない。
  • 依存ライブラリのコードをプロジェクトに混ぜない。CocoaPodsを使えばlockファイルで差分もわかる
    • なお、"混ぜない"は、プロジェクト内で修正を入れたりしないことが重要で、Podsから取得したコードをバージョン管理に含める運用の是非ではないです。
Android
  • Google Playの段階的リリースを使用している
  • 引き継いだ時点でテストコードなし
  • Robotiumを使用
    • リリースビルドをテストできないので、Appiumを検討中
    • まずスモークテスト
    • solo.getView()などをラップして、スクリーンショットを撮るようにする
    • 極力sleep()は使わず、waitForXxxx()を使う
    • ローカライズを意識して文字列はハードコーディングでなく文字列リソースを使う

所感

  • 過剰にUIのテストをしよう!みたいな雰囲気でなくて良かった
  • ビルド・配布から自動化に手を付けるというのは、良い傾向
  • とは言え、あとからテストを書くのは困難なことが多いです。CIを導入しているなら、サイクロマティック複雑度を計測して酷いものからリファクタリングしていくアプローチもあり。『iOSアプリテスト自動化入門』の8.2あたりが参考になるはず

Realm Tech Talk with JP Simard 1 に行ってきました #realm_jp

iOS/Androidで使用できるDBMS(+ORM)であるRealmのイベント、Realm Tech Talk with JP Simard #1(於 freee株式会社さん)に行ってきました。

Tech Talk with JP Simard from Realm

Realmのメンバー、JP SimardさんによるRealmの紹介。

Realmの特徴

  • オープンソース(ASL 2.0)である。なお、現時点でDBのCore部分はまだクローズドだが、オープンにする方向で進めている。
  • iOS/Androidで同じように使用できる。リリースタイミングのずれはあるが*1、双方、同じ機能を提供する。

既存DBMS(およびORM)との比較

  • iOSのCore DataはSQLiteのORM(Object Relational Mapper)で、古く、遅い。学習コストも高い。
  • SQLiteは早いが、ORMでなくSQLクエリで操作する必要がある。スレッドセーフでない。
  • ほか、FMDB(SQLiteのラッパー)などもあるが、総じて遅い。またAndroid向けには選択肢が少ない。
  • ベンチマーク
    • 単純なinsertは、生のSQLiteが早い(178k件/sec)が、Realmはそれに次ぐ(94k件/sec)。FMDB(47k件/sec)、Core Data(18k件/sec)
    • レコード数カウントは、Realm(30.9回/sec)、SQLite(13.6回/sec)、Core Data(1.0回/sec)。なお、数値はDBに200kレコード入っている状態のクエリ実行回数
    • 抽出クエリは、Realm(31回/sec)、SQLite(14回/sec)、Core Data(2回/sec)

モデルの定義

RLMObjectを継承したValue Objectを定義する。

@interface Employee : RLMObject
@property NSString *name;
@property float salary;
@end

RLM_ARRAY_TYPE(Employee)

@interface Company : RLMObject
@property NSString *name;
@property RLMArray<Employee> *employees
@end

これで、Employeeテーブルと、Employeeの配列(内部的にはrelation)を持ったCompanyテーブルができる。Swiftではデフォルト値の定義も可能。 (コードはその場でメモした擬似コードなので注意)

使いかた

Add(insert)はこんな感じで可能。

let employees = List<Employee>()
let company = Company()

Realm.write{     //transaction
    defaultRealm.add(company);
}

GCDから使う場合

dispatch_async(queue){
    realm.beginTransaction()
    ...
    realm.commitWriteTransaction()
}

(※コードはイメージです)

抽出クエリは、#filter()をチェインしていく形式で記述。

マルチスレッドでRealmを使っても、ちゃんと捌いてくれる。ただし、クエリで取り出したインスタンスはそのスレッドでしか使用できない。別スレッドでは再取得する必要があるので注意。

Notifications

例えばinsertされたタイミングで指定メソッドの呼び出しを登録できる。何がinsertされたかは(今は)わからない。

注意点

  • NSDataは、秒単位までしか保持されない。これはAndroidとの整合性のため。ミリ秒以上の精度はNSTimeintervalを使う。
  • KVOは未サポート

Work In Progress

  • multi process
  • fine-grain notifications(Notificationで操作されたオブジェクトも通知できる)
  • async queries
  • cascading deletes(リレーションのレコードを連鎖で削除する)
  • nullable types(現状はNullを入れられない)
  • sync
  • open source core

質疑応答

理解できたものだけ。

  • Realmのファイルは、iOS/Androidで共通フォーマットなのでファイルコピーで共有できる
  • desktop appで、Realm Browserというのがある。CUIツールは無い
  • coreは、TightDB(旧社名?)
  • in memoryで利用するには、begin transactionのとき指定できる
  • arrayのcountをクエリで取ることはできない(カウントを格納するプロパティを設けるなどのwork around)

realm-javaを使ってみての発表

Chatwork 宮下さんの発表。ChatworkのAndroid版で実際に使用したお話。

Android のORMはそもそも選択肢が少ない。

  • greenDAO: 早いけど準備が面倒
  • ActiveAndroid: insertが遅い(Chatworkさんの事例での話で、大量にinsertが発生すると厳しい)
    • Benchmark: 619件の処理(insert?)で、ActiveAndroidでは1551ms、Realmは414ms。

realm-javaでのモデル定義例

class xxx extends RealmObject{
    @primaryKey private long id;
}
  • RealmObjectはvalue objectでなくてはならず、ロジックは持てない。toString(), equals()とかもオーバーライドできない。
  • Realmのobjectは、スレッド単位でしか利用できない。別スレッドで扱うならqueryしなおす
  • 日本語ドキュメントのバージョンは0.72、最新は0.79.1。かなり変更があるので、最新情報にあたるほうがいい。
  • ButterKnifeと干渉するところがあったが、Gradleの定義で回避できた

所感

iOS/Androidで共用できるシリアライズ機構としては、Dropboxが公開しているmx3も気になっているのですが、こちらはライブラリでなく実装サンプルであり、ORMも含めて考えるとRealmの有望さを感じました。

モデルがvalue objectに限定されることは不便に思いますが、ロジック付きのラッパーを作ればいいので致命的ではなさそう。

最近、DBMSを使う系のアプリを作っていないのですが、止まっている電エースアプリAndroid版ででも使ってみたい。

懇親会では、freeeさんの会社およびプロダクト*2の話をたくさん聞けて、それもまた色々参考になりました。

freeeさん、岸川さん、通訳を買って出てくださった北さん、そしてJPさん、ありがとうございました。

*1:iOSがやや先行するみたい

*2:クラウド会計をユーザとして使いたいと思っていたので

#ABC2014W におけるOculus Rift体験展示のノウハウ #JAGVR

東海大学 高輪キャンパスで行われた、日本Androidの会のイベント「ABC 2014 Winter」においてVR部として一部屋いただき、Oculus RiftなどによるVRコンテンツの展示、またタオバイザー(Cardboard互換HMD)向けアプリ作成のハンズオンを行ないました。

私はほぼ展示に張り付いていたので、そこで得られた知見・改善点などを書き留めておきます。

なお、セッティング等どうしても汎用的な方向に振るしかなく、当日余り良いVR体験を得られなかった方もいらっしゃったはず。お詫びするとともに、できればこれに懲りず、よりご自身に合ったセッティングで体験いただける機会にリベンジしていただけましたら幸いです。

f:id:nowsprinting:20141221100418j:plain

Oculus Rift DK2

Oculus Riftは、Oculus VR社が販売しているHMD(Head Mount Display)です。現在、DK2(Development Kit 2/開発キット)をOculus VR社のWebサイトから購入(個人輸入)することができます。

今回VR部ではこれを2台(2コンテンツ)用意し、VR(Virtual Reality)体験してもらうという展示でした。

セッティングのポイントは以下3点。

  • レンズはA(通常・遠視向け)固定。ある程度の近視の方には裸眼、それで無理そうな方にはメガネ着用で体験していただきました*1。ここは妥協。
  • レンズ距離は一番近くに設定。私としては少しだけ離すのがベストなのですが、今回は両手が空くコンテンツですし、見づらい方には手で保持して顔から離して調整してもらうことに。
  • IPD(瞳孔間距離)は、今回は5.5cmとしました。IPDについての詳細、決め方など『Oculus Riftでオレの嫁と会える本』Chapter 8-1を参考にしました。

VRコンテンツ作成の入門書的な本ですが、VR酔いへの対応策なども丁寧に書かれています。ぜひ手元に置いておくべき一冊。

PC

880M SLI搭載のゲーミングノートを使用しました。OSはWindows 7。ノートのメリットは本体の可搬性と、説明員が状況を確認するためのモニタがあること。展示に使用するのでなければタワー型のほうがコスパいいです。

おすすめ設定は下記を参照。

コンテンツ

展示したコンテンツは、@youten_redoさんチョイスの以下ふたつ。

UnityCoaster2 UrbanCoaster

iWorks(@x68user)氏によるジェットコースターの定番。Oculus SDK 0.4.4対応のV1.1を使用。

Unity-Chan "Candy Rock Star" Live Demo

ユニティ・テクノロジーズ・ジャパン制作の「ユニティちゃん」ライブのOculus Rift対応版。GitHubリポジトリoculusブランチのものからOculus SDK 0.4.4に入れ替えたもの。

展示向けコンテンツの要件

必須要件としては3点。

  • 適度な長さ(この点、ユニティちゃんライブはショートに変えたほうがよかったかも)
  • VR酔いしにくい品質(75fps出ること)
  • 一回再生するごとに停止し、何らかのアクションで次の再生がはじまる(体験者が入れ替わるため)

さらに、3DVR体験としてのわかりやすさから、あったほうがいい特徴3点。

  • カメラに向かってキャラクターが接近する(3Dっぽさの体感)
  • まわりを見渡せる(ヘッドトラッキング
  • 近づいたり、覗きこんだり、VR世界内のキャラクターがこちらを見る(ポジショントラッキング

今回は借り物のアプリですが、展示を回す中で「あったほうが良い」と感じたことがいくつかありました。今後、自作する際には入れていきたいのが以下。

  • Rキーでポジションのリセット。これはOculus SDKのCameraには入っているのですが、Cardboard向けアプリでも何らかのジェスチャーでリセットできるような仕掛けは考慮したほうがよさそう
  • 再生を中断して、アイドル状態に戻る機能*2。これが欲しい理由は以下2つ。
    • 体験者が途中でHMDを外して離脱してしまうことが何度かあった
    • 体験者がいないとき、PCのモニタで再生しておきたい。それを、体験者が来たときに中断したい。

その他、準備したもの

ヘッドホン

没入感を増すため、周囲の雑音を考慮するとあったほうがいいです。より没入感が欲しいコンテンツ(今回のユニティちゃんライブや、Mikulusなど)はノイズキャンセリングが望ましいかも。

ただしノイズキャンセリングの場合、説明員の声が聞こえなくなるデメリットがあります。必要なアナウンスをコンテンツ側に作りこむ、オペレーションを徹底するなどの考慮は必要でしょう。

ウェットティッシュ

HMDのスポンジ部分を拭くために用意しました。余り活用できてなかったので、スポンジ保護シートなどと組み合わせることを考えたほうがいいかも。

フライヤー

これは用意してなかったのですが、どういったコンテンツか、体験時間は何分か、といった情報は紙で掲示しておくとよかったかも。

また、待ち行列ができるのであれば、HMDの調整の仕方なども事前に読んで待っていただけるとよりスムーズになるでしょう。

オペレーション

呼び込み

Ocufesなどとは異なり、「Androidのイベントに来て、講演の合間にうろうろしたらVR部屋があった」というものなので、積極的に並んでまで体験しよう、という方は少なかったです。 従って、遠目に、興味ありそうに見てる人がいたら呼び込んで被らせる形。

皆さん講演の合間に展示を回っている状況なので、待ち時間、終了時間が見える形でないと並んで待ってまで体験するには至らない、と途中で気付きました。今後似たようなケースであれば改善していきたい。

お子様が来られたとき、明らかに13歳未満でしたので泣いて馬謖を斬る思いでお断りしました。この辺りの根拠についてはこちらにまとめられています。ちゃんと断る覚悟を持つために読んでおいたほうがいいです。

セットアップ

体験者に座っていただいてから、スタートまでに気をつけたこと。

  • まず、正しく座ってもらう。荷物のために斜めに座ったりしてしまう方もいたので、極力まっすぐ座ってもらう。バックパックなどはそれほど問題ないと思ったので背負ったままで。
  • メガネの方、外してもある程度は見えるのかを聞く。
    • ある程度見えるのであれば、裸眼でHMDを被ってもらう
    • かなり悪いならば、一旦外してもらってHMDといっしよにかぶってもらう
  • HMDを被せる。ほとんどそのまま被ってもらってしまいましたが、メガネの方とか女性とか、もっと後ろのマジックテープ着脱を活用すればよかった
  • 目とレンズの位置がずれているとボヤけて見えるので、表示されている文字がくっきり見えているか確認。スループットが落ちてでもここはちゃんと調整してもらうほうがいいはず。
    • 「上下左右に動かしてはっきり見える位置」「もしくは顔から少し離してみる」という説明がうまく伝わらないこともあって、表現は検討・改善していきたい。
  • HMDを装着した状態でリラックスした姿勢を取ってもらい、そこでキーボードのRを押してポジションリセット。

スタート

  • コースターは、"Look Here"の文字を凝視するとスタートします。ちゃんと見るべき位置を言ってあげて、細かく観測補正(ちょい右、ちょい下、とか)してあげれば、問題なくスタートできていました(逆にサポートなしだと厳しいと思う)。
  • コースターが動き出してから、「ヘッドホン被せます」と宣言しつつヘッドホンを被せる。相手は視界を奪われているので、かならず宣言してから。
  • まだスピードが遅いうちに、まわりを見渡せることを伝えました。UnityCoaster2 v1.1では左に女の子が座っているので、それを見てもらうのがインパクトありました。
    • 伝えるタイミングはスタート前でもいいかも知れません
    • ノイズキャンセリングヘッドホンを使う場合は、スタート前に。

ゴール

  • ゴールの建物に入ったあたりでヘッドホンを外してあげます
    • このときも、先に「ヘッドホン外します」と宣言してから
    • 早めに外すのは、自分でHMDを先に外そうとする傾向があるため
  • ほとんどの方はHMDは自分で外してもらっていましたが、後ろのマジックテープを外してあげてもよかったかも
  • 何か言わないと気まずい、なんと言っていいかわからない、という人もいそうなので、とりあえず「酔いとかないですか?」とか先に聞いてみるなどしました

その後

  • ほぼ並んでいない状態ですが、「空いたらやってみたい」くらいの人はいるので、できるだけ早めに椅子から立ってもらったほうがいいです。
  • 座ったまま質問などはじめる方もいるので、できれば次の方をスタートさせるまで先にオペレーションする間、待っていただくよう上手く言えるとベスト。難しいですが。
  • 無理にHUDを外そうとした際にレンズ距離が動いてしまったことがあったので、定位置にあるかチェックする

所感

こういった形でOculus Riftの体験展示を行なうのははじめてだったので、オペレーションは一日かけて体験者の方を観察しながら改善していった感じです。

重ねて書きますが、当日余り良いVR体験を得られなかった方もいらっしゃると思います。申し訳ありませんでした。Oculusは、VR技術は悪くないので、ぜひぜひこれに懲りずにリベンジしていただけることを切に願います。

今回、展示は自作でなく借りもののコンテンツだったのですが、それでも体験された方のリアクションを見ているのはとてもとても楽しかったです。ご来場いただいた皆さま、ありがとうございました。

*1:視力1.0未満だとAではぼやけるそうです。裸眼でも1.0ある人は少ないはずなので、Bのほうがよかったのかも

*2:アプリケーションを再起動すればいいのですが