Intelligent Technology's Technical Blog

株式会社インテリジェントテクノロジーの技術情報ブログです。

Fabric CrashlyticsKitでベータ版配信(iOS編)

こんにちは、間藤です。

今回は、Twitter社のFabricが提供するサービスのうち、Crashlyticsのアプリのベータ版配信(Beta Distribution)について取り上げてみます。

まずは導入

導入手順は、すでに多くの方が情報を提供してくださっていますので、この記事であらためて紹介する必要もないかと思います。私は、以下の記事を参考にさせていただきました。

qiita.com

手順の補足

基本的に上に紹介した手順通りで問題はないと思うのですが、私はSDKの導入のところで躓いたところがあったので補足しておきます。
アカウントを作成してアプリをまだ1つもFabricに登録していない状態だと、ブラウザでFabricにログインしても、ダッシュボード画面に到達することはできません。ダッシュボード画面に遷移するには、上記手順を踏んで、アプリをFabricに登録しなければなりません。それが行われるのは、AppDelegateに以下のコードを追記して、アプリを起動したとき(つまり、以下のコードが実行されたとき)になるようです。

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
    // Override point for customization after application launch.
    [Fabric with:@[CrashlyticsKit]];

    return YES;
}

ですから、Mac上で動作させるFabric.appは、以下のような画面を表示して、対象アプリを実行するように促します。

f:id:IntelligentTechnology:20150413121549j:plain

そして、その指示通りにアプリを実行するのですが、右下の「くるくる」表示がそのまま変わらず、一向に先に進めません。しばらくすると、対象がアプリが実行されたことを認識できなかったと、エラー画面に遷移してしまいました。なぜうまくいかないのか理由がよくわからず、いろいろと試していましたが、実機ではなく、シミュレータ上でアプリを実行したしたところうまくいきました。以下のように"Done"と表示されればOKです。

f:id:IntelligentTechnology:20150413123330j:plain

本当に実機だとダメなのかは、しっかりと確認したわけではないですが、同じ状況に陥った場合、シミュレータ上で実行するとうまくいくかもしれないので、試してみてください。
ここまでくれば、ブラウザのほうからダッシュボードを確認することができるようになると思います。

Androidでは試せず

Androidについても試してみようと、Android Studioのプラグイン(バージョン2.2.0)を導入してみましたが、プロキシ経由での利用に問題があるようで、正常に動作させることができませんでした。プラグインからFabricにログインしようとしても、"Unable to connect to the network"というメッセージが表示されてログインできません。以下のリンクでも、私が遭遇している問題について議論していますが、解決策が見つかっていないようです。

Fabric AS plugin does not respect proxy settings - Fabric - Twitter Developers

テスト配信

XcodeでArchiveを実行すると、Fabric.appがそれを検知して配信を行うことができるようになります。また、配信を実行することで、そのバイナリがFabricに登録され、ダッシュボードから確認することができるようになります。
ここでいう「配信」とは、

  • テスターを登録する
  • テスト対象のアプリを登録する

という2つの意味があります。
それぞれについて、掘り下げてみていきたいと思います。

テスター登録

テスターの登録は、いくつかの方法が用意されています。

  • Fabric.appからメールアドレスを直接入力
  • テスターの一覧を入力したCSVファイルをFabric.appに指定
  • ダッシュボードから指定

テスターをCSVファイルで管理しておけば、多少は誤送信を防ぐ効果があるかもしれません。なお、CSVファイルのフォーマットですが、マニュアルを探しても見つけることができず、どうしたものかと思っていたんですが、"+Import CSV"ボタンにマウスカーソルを合わせると、以下のようにガイドが表示されました。(わかりづらいです・・・)

f:id:IntelligentTechnology:20150413134719j:plain

但し、CSVに記述した名前は無視されます。
名前は、招待メールを受け取った際、テスター自身が入力したものが使われます。デバイスの紐付けもこの時に行われるようですので、例えばMacから名前を登録することはできません。(テストに利用する端末からアクセスしろと怒られて、名前の入力は促されません)

f:id:IntelligentTechnology:20150413183927j:plain

テスターは、アプリの各バージョン毎に管理されており、ダッシュボードで確認することができます。ここからテスターを追加したり、無効化したりすることができます。(無効化については後でも取り上げます)

f:id:IntelligentTechnology:20150413135335j:plain

ダッシュボード上では、テスターの「名前」「メールアドレス」「利用デバイス」が関連付けされて表示されることになります。初めて登録されたテスターについては、メールアドレスしか表示されません。テスターが招待メールのリンクから登録を行うことにより、名前と利用デバイスが紐付けられることになります。

f:id:IntelligentTechnology:20150413143331j:plain

なお、一度メールアドレスに名前やデバイスを紐付けると、それを後から変更することができないようです。私が見落としているだけかもしれませんが、そのような設定画面を見つけることができませんでした。
テストは、個人所有の端末ではなく、プロジェクトチーム用に用意された端末を利用することも多いかと思います。そういったケースでは、ある端末の利用者が頻繁に変わることもあるでしょう。ですから、名前は個人名ではなくて、端末の名前を登録しておけばよいと思います。つまり、配信の際に指定するメールアドレスは、事実上デイバスを指定しているのと同じ意味になるということです。

ダッシュボード上では、あくまでもメールアドレス単位でテスターが管理されるようです。別のテスターに同一の端末が紐付いていたとしても、特に支障はなく利用できるようです。

f:id:IntelligentTechnology:20150413164039j:plain

ただ、こんな風になってしまうと、管理が煩雑になるので、やめたほうがよいですね。

アプリのバージョン管理

アプリの新しいバージョンをアップロードする方法もいくつか用意されています。

  • Fabric.appからアップロード
  • submitコマンドを利用してアップロード

Fabric.appからアップロードを行う際、そのバージョンに対するテスターを一緒に指定しますが(指定しないことも可)、各テスターに紐付けられたデバイスにアプリをインストールできるかのチェックも行ってくれます。Archiveした際に利用したプロビジョニングプロファイルが、テスターのデバイスに適合していない場合は、以下のような警告を表示します。

f:id:IntelligentTechnology:20150413165519j:plain

ここで"Skip"を選択すれば、警告を無視してアップロードすることも可能です。(もちろん、適合しないデバイスにはインストールできませんが)

submitコマンド利用については、以下のマニュアルの記述を参考にしました。

Distribution with iOS Build Tools — Fabric iOS latest documentation

ここには、2つの方法が記載されていますが、1つ目の方法はうまく動きませんでした。Xcode上でRun Scriptを追加し、ここに記載されたようにsubmitコマンドを実行するようにしてみましたが、

submit Crashlytics: Crashlytics.framework/submit 1.3.1 (11)
submit Crashlytics: Submit binary cannot be run from within Xcode. Please use the Crashlytics app instead.

というようなエラーメッセージが表示され、ビルドが失敗してしまいました。このメッセージ通りであれば、Xcodeから直接アップロードすることはできないということだと思います。(マニュアルが古い?)
一方、2つ目の方法は、この記載の通りでアップロードすることができました。CIサーバと連携してアップロードする場合は、この方法を利用することになると思います。

アプリのバージョンは、Version(CFBundleShortVersionString)とBuild(CFBundleVersion)によって識別されます。

f:id:IntelligentTechnology:20150413174125j:plain

ダッシュボード上でもそれは同様で、VersionとBuildが同一のバイナリをアップロードした場合、前のアップロードを上書きします。(リリースノートも上書きされます)

f:id:IntelligentTechnology:20150413175444j:plain

逆に言えば、アップロードする際には、VersionかBuildを更新しておかないと混乱してしまいそうです。

テスターの無効化

テスターは、各バージョンごとに管理されているため、テスターは必ず最新バージョンをインストールできるわけではありません。最新バージョンのテスターに指定されていない場合は、そのバージョンをインストールすることはできません。また、一度テスターに指定されても、その後で無効化(Revoke Access)されると、そのバージョンを(まだインストールしていなければ)インストールできなくなります。

過去バージョンに遡って、テスターを無効化したいというケースはあまりないとは思いますが、一括であるテスターを削除するということはできないようです。
アプリの過去バージョンを無効化することは可能です。
ダッシュボードの左に配置されたSettingsメニューから遷移し、「APPS」を選択します。対象アプリを選んで、Versionsタブを選択すると、各バージョンの有効/無効を切り替える画面が表示されます。

f:id:IntelligentTechnology:20150413181259j:plain

無効化されたバージョンは、すべてのテスターからインストールすることができなくなります。

Crashlyticsの無効化

Crashlyticsのクラッシュレポート機能は利用しないのであれば、アプリを本稼働(App Storeに公開)する際には、Crashlyticsを無効化したいということもあるかもしれません。プログラム上では、前述のようにAppDelegateに一行追加するだけですので(ヘッダのインポートを入れれば3行ですが)、これをコメントアウトすることで、Crashlyticsを無効化できると思います。さらに、FabricおよびCrashlyticsのFrameworkをリンクしないようにするのであれば、そのための変更も必要です。
これをリリースのたびに手作業で行うのはナンセンスですので、例えばベータ版配信用にConfigurationを作成し、ビルドを切り替えるようにすればよいと思います。
以下、自分のための備忘録を兼ねて、設定例を記載します。

Configurationを追加

Configurationを追加します。(ここでは、Stagingという名前で追加しました)

f:id:IntelligentTechnology:20150415173101j:plain

追加したConfigurationにマクロ定義

f:id:IntelligentTechnology:20150415173716j:plain

コードを編集
#ifdef USE_FABRIC
#import <Fabric/Fabric.h>
#import <Crashlytics/Crashlytics.h>
#endif

・・・

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
    // Override point for customization after application launch.
#ifdef USE_FABRIC
    [Fabric with:@[CrashlyticsKit]];
#endif

    return YES;
}
Frameworkを追加したConfigurationのみリンクするよう変更

以下の設定からは、Fabric.frameworkとCrashlytics.frameworkを削除し、

f:id:IntelligentTechnology:20150415180031j:plain

追加したConfigurationに個別設定します。

f:id:IntelligentTechnology:20150415180537j:plain

スキーマを追加

スキーマを追加して、Archive時のConfigurationにStagingを指定します。(スキーマ名もStagingにしました)

f:id:IntelligentTechnology:20150415181308j:plain

これでStagingスキーマでArchiveしたときだけCrashlyticsと連携するようにビルドされますので、Fabricにはこのバイナリをアップロードすればよいでしょう。
いざ、リリースという際は、元々あるスキーマでArchiveすれば、Crashlyticsとは連携しないようビルドされるはずです。

テスト配信の実運用

CrashlyticsKitでのテスト配信は、緊急なバグFIXが必要になった場合を除けば、事前に計画されたスケジュールに沿って行う運用になるのではないかと思います。そう考えると、プログラマがXcodeでArchiveし、Fabric.appでアップロードを行うというよりも、CIサーバと連携してアップロードする運用になるでしょう。
そうであれば、導入手順の中でBuild Phaseに追加したRun Scriptは必要ありませんので、削除してしまったほうがよいでしょう。この中に記述してしまっている"API Key"と"Build Secret"がわかってしまえば、誰でもアップロードできてしまうので、使わないなら削除しておいたほうがいいのかなと思います。

CIサーバと連携するなら、例えば以下のようなスクリプトでipaファイルを作成し、

#!/bin/sh

#
# (注意)エラーの考慮は一切していません
#

PROJECT_DIR="/Users/matoh/fabric/SampleApp"
WORKSPACE_FILE=${PROJECT_DIR}/"SampleApp.xcworkspace"
SDK="iphoneos"
SCHEME_NAME="Staging"
CONFIGURATION="Staging"

PRODUCT_NAME="SampleApp"
VERSION_NAME=$(/usr/libexec/PlistBuddy -c "Print CFBundleShortVersionString" ${PROJECT_DIR}/'SampleApp/Info.plist')
BUILD_NUMBER=$(/usr/libexec/PlistBuddy -c "Print CFBundleVersion" ${PROJECT_DIR}/'SampleApp/Info.plist')
IPA_FILE_NAME=${PRODUCT_NAME}_${VERSION_NAME}\(${BUILD_NUMBER}\).ipa

OUT_APP_DIR=${PWD}/"build"
OUT_IPA_DIR=${OUT_APP_DIR}

if [ ! -d ${OUT_IPA_DIR} ]; then
    mkdir "${OUT_IPA_DIR}"
fi

xcodebuild clean -workspace "${WORKSPACE_FILE}" -scheme "${SCHEME_NAME}" -configuration "${CONFIGURATION}"
xcodebuild -workspace "${WORKSPACE_FILE}" -scheme "${SCHEME_NAME}" -sdk "${SDK}" -configuration "${CONFIGURATION}" install DSTROOT="${OUT_APP_DIR}"
xcrun -sdk "${SDK}" PackageApplication "${OUT_APP_DIR}/Applications/${PRODUCT_NAME}.app" -o "${OUT_IPA_DIR}/${IPA_FILE_NAME}"

後は上で紹介したsubmitコマンドでアップロードするような感じになるでしょうか。