Firebaseを利用したiPadOSアプリをCatalyst対応してmacOSアプリ化 #firebase #catalyst #iOS #iPadOS #macOS

先日macOS Catalinaがリリースされました。それに伴いTwitter公式アプリもCatalystでmacOS化されてリリースされました。
[Twitter](https://apps.apple.com/jp/app/twitter-for-mac/id1482454543?mt=12)

個人的に開発している[# Type](https://type-markdown.app)もCatalystには向いているアプリなので是非やってみたいな常々思っていました。
そこに立ちはだかるのが利用しているライブラリ群です。
特に[# Type](https://type-markdown.app)で大きく依存いているのがFirebaseです。

Firebaseのissueを覗いてみるといくつか **Feature Request** でCatalystのサポートが要求されている事が分かります。
[Issues · firebase/firebase-ios-sdk · GitHub](https://github.com/firebase/firebase-ios-sdk/issues?utf8=✓&q=is%3Aissue+Catalyst)

これをウォッチしていると、Analyticsなどプライベートな機能を有するものを除き、コミュニティによる支援によってある程度の対応が済んだ事が発表されました。
[macOS and tvOS | GitHub - firebase/firebase-ios-sdk: Firebase iOS SDK](https://github.com/firebase/firebase-ios-sdk#macos-and-tvos)

今回[# Type](https://type-markdown.app)でCatalyst対応を試みたので流れを公開します。

## 環境

- macOS Catalina(10.15)
- Xcode 11.0

## 前提条件

iPadOSに対応している必要があります。
(更に `SceneDelegate` に対応しておいた方が良いかと思いますがこの辺りは試してないので余計な事は言わないでおきます)

## Targetの分割

最初にiOSのターゲットのままは厳しそうだと感じたので、ターゲットを分割しました。(同じターゲットのままでももしかしたら通るかもしれないですが、iOS側で必要なframeworkのせいで進まない可能性もあるので、必要ないframeworkを削除しやすくする為には分割するのが楽だと思います)

<img width="263" alt="image.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2846/60ad03e2-3397-eb5e-6d5a-d6e1a5fe4979.png">

今回は`$(PRODUCT_NAME)Catalyst`の様な名前にしました。
Deployment InfoのDeviceを `iPhone` を外して `iPad` を残し、新たに `Mac` を選択します。そうするとダイアログで確認されるので `Enable` をクリック。

<img width="525" alt="image.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2846/c0e91c38-a268-759d-e40e-deb25219ac20.png">

## スキームの追加

<img width="500" alt="image.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2846/685dc54e-7045-b12f-1df2-eafe3758f05a.png">

ターゲットと同様に `${PRODUCT_NAME}Catalyst` を追加。

## Podfileの修正

分割したターゲットに合わせて`Podfile`を修正します。

```diff
target 'Type' do
  use_frameworks!
  pod 'Firebase/Core'
  pod 'Firebase/Storage'
  pod 'Firebase/AdMob'
  pod 'Firebase/RemoteConfig'
  pod 'Firebase/Auth'
  pod 'Firebase/Firestore'
  pod 'Firebase/Messaging'
  pod 'Firebase/Functions'
  pod 'FirebaseFirestoreSwift'
  pod 'Fabric'
  pod 'Crashlytics'
  pod 'SwiftLint'
  pod 'SwiftFormat/CLI'
+
+  pod 'GoogleUtilities/AppDelegateSwizzler'
+  pod 'GoogleUtilities/MethodSwizzler'
+  pod 'GoogleUtilities/Network'
+  pod 'GoogleUtilities/NSData+zlib'
+  pod 'GoogleUtilities/AppDelegateSwizzler'
+  pod 'GoogleUtilities/Environment'
+  pod 'GoogleUtilities/Logger'
+  pod 'GoogleUtilities/UserDefaults'
+  pod 'GoogleUtilities/Reachability'

end
+
+target 'TypeCatalyst' do
+  use_frameworks!
+  pod 'FirebaseCore'
+  pod 'Firebase/Storage'
+  pod 'Firebase/RemoteConfig'
+  pod 'Firebase/Auth'
+  pod 'Firebase/Firestore'
+  pod 'Firebase/Messaging'
+  pod 'Firebase/Functions'
+  pod 'FirebaseFirestoreSwift'
+  pod 'GoogleUtilities/AppDelegateSwizzler'
+  pod 'GoogleUtilities/MethodSwizzler'
+  pod 'GoogleUtilities/Network'
+  pod 'GoogleUtilities/NSData+zlib'
+  pod 'GoogleUtilities/AppDelegateSwizzler'
+  pod 'GoogleUtilities/Environment'
+  pod 'GoogleUtilities/Logger'
+  pod 'GoogleUtilities/UserDefaults'
+  pod 'GoogleUtilities/Reachability'
+end

```

差分としてはAdMob, Fabric, Crashlytics, SwiftLint, SwiftFormatなどのmacOSでは不要なライブラリを外した様な感じです。
そして `pod deintegrate && pod install` を実行。(多分 `pod install` だけでも大丈夫だけど念の為)

**2020/04/12追記**

`GoogleUtilities` 系をそれぞれiOSとmacCatalyst用に追加が必要でした。

**2020/04/12追記終わり**

## ビルドしてみる

先述したスキームを選択した状態で⌘+Bでビルドしてみます。

<img width="824" alt="image.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2846/71c52b4d-c83b-2d9d-7923-e43b7049b56d.png">

早速エラーが出ました。

<img width="1103" alt="image.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2846/9afa06f8-96c5-18b4-7e80-eceed8d863c2.png">

`gRPC+c++` が証明書関連でビルド出来ないと言われるので`Pods` -> `gRPC-C++` から `Signing & Capabilities` で自分のチームを選択。

<img width="468" alt="image.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2846/a8f07f5c-5bf9-7031-129d-699a3cd7fd53.png">

更にビルドしてみる。

<img width="685" alt="image.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2846/49117e3f-a9be-5136-edda-6d3950f649b3.png">

先述したCatalystターゲットでは削除したライブラリが無いので途中でビルドがコケました。
ここから先はTry&Errorで対応していきます。
大抵は

```swift
#if !targetEnvironment(macCatalyst)
import Fabric
import Crashlytics
#endif
```

の様な感じで場合わけをしていくことになると思います。

色々直して更にビルドをしてみます。

<img width="628" alt="image.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2846/f08d1263-c504-cd25-64c9-af51353fdd10.png">

Firebase Analytics辺りでコケた様に見えます。
Xcodeのプロジェクト全体から `FIRAnalyticsConnector` を検索してみます。

<img width="267" alt="image.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2846/0b340815-3b32-0186-6671-39f69b8ee0cc.png">

この中から `Pods-${PRODUCT_NAME}Catalyst.debug.xcconfig` と `Pods-${PRODUCT_NAME}Catalyst.release.xcconfig` を修正します。
`FIRAnalyticsConnector` 、 `GoogleAppMeasurement` 、 `FirebaseAnalytics` 、 `FirebaseAnalyticsInterop` 辺りはmacOSに不要なので検索して諸々削除します。

```diff
- "${PODS_ROOT}/GoogleAppMeasurement/Frameworks"
- "${PODS_ROOT}/FirebaseAnalytics/Frameworks"
- "${PODS_ROOT}/Headers/Public/FirebaseAnalyticsInterop"
- -iframework "${PODS_ROOT}/FirebaseAnalytics/Frameworks"
- -iframework "${PODS_ROOT}/GoogleAppMeasurement/Frameworks"
- -framework "FIRAnalyticsConnector"
- -framework "GoogleAppMeasurement"
- -framework "FirebaseAnalytics"
```

抜粋するとこんな感じ。

**2019/10/20追記**

この記事を見た人からCatalystでは`pod 'Firebase/Core'`ではなく `pod 'FirebaseCore'`を利用すると良いと情報をいただきました。
その様にすると、開発版ではこの部分の対応が不要になりました。
但し、Archiveをすると `GoogleUtilities` が2度呼ばれるエラーが発生します。
下記の記事のWorkaroundを対応すると回避出来ます。

- [GoogleUtilities installed twice, impossible to archive app on Xcode 11.x · Issue #4087 · firebase/firebase-ios-sdk](https://github.com/firebase/firebase-ios-sdk/issues/4087)

**2019/10/20追記終わり**

対応が済んで更にビルドしてみる。
`Run script` でコケたのでCrashlytics、SwiftLint、SwiftFormatなどを削除。

<img width="894" alt="image.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2846/66abc60f-ee19-0f4f-eb76-6065b5761f91.png">
![Artboard.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2846/5f4fd823-ee45-1a05-0075-09b8aea71df6.png)

**さすがに疲れてきたので半ば諦めながら再ビルド...**

<img width="334" alt="image.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2846/0306a035-f732-3d9d-ed70-e85a0778945b.png">

🎉

<img width="790" alt="image.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2846/ebf61a6c-e14f-7051-4a3e-f5c4addc9b57.png">

無事ビルドが出来たので色々触ってみた所、色々修正は必要そうなので諸々対応していきました。

## オチ

そろそろ一旦Test Flightにでも上げたいなという所まで対応して、ローカル環境でArchiveを試みた所...

😱
<img width="957" alt="image.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2846/5269714c-5db3-2134-cdcb-8c1433ddeea1.png">

gRPCのライブラリでヘッダーファイルが見つからないとエラーが出てしまいました。
こちらはFirebaseでもGitHub Issuesにあがっていました。

- [gRPC-Core file not found (Mac Catalyst) · Issue #4007 · firebase/firebase-ios-sdk](https://github.com/firebase/firebase-ios-sdk/issues/4007)
- [gRPC-Core fails to archive for Catalyst · Issue #20500 · grpc/grpc](https://github.com/grpc/grpc/issues/20500)

## 補足(CocoaPods以外のライブラリについて)

元々Carthageも利用していましたが、Xcode 11 betaの段階でSwift Package化していたので、今回ほとんど対応不要でした。
(一部[kishikawakatsumi/KeychainAccess](https://github.com/kishikawakatsumi/KeychainAccess)がCatalystに対応していなかったので今回は削除して進めました)

**2020/04/12追記**

[kishikawakatsumi/KeychainAccess](https://github.com/kishikawakatsumi/KeychainAccess)もCatalyst対応されていたのでバージョンを上げるだけで利用できました。

**2020/04/12追記終わり**

# まとめ

残念ながらArchiveに失敗し、Test Flightにアップロードが出来なかったのですが、開発環境ではビルドが成功したので時間を見て必要な対応があれば進めていこうと思います。
[github.com/grpc/grpc](https://github.com/grpc/grpc)をウォッチして、Archiveが出来る様になるまでは[# Type](https://type-markdown.app)のmacOS化対応は様子見ですね。。
とほほ。。

もし、[# Type](https://type-markdown.app)のmacOS化をお待ちの方がいましたらもう暫くお待ちください🙇🏻‍♂️
という事でCatalystに成功しなかったので記事のタイトルは `[WIP]` のままです。

## 追記: 2020/04/12

gRPCのIssueのコメントにあるスクリプトを実行しながらArchiveを実行すれば最後までArchiveが可能になりました。
少し手直しをしたものがこちらです。

```sh
#!/usr/bin/env bash

# Semiautomatic workaround for the issue described in
# https://github.com/grpc/grpc/issues/20500

PRODUCT_NAME="Type"

trap "exit" INT

while true; do
    sleep 0.1
    _boring_ssl=$(find ~/Library/Developer/Xcode/DerivedData/${PRODUCT_NAME}* \
        -name "BoringSSL-GRPC" | head -n1)

    cd "$_boring_ssl/openssl_grpc.framework" && ln -s Versions/Current/Headers Headers &&
        echo "Patched openssl_grpc"

    _grpc_core=$(find ~/Library/Developer/Xcode/DerivedData/${PRODUCT_NAME}* \
        -name "gRPC-Core" | head -n1)

    cd "$_grpc_core/grpc.framework" && ln -s Versions/Current/Headers Headers &&
        echo "Patched grpc"
done

```


`PRODUCT_NAME` をご自身のアプリ名に合わせれば利用可能かと思います。
これを持って長い間付いていた `WIP` を外します。