Crashlytics をどうしても Carthage で使いたいあなたへ

※Firebase 及び Crashlytics の Carthage 対応は本家ではまだ安定バージョンをリリースしていないため、本記事の有用性は執筆時点の最新版（Firebase：5.17.0；Crashlytics：3.12.0）以外では保証しかねます。ご注意ください。

脱 CocoaPods の道のりで一番の難関とされる二大フレームワーク（筆者調べ）の R.swift と Firebase ですが、[前者についてはすでに記事化した](https://qiita.com/lovee/items/5617cdaa28a470b141c2)ので、今回は後者の Firebase ——の中でもおそらく一番よく使われる機能である Crashlytics の導入についてまとめます。

## Firebase 部分

Crashlytics は現在 Firebase の一部となっており、導入には Firebase をまず導入する必要があります。

Firebase 公式としては Carthage 対応を視野にしており、安定版ではないが [Carthage でのインストール手順を公開しています](https://github.com/firebase/firebase-ios-sdk/blob/master/Carthage.md)。まずはこの手順を沿って Firebase をインストールします。

### 1. Cartfile に必要な項目を追加

上記の手順の中に Firebase の各コンポーネントリストがありますが、ここで必要なのはコアの `FirebaseAnalyticsBinary` のみです。**`FirebaseCrashBinary` は必要ありません**。（筆者自身はその名前に騙されて最初はこちらも導入してしまったのですがこれは必要ないです；むしろ[後述するように](#q3-ビルドもインストールも成功したが通常実行で-crashlytics-のテストコードなどで意図的にクラッシュを起こそうとしてもアプリが落ちずにフリーズしてしまいます)導入したら逆に問題が起きました）

```text:Cartfile
binary "https://dl.google.com/dl/firebase/ios/carthage/FirebaseAnalyticsBinary.json"
```

### 2. Carthage をアップデートし、落としたフレームワークをプロジェクトに追加

Carthage のアップデートは何も言うことないと思います、ターミナル開いて `$ carthage update` を打つだけです（プロジェクトや開発者に応じて後ろにさらに引数が付くこともあるかと思いますが）。

ところが肝心なのはそのあとの、`落としたフレームワークをプロジェクトに追加` の部分です。これは通常の Carthage で導入するフレームワークとちょっと違う部分です。

Firebase のフレームワーク**群**は **Static Library も含まれています**。そのためよくやるような、Build Phase で `carthage copy-frameworks` のスクリプトを追加し、中の Input Files にフレームワークのパスを記述してはいけません。

ですのでこのステップの正しいやり方は、Carthage でビルドされた（正確には落とした）フレームワークを Finder で表示し、これらのフレームワークを全て Xcode のプロジェクトファイルツリーにドラッグし、グループとして参照を追加することです。追加する際に注意しないといけないのは、利用する予定のアプリターゲットにチェックが入らないといけないです。

![スクリーンショット 2019-03-05 19.59.39.png](https://qiita-image-store.s3.amazonaws.com/0/63998/b25c2cf0-159f-63ed-c886-d2f64bbcd789.png)

`FirebaseCrashBinary` のみの場合、落とされるフレームワークは下記のものになります：

```md
- FIRAnalyticsConnector.framework
- Firebase.framework
- FirebaseAnalytics.framework
- FirebaseCore.framework
- FirebaseCoreDiagnostics.framework
- FirebaseInstanceID.framework
- GoogleAppMeasurement.framework
- GoogleUtilities.framework
- nanopb.framework
```

### 3. 「Firebase.framework」だけ「Linked Binary With Libraries」から削除

上記の手順を踏んだら、ターゲットの「Build Phases」タブにある「Linked Binary With Libraries」内に先ほど追加した全てのフレームワークが勝手に追加されますが、この中に[「Firebase.framework」のみリストから外す必要があります](https://github.com/firebase/firebase-ios-sdk/issues/911#issuecomment-372455235)。

### 4. 「Build Settings」の「Other Linker Flags」に `$(OTHER_LDFLAGS) -ObjC` を追加します

絶対必要だそうです。ちなみに公式手順では `$(OTHER_LDFLAGS)` と書かれていますが、Xcode の習慣として `$(inherited)` と書いても問題ありません。

### 5. 「GoogleService-Info.plist」をプロジェクトに追加

こちらは [2.](#2-carthage-をアップデートし落としたフレームワークをプロジェクトに追加) と同じ要領で、落とした「GoogleService-Info.plist」をプロジェクトに追加し、ターゲットに入っていることが確認できれば OK です。

### 6. 必要な外部ライブラリーをリンク（←これ重要）

ターゲットの「General」タブにある「Linked Frameworks and Libraries」にも [2.](#2-carthage-をアップデートし落としたフレームワークをプロジェクトに追加) で追加されたフレームワークが表示されていますが、この中にもさらにそれらのフレームワーク群が使っているライブラリーやフレームワークを追加する必要があります。FirebaseCrashBinary のみの場合は下記のものが必要です：

```md
- libc++.tbd
- libsqlite3.tbd
- CoreTelephony.framework
- StoreKit.framework
```

もし他に利用するコンポーネントがあったらさらに別のフレームワーク等が必要かもしれません。判別法はまた[後に述べます](#q1-ビルドで-undefined-symbols-for-architecture--エラーが出ました)。

### 7. Firebase を初期化

これで Firebase の導入の最後です。適当なところ（大抵 `application(_:, didFinishLaunchingWithOptions:)` の中かと思いますが）で `FirebaseApp.configure()` を追加するだけです。

```swift:AppDelegate.swift
// 略…
import Firebase

@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {

    func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
        // 略…
        FirebaseApp.configure()
        return true
    }

    // 略…

}
```

これでひとまずアプリをビルドして実行すれば、Firebase との通信が可能になりました。

## Crashlytics 部分

Crashlytics は残念ながら今のところまだ Carthage 対応が全くない[^crashlytics-carthage]状態です。ただ幸いに、[公式ドキュメント](https://firebase.google.com/docs/crashlytics/get-started)では手動導入の方法も紹介しており、しかもそのドキュメントは日本語版もありますので難しくはないかと思います。

### 1. 「Crashlytics SDK ファイル」を落として Xcode プロジェクトに追加

これは [Firebase 部分の 2.](#2-carthage-をアップデートし落としたフレームワークをプロジェクトに追加) と同じ要領でできると思います。落としたものは「Crashlytics.framework」と「Fabric.framework」の二つのフレームワークがありますので、両方プロジェクトディレクトリーに移動して、プロジェクトに追加します。その際はきちんとターゲットに追加されていることを確認します。

### 2. Crashlytics の実行スクリプトを追加

次にターゲットの「Build Phases」タブに行って、「New Run Script Phase」を**末尾に**追加し、「Fabric.framework/run」を実行するスクリプトを追加すれば OK です。例えば公式ドキュメントは下記のコマンドラインを紹介しています：

```bash
"${PROJECT_DIR}/Fabric.framework/run"
```

ただこれは「Fabric.framework」をプロジェクトのルートディレクトリーに置いてる場合は問題ないですが、実際はご自身のこれらのフレークワークのディレクトリーに応じて、上記のコマンドラインに修正を入れる必要があるかもしれません。

また、Xcode 10 はビルドシステムが変わったため、該当 Run Script Phase の「Input Files」に、ビルドしたパッケージの Info.plist ファイルのパスを追加する必要があります：

```text
$(BUILT_PRODUCTS_DIR)/$(INFOPLIST_PATH)
```

### 3. Debug Information Format を更新

おそらく Xcode の初期状態では、dSYM ファイルは Release ビルドにしか含まれていないかと思います。Debug ビルドでも Crashlytics で情報収集したい場合は dSYM ファイルを Debug ビルドにも含めるようにする必要があります。これはターゲットの「Build Settings」の「Debug Information Format」の欄を探し、値を「DWARF with dSYM File」に変更すれば OK です。

これで、適当に `Crashlytics.sharedInstance().crash()` とかのテストコードを仕込んでみれば、Crashlytics によるクラッシュ情報の収集が可能になったはずかと思います。

## よくある（というよりも筆者が実際あった）質問

### Q1. ビルドで `undefined symbols for architecture ...` エラーが出ました

A1：もしこれは Firebase 導入前に発生しておらず、Firebase の導入とともに発生したエラーでしたら、必要なライブラリーが見つからなかったということを意味します。この時に確認すべき事項は下記の二つかと思います：

1. Carthage で落とした**全ての** Firebase 関連フレームワークをターゲットに追加しましたか
2. 上記以外のものでも、上記フレームワークが内部として使っているものがありますので、[これらを追加する必要があります](#6-必要な外部ライブラリーをリンクこれ重要)（←筆者がまさにこれに引っかかりました）

ではどのようにさらに必要なフレームワークが必要かを判別するかというと、このエラーの下には必ず `"xxx", referenced from: yyy in zzz` の具体的な情報が入っています。その `xxx` の部分でググれば、大抵これがどのフレームワークのものかが判別できるかと思います。

### Q2. ビルドは成功したが、インストール時に `Found bundle at ... with the same identifier (com.firebase.Firebase) as bundle at ...` エラーが出ました

A2：これは Firebase 関連のフレームワークを `carthage copy-frameworks` のフェーズで Input Files として入れてしまったからです。Firebase 関連のフレームワークは中身に Static Library が含まれているため、**通常の Carthage によるフレームワークの導入の仕方をしてはいけません**。必ず[これらのフレームワークをそのままビルドターゲットに追加](#2-carthage-をアップデートし落としたフレームワークをプロジェクトに追加)する必要があります。

### Q3. ビルドもインストールも成功したが、通常実行で Crashlytics のテストコードなどで意図的にクラッシュを起こそうとしても、アプリが落ちずにフリーズしてしまいます

A3：これはおそらく黒魔術のコンフリクト的なものによる現象かと思います。もしこれを発生してしまったら、Cartfile に `FirebaseCrashBinary.json` が入っているかどうかを確認しましょう。おそらくはこちらのフレームワークの黒魔術と Crashlytics のフレームワークの黒魔術が衝突しています。[`FirebaseCrashBinary.json` は必要ない](#1-cartfile-に必要な項目を追加）。

[^crashlytics-carthage]: 正確には公式ではそういった対応が現状全くないですが、非公式な方法でしたら[こちらの方法](https://github.com/Building42/Specs)があります。