制約されたネットワークでローカルPush接続性を構築する – WWDC2020

Session概要

ローカルプッシュ接続性を活用し、Appサーバからネットワーク上のデバイスへ、インターネット接続の無い状況でも通知を配信できます。
制約されたネットワーク環境で実行するApp向けに通知を構築する方法、期待されるインターネット接続時と同様の信頼性と体験を提供しコミュニケーションを継続するための通知を構築する方法について。
この技術を利用するためにAppでどのように実装するかも説明します。 https://developer.apple.com/videos/play/wwdc2020/10113/

ローカルプッシュについて

  • デバイスがインターネットに接続していなくても利用可能

iOS 14で追加された新しいAPI

  • Wi-Fiネットワークで動作する独自のPush接続サービスの作成を可能にする

Push通知の構成

  • プロバイダサーバからApple Push通知サービス(APN)へプッシュ通知依頼を送ります
  • APNは対象デバイスに対して対応する通知ペイロードを送信する
    • 通知タイプによってペイロードを受け取るためにPushKitかUserNotificationsのいずれかのフレームワークを使用する
  • ただし、オフライン環境ではPush通知が送れない
    • ローカルPush接続はこの問題を解決するために設計されたソリューション
    • iOS 14ではローカルPush接続APIが追加された
      • NetworkExtensionの一部

ローカルPushの接続性

  • ローカルPush接続APIにより、プロバイダサーバと通信するアプリケーション拡張機能を構築することが出来る
  • APNはないので、プロバイダサーバとアプリケーション拡張の間で通知を届ける
    • プロトコルを独自で定義する必要がある
  • 有効にするWi-Fiネットワークをアプリケーションで指定する必要があり、接続するとシステムがアプリケーション拡張機能を起動する。
    • アプリケーション拡張機能はプロバイダサーバとのネットワーク接続を維持し、通知を受け取る責務を負う
    • 指定されたWi-Fi環境に接続している限り、アプリケーション拡張機能はバックグラウンド実行し続ける
    • 反対に指定されたWi-Fi環境の接続を失うとシステムはアプリケーション拡張機能を停止する
  • ローカルPush接続のユースケース
    • ネットワーク環境が限定されている
    • そのネットワーク環境におけるユーザの要求によって定義される
  • 新しい権限を申請する必要がある



ローカルPush Connectivity API

  • NEAppPushManager
    • 親アプリは NEAppPushManagerのインスタンスを使用して設定を作成し、ローカルPushが動作するWi-Fiネットワークの情報が含まれている
      • 設定を読み込み、保存、削除 可能
    • VoIPアプリを実装する場合は、デリゲートを設定するためにも使用する
  • NEAppPushProvider
    • 親アプリとアプリケーション拡張機能はこの権限を持っている必要がある
    • アプリケーション拡張機能のライフサイクルを管理し、起動時と終了時にシステムに呼び出される

Sampleコード

NEAppPushManagerの設定
import NetworkExtension

let manager = NEAppPushManager()

// 有効にしたいSSIDの範囲を指定
manager.matchSSIDs = [ "Cruise Ship Wi-Fi", "Cruise Ship Staff Wi-Fi" ]

// アプリケーション拡張機能のバンドルID
manager.providerBundleIdentifier = "com.myexample.SimplePush.Provider"
manager.providerConfiguration = [ "host": "cruiseship.example.com" ]
manager.isEnabled = true

// 実行後、システムはこの設定を使ってローカルPush接続を有効化する
manager.saveToPreferences { (error) in
    if let error = error {
        // Handle error
        return
    }
    // Report success
}
アプリケーション拡張機能のライフサイクル実装
// Manage App Extension life cycle and report VoIP call 

class SimplePushProvider: NEAppPushProvider {

    override func start(completionHandler: @escaping (Error?) -> Void) {
        // Connect to your provider server       
        completionHandler(nil)
    }

    override func stop(with reason: NEProviderStopReason,
                                   completionHandler: @escaping () -> Void) {
        // Disconnect your provider server
        completionHandler()
    }
    
    func handleIncomingVoIPCall(callInfo: [AnyHashable : Any]) {
        reportIncomingCall(userInfo: callInfo)
    }
}
親アプリ側でVoIPを受け取るための実装
class AppDelegate: UIResponder, UIApplicationDelegate, NEAppPushDelegate {
    func application(_ application: UIApplication,
                     didFinishLaunchingWithOptions launchOptions:       
                     [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
        NEAppPushManager.loadAllFromPreferences { (managers, error) in
            // Handle non-nil error
            for manager in managers {
                manager.delegate = self
            }
        }
        return true
    }
    
    func appPushManager(_ manager: NEAppPushManager,
                didReceiveIncomingCallWithUserInfo userInfo: [AnyHashable: Any] = [:]) {
        // Report incoming call to CallKit and let it display call UI
    }
}
着信状態を受け取れるようにするには、アプリのcapabilityで Voice over IP バックグラウンドモード有効にしている必要がある
最新情報をチェックしよう!