iOSの最新リリースであるiOS 13.5は、Exposure Notification APIのベータサポートを含んでいる。これは接触追跡アプリを有効にするためにAppleがGoogleと共同で定義したものである。Appleは、また、接触追跡アプリのベストプラクティスを紹介するサンプルアプリも公開した。
実際のフレームワーク実装を提供するだけでなく、iOS 13.5には、ユーザが接触ログにオプトインまたはオプトアウトできる基本的なユーザ向け認証メカニズムが含まれている。Exposure Notification APIを使用するアプリはまだ利用できないため、Exposureのログ設定は無効になる。一部の保健当局によって開発されたサードパーティのアプリをインストールすると、ユーザはオプトインまたはオプトアウトするオプションが与えられる。
接触追跡アプリを作成したいサードパーティが作成を容易にするために、Appleはリファレンスデザインを提供するサンプルアプリを公開した。このサンプルには、フレームワークを使用する際のベストプラクティスを示すだけでなく、中央サーバの応答をシミュレートして診断キーの共有と暴露基準の管理を実装するためのコードも含んでいる。
Appleサンプルアプリは、起動時にユーザがExposureログを有効にしているかどうかを確認し、有効になっていない場合は有効にするように求める。iOSによって管理される他の特権で通常発生することとは異なり、Exposure Notification APIは、アプリがENManager
シングルトンを介して承認メカニズムを明示的にトリガするメカニズムを提供する。
static func enableExposureNotifications(from viewController: UIViewController) {
ExposureManager.shared.manager.setExposureNotificationEnabled(true) { error in
NotificationCenter.default.post(name: ExposureManager.authorizationStatusChangeNotification, object: nil)
if let error = error as? ENError, error.code == .notAuthorized {
viewController.show(RecommendExposureNotificationsSettingsViewController.make(), sender: nil)
} else if let error = error {
//...
}
}
}
Appleのサンプルは、ユーザが最初のリクエストで許可を拒否した場合に、サービスを2回有効にするようにユーザに求めるところまで行っている。これは明らかに要件ではないが、これが受け入れられた慣行であるとAppleが考えていることを示唆している。つまり、ユーザの許可を収集するこのような積極的な戦略は、最終的なApp Storeレビュープロセスで拒否される原因とは見なされない。
アプリは、ユーザがオンボーディングしたかどうかを示すフラグ、行った検査に関するデータ、サーバと共有したかどうかなど、ローカルに記録するすべてのデータを保存する。すべてのユーザデータを中央サーバに保存する必要はない。ローカルに保存するとユーザのプライバシが維持されるため、これはExposure Notificationプロトコルの重要な機能である。
ユーザがCOVID-19と確実に診断された場合にのみ、中央サーバと共有することを決定できる。これには、アプリが診断キーのリストを取得する必要がある。これにより、ユーザは毎回明示的な認証を提供し、それをサーバに送信する必要がある:
func getAndPostDiagnosisKeys(testResult: TestResult, completion: @escaping (Error?) -> Void) {
manager.getDiagnosisKeys { temporaryExposureKeys, error in
if let error = error {
completion(error)
} else {
// In this sample app, transmissionRiskLevel isn't set for any of the diagnosis keys. However, it is at this point that an app could
// use information accumulated in testResult to determine a transmissionRiskLevel for each diagnosis key.
Server.shared.postDiagnosisKeys(temporaryExposureKeys!) { error in
completion(error)
}
}
}
}
サンプルアプリはまた、バックグラウンドタスクを使用して、COVID-19診断を受けていないユーザの曝露を定期的にチェックする。バックグラウンドタスク識別子は .exposure-notification
で終わる必要がある。これにより、操作を完了するためにより多くのバックグラウンド時間が自動的に受信される。さらに、タスクを所有するアプリは、実行されていないときに、より頻繁に起動される。バックグラウンドタスクは、アプリの detectExposures
メソッドを呼び出して、ユーザが公開されたかどうかを確認し、自分自身を再スケジュールする。
BGTaskScheduler.shared.register(forTaskWithIdentifier: AppDelegate.backgroundTaskIdentifier, using: .main) { task in
// Notify the user if bluetooth is off
ExposureManager.shared.showBluetoothOffUserNotificationIfNeeded()
// Perform the exposure detection
let progress = ExposureManager.shared.detectExposures { success in
task.setTaskCompleted(success: success)
}
// Handle running out of time
task.expirationHandler = {
progress.cancel()
LocalStore.shared.exposureDetectionErrorLocalizedDescription = NSLocalizedString("BACKGROUND_TIMEOUT", comment: "Error")
}
// Schedule the next background task
self.scheduleBackgroundTaskIfNeeded()
}
最後に、Exposure Notificationフレームワークは、接触が検出されるたびにリスクを推定する方法を提供する。これは、相互作用がいつ発生したか、および検出されたデバイスの近接性に基づいてそれがどのくらい続いたかを考慮に入れている。アプリは、通常サーバから送信される ENExposureConfiguration
オブジェクトを提供することでリスクの推定方法を変更し、最終的に finish
を呼び出してローカルストアを更新し、検索を完了することができる。ENExposureConfiguration
オブジェクトは、最小リスク、感染リスク、接触期間、最後の曝露からの日数などのパラメータをサポートする。
ENExposureConfiguration
オブジェクトは、シングルトン ENManager
の detectExposures(configuration:diagnosisKeyURLs:completionHandler:)
メソッドに渡される。検出された暴露ごとに、アプリは getExposureInfo(summary:userExplanation:completionHandler:)
を使用して追加情報を取得できる:
Server.shared.getExposureConfiguration { result in
switch result {
case let .success(configuration):
ExposureManager.shared.manager.detectExposures(configuration: configuration, diagnosisKeyURLs: localURLs) { summary, error in
if let error = error {
finish(.failure(error))
return
}
let userExplanation = NSLocalizedString("USER_NOTIFICATION_EXPLANATION", comment: "User notification")
ExposureManager.shared.manager.getExposureInfo(summary: summary!, userExplanation: userExplanation) { exposures, error in
if let error = error {
finish(.failure(error))
return
}
let newExposures = exposures!.map { exposure in
Exposure(date: exposure.date,
duration: exposure.duration,
totalRiskScore: exposure.totalRiskScore,
transmissionRiskLevel: exposure.transmissionRiskLevel)
}
finish(.success((newExposures, nextDiagnosisKeyFileIndex + localURLs.count)))
}
}
case let .failure(error):
finish(.failure(error))
}
}
Exposure Notification APIは、iOS 13.5とXcode 11.5が必要である。