iOS 10.3 から、アンインストールすると Keychain が削除される → されませんでした

顛末

  1. iOS 10.3 beta でアンインストールすると keychain が削除される問題が発見される
  2. これはバグか?という議論がなされる中、 Apple スタッフから 「これは仕様だ」とコメントが入る
  3. しかし iOS 10.3 beta7 でアンインストールしても keychain から消えない、元の仕様に戻った
  4. iOS 10.3 Public でもアンインストールしても keychain は消えない ← イマココ!

以下 keychain 削除について

nextstep.fm by nextstep.fm on iTunesiOS 10.3 について話されていて、どうやらアプリをアンインストールすると Keychain が削除されるらしい。
これはバグでそうなっている可能性もあるらしく、修正されるかもしれない。
→ 仕様とのこと。
iOS 10.3 beta7 からアンインストールしても消えなくなったとのこと…。

forums.developer.apple.com

友達の友達から聞いた情報によると、 iOS 11 からアンインストールで keychain が消えるらしいが、ググってもそれらしいのは見当たらなかったのでガセかもしれない。
判明したらまた追記します。

動作

  • いままで
    • アプリをアンインストールしても Keychain のログイン情報は残ったまま。
    • つまり再インストールすると前回のログイン情報を引き継ぐ。
      • (再インストール後の初回起動時に Keychain を削除することで引き継がせないアプリもある)
  • これから
    • アプリをアンインストールすると Keychain も消える。
    • 再インストールしても前回のログイン情報を引き継がなくなったため、常に初回起動と同じ動作になる
    • ただし、 keychain が他のアプリと共有されている場合、他のアプリも消さないと keychain は消えない。

バグの可能性

Keychain の変更は iOS 10.3 の仕様に記載されている事項ではなく、今後の 10.3 正式リリース時までに削除される可能性があると話されていた。
まあ beta 版での話なので、杞憂に終わればそれで良い。

コメントで教えていただいた、これは仕様とのこと。
→上記に書いた通り、仕様になったが beta7 からまた元に戻った。

https://forums.developer.apple.com/thread/72271 このスレッドに Apple スタッフからの回答がありましたよ。

This is an intentional change in iOS 10.3 to protect user privacy. Information that can identify a user should not be left on the device after the app that created it has been removed. It has never been a part of the API contract that keychain items created by an app would survive when the app is removed. This has always been an implementation detail. ユーザーのプライバシーを守るための意図的な変更とのことです。

対策

開発者的に何かすることはおそらくないと思う、常に初回起動と同じ挙動になるだけ。(Analytics のトラッキングが正常に動作しない可能性はある?)

ただ、「再インストール」というのを特定するために Keychain を使う必要があったが、それができなくなる。
アプリの再インストールを検出する - Qiita

一応 iOS10.3 以上とそれ以下で実装を気をつけたほうがよさそう。

参考

iOS 10.3 Beta 2 autodeletes keychain items afte... | Apple Developer Forums
iphone - iOS 10.3 beta 3 doesn’t persist data of KeychainItem - Stack Overflow

fastlane の increment_build_number で Cannot find が発生した時の対処法

エラー内容

fastlane で increment_build_number を使ったら以下のようにエラーが出て increment されなかった。

[14:33:08]: ------------------------------------
[14:33:08]: --- Step: increment_build_number ---
[14:33:08]: ------------------------------------
[14:33:08]: $ cd /Users/kensuke/Documents/xcode/hoge && agvtool next-version -all && cd -
[14:33:09]: ▸ Setting version of project hoge to:
[14:33:09]: ▸ 5.
[14:33:09]: ▸ Also setting CFBundleVersion key (assuming it exists)
[14:33:09]: ▸ Updating CFBundleVersion in Info.plist(s)...
[14:33:09]: ▸ $(SRCROOT)/hoge/SupportingFiles/appStaging.plist
[14:33:09]: ▸ Cannot find "$(SRCROOT)/hoge/SupportingFiles/appStaging.plist"
[14:33:09]: ▸ $(SRCROOT)/hoge/SupportingFiles/appDev.plist
[14:33:09]: ▸ Cannot find "$(SRCROOT)/hoge/SupportingFiles/appDev.plist"
[14:33:09]: ▸ Updated CFBundleVersion in "hoge.xcodeproj/../appTests/Info.plist" to 5
[14:33:09]: ▸ Updated CFBundleVersion in "hoge.xcodeproj/../hoge/SupportingFiles/Info.plist" to 5

発生状況

TARGETS を app, appStaging, appDev, appTests のように分離していて、 Info.plist が 4 つできている状態になっていた。

対処法

Xcode⌘ + Shift + f を押して INFOPLIST_FILE で検索する。
そうすると $(SRCROOT)/hoge/SupportingFiles/appStaging.plist などと書いてあるところが出てくるので、そこの $(SRCROOT)/ を削除する。

hoge/SupportingFiles/appStaging.plist なるようにする。

結果

[14:41:41]: ------------------------------------
[14:41:41]: --- Step: increment_build_number ---
[14:41:41]: ------------------------------------
[14:41:41]: $ cd /Users/kensuke/Documents/xcode/hoge && agvtool next-version -all && cd -
[14:41:42]: ▸ Setting version of project hoge to:
[14:41:42]: ▸ 6.
[14:41:42]: ▸ Also setting CFBundleVersion key (assuming it exists)
[14:41:42]: ▸ Updating CFBundleVersion in Info.plist(s)...
[14:41:42]: ▸ Updated CFBundleVersion in "hoge.xcodeproj/../appTests/Info.plist" to 6
[14:41:42]: ▸ Updated CFBundleVersion in "hoge.xcodeproj/../hoge/SupportingFiles/Info.plist" to 6
[14:41:42]: ▸ Updated CFBundleVersion in "hoge.xcodeproj/../hoge/SupportingFiles/appStaging.plist" to 6
[14:41:42]: ▸ Updated CFBundleVersion in "hoge.xcodeproj/../hoge/SupportingFiles/appDev.plist" to 6

参考

fastlane cannot update project version · Issue #329 · fastlane/fastlane

iPad 向けアプリで iTunesConnect 提出時に ITMS-90029 が発生した時の対処法

設定

iPad 向けになってる。

f:id:star__hoshi:20170214102719p:plain

iTunesConnect に申請してみる

iPad 向けにしてるのに iphone の storyboard がウンヌンって言われてる。

f:id:star__hoshi:20170214102555p:plain

Info.plist 見てみる

邪魔そうなのがいる… 👀

f:id:star__hoshi:20170214103008p:plain

消してみる

もう一回申請すると

エラーが消えました 🙌

Swift 実践入門 を読んだ

Swift実践入門 ── 直感的な文法と安全性を兼ね備えた言語 (WEB+DB PRESS plus)

Swift実践入門 ── 直感的な文法と安全性を兼ね備えた言語 (WEB+DB PRESS plus)

全体感想

良い本だった、書名になっているが初心者向けではなく実践入門なので、実践的な内容が多くためになる。
だいたい 16 時間くらいで読みおわった。

普段ゆるふわで書いているとクロージャジェネリクスなど使いこなせていないし、非同期処理なども注意がおそろかになりがちだし、循環参照とかも起きてしまう。
そこらへんちゃんと理解してちゃんと書いていこうという内容であるし、この本を読みながら既存のアプリをリファクタリングしていきたい。

読み終わったら終わりな本ではなくて、手元に置いておいて Swift 書きながら読み返して体に染み込ませていくのがいいのかなと思いました。

石川さんから『Swift 実践入門』を頂いたので、見所などを綴ってみました。 にまともな書評がある。

以下メモ

1章 Swift はどのような言語か

ざっくり Swift こんな言語だよ〜って説明。
静的型付けで Optional 良いよねとかそんな話。
OSS にもなってるよ、コマンドラインとかからでも実行できるよ〜みたいな内容。

2章 変数、定数と基本的な型

変数とか定数、基本的な型(String, Int, Optional など…)の話。
強制アンラップはやべーよなとか、キャストとかも書いてある、ほとんど知ってたけど Swift 書くなら一度読んでおいた方が良い内容。

メモ

  • 定数は宣言時に値を代入しなくても、宣言した後に1度代入されるように書かれていれば問題ない。
  • String の n 文字目を取得するのは characters 型の offetBy を使う、ここら辺は面倒だ。
  • command + control + ? (英字keyの場合は command + control + shift + / ) で型情報見れる。
    • 知らなかった、便利だ。今まで画面右の Quick Help で見てた。
    • 長いのでエイリアス当てておきたい

3章 制御構文

if, switch, guard などのはなし、深く実践的な内容が書かれている。

メモ

  • if case 1...10 = value みたいに書く。この書き方いつも忘れる。
  • switch casewhere はあまり使ってない、というか存在を忘れていたので今後使っていこう。
  • switch の default を書いちゃうと今後増えた時に全部 default 入っちゃってよくないから default は極力書かないようにしよう。
  • for-case in という書きかた普通に知らなかった、便利だ。
  • while は嫌いだから俺は使わない
  • defer は遅延処理
  • enum はもっとうまく使わないとなあ

4章 関数とクロージャ

関数は他の言語と大体同じ。
クロージャは使いこなせておらず知らいことが多い、めっちゃ便利だがそれなりに難しく、後3回くらい読み直したい。

メモ

  • インアウト引数はあまり使い慣れない
    • 関数先で渡した引数が更新されるのわかりにくくない?
  • クロージャは1行の時だけ return 省略できる
  • キャプチャ
    • クロージャ内部の変数を保持することができる。
    • 用途によって色々使えそう。
  • 関数の引数にクロージャ渡すとかめっちゃ便利そうだが複雑になって脳内メモリ足らなくなる

5章 型の構成要素

class, struct, enum で利用出来るプロパティやメソッドの説明。
今まで雰囲気で Swift 書いてきた人間はこの章で死ぬ。

メモ

  • プロパティオブザーバ
    • willSet, didSet あたりあまり使いこなせてない
  • 失敗可能イニシャライザ使いこなせたら強そう
  • subscript 全く使いこなせてない
    • コレクション要素にアクセスできるやつ、 array[0] の [0] の所とか
  • UIAlertController の initializer の所良い、真似しよう。

6章 型の種類

class, struct, enum の説明。
class は参照型、struct は値型で、参照型は意図せぬ値の変更などが起きうるのでなるべく値型を使い、参照の共有が必要な時だけ参照型を使おう。

メモ

  • クラスは参照型で継承が可能、多重継承は出来ない
    • final つければ継承やオーバーライドできなくなる
  • クラスメソッドとスタティックメソッドの違いはオレバーライドできるかできないか
  • enum に値を持たせるのを連想値という

7章 プロトコル

他の言語でいうインターフェースに近い。
100回読みたい。

メモ

  • associatedtype つかえばいま開発中のアプリのやばいところ綺麗に書けそう
  • プロトコルエクステンションにwhereで型制約つけると、その型の時だけ有効になるエクステンション作れる
  • Equatable で URL の一致書こう

8章 ジェネリクス

100回読みたい。

メモ

  • ジェネリック型に具体的な型を与えることを特殊化という。
    • Optional に String とか渡すのもそう。
  • ジェネリクス + 型安全で最高のプログラミング

9章 モジュール

配布可能なプログラム形式(import するやつ)、フレームワークとかを作ろうって話。
普通にプログラム書いてる分には Framework 作ることはないかなと思う。

メモ

  • ドキュメントコメント
    • // や / / は普通のコメント
    • /// や /* / はドキュメントコメント
      • ドキュメントコメントでは Markdown が書ける。

10章 型の設計指針

クラスより構造体を使うべき。
ただしクラスの方が向いている処理もあるので、状況を見て判断すること。

メモ

  • Array や Dictionary はサイズがでかいかもしれないから、それをコピーする時はコピーオンライトって仕組みがあって、必要になるまでコピーされないようになっている。
  • クラスは、参照の共有やインスタンスのライフサイクルに合致した場合などに有用なので、そういう時に使うこと。
  • クラスの継承よりも protocon-extension に優位性があるので、なるべく protocol-extension-struct を使うこと。
  • protocol-extension ではストアドプロパティの保持ができないため、そういう時は class を使う。

11章 イベント通知

UI のタップとかのイベントをどう取り扱うか。 デリゲート、クロージャ、オブザーバパターンなどある。

循環参照について書かれている、100回読みたい。

メモ

  • ボタンタップとかの命名は didSelect などにするのが良さそう
  • delegate は weak にして弱参照にすること
  • クロージャのキャプチャリストを使うときは weak や unowned をつけ弱参照としないと循環参照おきる
    • weak はアクセス時に nil でも死なないが unowned だと実行時エラー起きる
  • self を使うと循環参照する -> 今作っているアプリのあそこ循環参照してる気がする
  • typealias でクロージャの型を作れる

12章 非同期処理

マルチスレッドのはなし。
スレッドを使うにはGCD、OperationQueue、 Thread のどれかを使う。
Thread を使うケースは稀で、簡単な非同期は GCD、複雑な非同期は OperationQueue を使うのが良さそう。

めちゃくちゃためになるというか、真面目に考えないといいアプリにならないがなかなか難しい。
スレッド書くときは読みながら書きたい。

メモ

  • GCD は CPU の負荷などを見ていい感じにスレッド使ってくれる。
    • この手法をスレッドプールという。
    • GCD はシンプルな非同期処理の実現に向いている
      • サブスレッドで実行しその結果をメインスレッドで表示したいとか
      • タスクのキャンセルとかは向いていない
  • Operation, OperatioQueue クラス
    • 内容がだいぶ複雑で頭に入ってこない
  • Thread クラス
    • 継承してクラスそのものを thread にできる

13章 エラー処理

エラーハンドリング。
Optional, Result<T, Error>, do-catch, try, fatalError, アサーションなどの使い方や使い分けなど。

最後にどういういう時にどのエラーを使うべきかという流れが書いてあって便利。

メモ

  • Result<T,Error> はエラーのパターンを判別しハンドリングしたい時に使う
  • try! は危険のように思えるが、失敗したらしゃーないって意図がある
  • do-defer を使うと失敗した時に必ずずっこうしたい処理が書けて良い。
  • do-catch はエラーが起きたかというので取れるので、 Result よりも幅が広く、ネストしたエラーも do-catch であればネストせず行ける。
  • do-catch は エラー処理を強制させる
  • fatalError は想定されていないケースに使う。
    • fatalError が呼ばれることこそが問題だからアプリを落とすという判断
    • fatalError は Never 型を返す。
      • Never 型は空ではなく、そこでプログラムの実行をやめるという意味で何も変えさない
  • アサーション
    • これが有効なのはデバッグ時のみで、リリース時には影響しない

14章 実践的な Swift アプリケーション

API Client を作っていく。
APIKit を作っていく感じ。
Protocol + Extension で抽象的なプログラムを書くの楽しい。

全部写経した。
starhoshi/GitHubSearchRepository: Swift 実践入門

15章 Swift から Objective-C を利用する

Objective-C は実績のある言語だから、過去に書かれた ObjC を Swift で書き直していくにはどうすべきか?みたいな話。
Swift から ObjC の参照の仕方や、 ObjC もちゃんと書いてないと Swift から利用するのはむしろ逆に辛いかもしれないので、 ObjC もちゃんと書こうという話がされてる。

そもそも ObjC 全然詳しくないので勉強にはなるけど、正直 ObjC に努力値振る暇あったら Swift の知見深めたいところではある。

メモ

  • Swift から ObjC を使うときは Bridging-Header を使う
    • Swift 1 のときはライブラリ使うために必要だったな :relaxed:
  • ObjC のランタイムを使うと動的にメソッドの入れ替えなどが可能になるらしい
  • Swift から ObjC を使うにしても、漫然と書かれた ObjC は危険がたくさんなので、 ObjC も気をつけて書いた方が良い
    • ライトウェイトジェネリクス, nullableなど使っていこう
    • id 型はやめろ
  • ObjC から Swift も利用できるけど、制限が多いのでメリットはあまりない

JetBrains の Opensource license を申請したら承認された

JetBrains :: Opensource license

JetBrains のエディタは普通に使おうとするとお金がかかるのだが、 OSS を開発している人は無料でライセンスがもらえる。(もらえる条件はもうちょっと色々ある)
ライセンスをもらうには申請が必要なのだが、その申請が通りライセンスをもらうことができた。

ただし、このライセンスは OSS の開発にのみ使って良いライセンスのため、商用アプリケーションをしたい場合は別途ライセンス必要の支払いが必要になる。

申請方法

https://www.jetbrains.com/shop/eform/opensource?product=ALL

ここに自分のつくっている OSS を入れていくだけ。
私はたいした OSS はつくってないけど、 starhoshi/pi-chan2 という esa.io の iOS クライアントアプリで申請したら通った。
入力フォームは特に考えるそれっぽくいい感じに埋めていっただけ。

star 2 しかないけど通ったので、興味ある人はやってみると良いと思う。

Firebase Analytics だけでモバイルアプリの解析ができるか検討

Firebase

Google がやってるやつ。 Push 受けたり、クラッシュレポート取ったり、 Analytics 取れたりしてて、モバイルでは Google Analytics より Firebase Analytics 使ったほうが良い空気を Google から感じる。

また、Fabric + Crashlytics も買収して、もうモバイルアプリはこれに依存していく以外の選択肢が見つからない。

結論

全面的に信頼するのはまだ早いので、 GA と併用しつつ Firebase でもデータをためてどう使っていくか検討していく。

Analytics

デフォルト設定でここまで取れてすごい。無料である。

f:id:star__hoshi:20170126113146p:plain

Screen Transition

半年前は取得できなかった画面遷移が、現在は取れるらしい。と言うか、自動で取ってくれているらしい、すごい!
と思いきや、取得はされているようだが画面上では見れないらしい… 課金すれば見えるのかも?

画面遷移を見たい場合はとりあえず Google Analytics 使わないとダメ。

The screen parameters will accompany the events reported to the server but they currently are not displayed on Developer Console yet (at least in my developer console).
swift - Screen tracking data on Firebase Analytics - Stack Overflow

Event

FIRAnalytics.logEventWithName(kFIREventSelectContent, parameters: [
  kFIRParameterContentType:"cont",
  kFIRParameterItemID:"1"
  ])

イベント名とそれに対応するパラメータを送信できる。
デフォルトで用意されているイベントが20くらいあって、基本的にデフォルトイベントを使用した方が良さそう。
(デフォルトだと分析とか多分楽になるのだろう)

kFIREventSelectContent がデフォルトイベント名。ここを login_button_tapped みたいなカスタムイベントにするのも可能。

パラメータ

パラメータにもデフォルトのが用意されている。上記でいうと kFIRParameterContentType が該当する。
カスタムパラメータも使える。ただし、カスタムパラメータを使う場合は Analytics レポートに乗ってこないらしい。

カスタム パラメータ: カスタム パラメータは Analytics レポートには記載されませんが、ユーザーリストの定義でフィルタとして使用でき、あらゆるレポートに適用できます。アプリが BigQuery プロジェクトとリンクされている場合、カスタム パラメータは BigQuery にエクスポートされるデータに含まれます。
イベントのログを記録する  |  Firebase

カスタムイベント + デフォルトパラメータだとどうなるのか?というのは不明。やってみるしかない。
ここら辺は BigQuery で分析することを前提とした空気を感じるので、とりあえずデータを蓄積してどうするかを考えていきたい。

まとめ

入れておくだけでかなりいい感じに全体分析ができるので、絶対に入れたほうが良い。
ただし、画面遷移やイベントなどは実績のある GA で取りつつ、今後 Firebase だけで運用できないか知見をためていく!

Cloudflare で DNS 登録したリクエストのIPアドレスがカンマ区切りになる

heroku + Rails5でアプリケーションを開発し、リクエストを受ける前段に Cloudflare を利用している。
Cloudflare は DNS, Crypto で SSL, CDN の設定を利用している。

しかし、 Cloudflare を使い始めたからか IPアドレスが validation エラーになってしまった。

デバッグして中身を見ていると、IP アドレスが以下のようになっていた。(前半の IP は書き換えてる)

p ip_address = request.env['HTTP_X_FORWARDED_FOR'] || request.remote_ip
# => "100.10.10.150, 103.22.200.116"

カンマの前半が実際のユーザの IP アドレスで、後半が Cloudflare の IP アドレスっぽい。
103.22.200.116 - Japan - Cloudflare - IP address location and data

ユーザの IP アドレスを取得したかったら ip_address.split(",")[0] でいけると思うけど、だいぶ不安だ...。

特定の UITableViewCell だけ separator の線消したい

tableView で この cell だけ separator 消したいよ〜 という時の方法。

ググると、全ての tableView の separator 消して追加したいやつだけ追加するコード書こうとか、全部の tableView の線を消す方法とか出るけど、特定の cell だけ separator 消す というのをしたい。

やり方

import UIKit

class HogeTableViewCell: UITableViewCell {
    static let height: CGFloat = 88

    override init(style: UITableViewCellStyle, reuseIdentifier: String?) {
        super.init(style: style, reuseIdentifier: reuseIdentifier)
    }

    convenience init(_ separatorInsetLeft: CGFloat) {
        self.init(style: .default, reuseIdentifier: nil)
        separatorInset = UIEdgeInsets(top: 0, left: separatorInsetLeft, bottom: 0, right: 0)
    }

    required init?(coder aDecoder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
}

separatorInset の left を画面右端にすると、線が見えなくなる。

使い方

    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        return FooterTableViewCell(view.bounds.width)
    }

これでいける、register nib している場合は init じゃダメだと思うので cell.separatorInset = ... で書けばいけると思う。

RxSwift をやる前にちゃんと Observer パターンを学ぶ

RxSwift やるぞ!と思ったはいいものの、ちゃんと Observer パターンを学んだことがなかったので Swift でゼロから書いてみる。

Observer パターン

観察者が何かを監視していて、その監視対象に更新があった際に観察者に通知が届く仕組み。

Subject が監視対象で、 Observer が観察者。
RxSwift でも PublishSubject とか出てくるし、 Observable とかも Subject に該当するのかな? (まだ RxSwift を理解できてない)

実際に書いてみる

Observer パターン - デザインパターン入門 - IT専科 を参考に Swift にしていく。

1. Observer protocol

観察者のインターフェースの protocol を作る。
id は後述するが、 Observer を削除するときに必要になる key みたいなもの。

protocol Observer {
    var id: String { get }

    // Subject からの通知が届く
    func update(_ string: String)
}

extension Observer {
    func update(_ string: String) {
        print("\(type(of: self)) に届いた新しい値は \(string) です。")
    }
}

2. Subject protocol

observers が mutating なため、 class を継承している。

protocol Subject: class {
    // 観察者リスト
    var observers: [Observer] { get set }

    // 観察者を追加する
    func add(_ observer: Observer)

    // 観察者を削除する
    func remove(_ observer: Observer)

    // 観察者に更新を通知する
    func notify(newString: String)
}

extension Subject {
    func add(_ observer: Observer) {
        observers.append(observer)
    }

    func remove(_ observer: Observer) {
        for (index, registerdObserver) in observers.enumerated() {
            // observer.id が登録済みだった場合削除する
            if registerdObserver.id == observer.id {
                self.observers.remove(at: index)
            }
        }
    }

    func notify(newString: String){
        print("新しい値 \(newString) を observer に通知します。")
        observers.forEach { observer in
            observer.update(newString)
        }
    }
}

3. 具象クラス

Observer は id を UUID で作成して重複しないようにしている。

final class SubjectA: Subject {
    var observers: [Observer] = []
}

final class ObserverA: Observer {
    var id = UUID().uuidString
}

final class ObserverB: Observer {
    var id = UUID().uuidString
}

(本当はシステム的に UUID を id にするようにしたほうがよさそうだけどサボる)

4. 実際に動かす

observerA と observerB が subjectA を監視する。
途中で observerB が監視をやめる。

let subjectA = SubjectA()
let observerA = ObserverA()
let observerB = ObserverB()

subjectA.add(observerA)
subjectA.add(observerB)

subjectA.notify(newString: "New!")
// -> 新しい値 New! を observer に通知します。
// -> ObserverA に届いた新しい値は New! です。
// -> ObserverB に届いた新しい値は New! です。

subjectA.remove(observerB)
subjectA.notify(newString: "New!!!!!!!!!!!!!!!")
// -> 新しい値 New!!!!!!!!!!!!!!! を observer に通知します。
// -> ObserverA に届いた新しい値は New!!!!!!!!!!!!!!! です。

想定通りの動作になった。

全コード

Gist - Swift observer pattern.

感想

実際に書いてみて、 Subject や Observer などの関係性などが理解できてよかった。

とはいえ Observer パターンはそんなに難しくないしゼロから書くほど価値はあまりないしちょっと時間の無駄だった感ある、KVO でやればこんな書かなくて良いしな!!!!!!!

Google Analytics と Firebase を同時に使うとき、 1 つの GoogleService-Info.plist で管理する

GoogleService-Info.plist

何も考えずに Google Analytics と Firebase をアプリに導入しようとすると、それぞれ別で Project を作成して、GoogleService-Info.plist も 2 つ生まれてしまう。
2 つあったとしても読み込む plist はそれぞれ設定できるので問題ないといえばないが、管理などがややこしくなる。

それを 1 つで管理する方法があるので、その方が便利。

ここでは、 iOS アプリに GA と Firebase を組み込んでいきます。

Google Project の作成

iOS アプリにアナリティクスを追加する  |  iOS 向けアナリティクス  |  Google Developers

この手順の「設定ファイルを取得する」項に 設定ファイルを取得 ボタンがあるので押す。

f:id:star__hoshi:20170109093051p:plain

そうすると、 「Create or choose an app」 画面が開く。

Create or choose an app

アプリ名、 bundle id など入れていく。
入れたら Choose and configure services を押す。

f:id:star__hoshi:20170109093202p:plain

Choose and configure services

Analytics を選択し、 Property を作成にして ENABLE ANALYTICS SERVICE を押す。

f:id:star__hoshi:20170109093427p:plain

Generate Configure Service は無視して画面を閉じて良い。

Firebase でインポート

(Firebase に登録していない人は登録が必要。)

Firebase Console を開いて、中央右らへんにある GOOGLE プロジェクトをインポート を押す。

追加ダイアログの選択肢で先ほど作成した sample を選択して FIREBASE を追加 する。

f:id:star__hoshi:20170109093943p:plain

追加すると Firebase Project に追加される。

設定ファイルのダウンロード

左上の歯車から プロジェクトの設定 を選択し、 GoogleService-Info.plist を DL する。

f:id:star__hoshi:20170109094208p:plain

設定ファイルを確認

Firebase からダウンロードした設定ファイルでも、 GA の TRACKING_ID が挿入されている。🎉

...略...
    <key>TRACKING_ID</key>
    <string>UA-44007234-21</string>
...略...

あとは GA も Firebase も普通の手順でやれば OK!!!