はじめに
アプリからHTTPリクエストで行動ログを送信し、APIサーバがCookieから端末を識別するための情報(端末ID)を取得して、行動ログと端末を紐づけて分析基盤に記録するということをしています。
あるとき、アプリとアプリエクステンションとで、行動ログに紐づく端末IDが異なっていることに気づきました。
正しく分析を行うためには、アプリとアプリエクステンションのどちらから送信される行動ログにも同じ端末IDを付与する必要があります。
原因と対応方法がわかったので、以下にまとめます。
アプリとアプリエクステンションはそれぞれ別々のCookieストレージを持っている
アプリがAPIサーバに初めてアクセスしたとき、APIサーバが端末IDを発行し、それをSet-Cookieヘッダーの値としてアプリに返す仕組みになっています。
アプリは次回以降のアクセスでそのCookieをリクエストに付与するので、リクエストを端末と紐づけて記録することが出来ます。
ところが、アプリとアプリエクステンションはそれぞれ別々のCookieストレージを持っているため、アプリが端末IDのCookieをすでに保持していたとしても、アプリエクステンションがAPIサーバにアクセスするときそのCookieは付与されないので、APIサーバは新しい端末IDを発行します。
その結果、アプリとアプリエクステンションとで、異なる端末IDのCookieが送信されることになります。
HTTPCookieStorageのドキュメントに以下の説明があります。
iOS — Each app and app extension has a unique data container, meaning they have separate cookie stores. You can obtain a common cookie storage by using the sharedCookieStorage(forGroupContainerIdentifier:) method.
共有Cookieストレージを使ってアプリとアプリエクステンションでCookieを共有する
前述のドキュメントに以下の説明があります。
You can obtain a common cookie storage by using the sharedCookieStorage(forGroupContainerIdentifier:) method.
HTTPCookieStorage.sharedCookieStorage(forGroupContainerIdentifier:)
を使用することでCookieを共有することができるとのことなので、やってみましょう。
例えばアプリ起動直後にAPIサーバにHTTPリクエストを送信するとして、そのレスポンスに端末IDのCookieが含まれるとしましょう。
まずはこのCookieを共有Cookieストレージに保存します。
let urlRequest = URLRequest(url: URL(string: "https://api.example.com/xxx")!)
let (data, response) = try await URLSessoin.shared.data(for: urlRequest)
guard let response = response as? HTTPURLResponse else { return }
guard let headers = response.allHeaderFields as? [String: String],
let url = response.url
else { return }
// HTTPレスポンスヘッダーからHTTPCookieインスタンスを生成する
let cookies = HTTPCookie.cookies(withResponseHeaderFields: headers, for: url)
// 共有Cookieストレージにdevice_idという名前のCookieを保存する
if let cookie = cookies.first(where: { $0.name == "device_id" }) {
HTTPCookieStorage.sharedCookieStorage(
forGroupContainerIdentifier: "group.com.example.App"
)
.setCookie(cookie)
}
次に、アプリエクステンション側でこのCookieを利用できるようにします。
単に共有Cookieストレージに保存しただけではアプリエクステンションが送信するHTTPリクエストにそのCookieは付与されません。
共有Cookieストレージからdevice_id
という名前のCookieを取り出し、アプリエクステンション用のCookieストレージに保存します。
if let cookies = HTTPCookieStorage.sharedCookieStorage(
forGroupContainerIdentifier: "group.com.example.App"
).cookies {
if let cookie = cookies.first(where: { $0.name == "device_id" }) {
HTTPCookieStorage.shared.setCookie(cookie)
}
}
これで、アプリエクステンションが送信するHTTPリクエストにdevice_id
のCookieが付与されるようになります。