WWDC Quick Look 💓 By SwiftGGTeam
The Push Notifications primer

The Push Notifications primer

元の動画を見る

ハイライト

これはプッシュ通知初心者向けの完全なチュートリアルです。セッションは、リモート プッシュとは何か、デバイス トークンが必要な理由、APN の仕組み、クライアントとサーバーにそれぞれプッシュ機能を実装する方法など、最も基本的な概念から始まります。

主要内容

多くのアプリは、オンラインになった後に同じ問題に遭遇します。つまり、ユーザーはアプリを開いていませんが、サーバーはユーザーに関する新しい情報を持っています。レストランには今日の特売情報があり、電子商取引には注文状況があり、コンテンツ アプリには最新のデータがあります。ユーザーによるアクティブな更新に依存すると機会を逃すことになります。また、アプリケーションがバックグラウンドで長時間実行されることに依存すると、システムの電力モデルに準拠しません。

このセッションでは、プッシュ通知を 2 つのカテゴリに分割します。アラート通知は、インタラクティブなリマインダーを表示するために使用されます。アプリケーションが実行されていないときにシステムが表示します。バックグラウンド通知により、アプリケーションに一定期間のバックグラウンド実行時間が与えられるため、ユーザーがアプリケーションを開く前にコンテンツを更新できます。どちらも同じ入り口から始まります。アプリケーションは APN に登録し、デバイス トークンを取得し、そのトークンを独自のプロバイダー サーバーに送信します。

本当に問題となるのは境界です。アラートのプッシュには、コンテキストに応じたユーザー アクション、ペイロードの後に​​許可が必要ですaps辞書はシステム表示を担当し、カスタム データはaps外。バックグラウンドプッシュのみが必要ですcontent-available, ただし、システムによりバックグラウンド実行の数が制限され、バッテリー残量が少ないなどの状況ではアプリケーションが起動できない場合があります。この処理の結果をシステムに知らせるために、各コールバックの最後に完了ハンドラーを呼び出す必要があります。

詳細

APNを登録してデバイストークンを取得する

(02:02) プッシュ リンクの最初のステップは、リモート通知に登録することです。registerForRemoteNotifications()デバイスを APN に登録し、デバイス トークンをアプリケーションに返します。アラート通知については、AppDelegateまだそうでなければならないUNUserNotificationCenterDelegateこれにより、ユーザーが通知をクリックしたときにアプリケーションがコールバックを受信できるようになります。

class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterDelegate {

    func application(_ application: UIApplication,
                     didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
        UIApplication.shared.registerForRemoteNotifications()
        UNUserNotificationCenter.current().delegate = self
        return true
    }
}

キーポイント:

  • UIApplicationDelegateリモート通知登録結果の受信を担当します。
  • UNUserNotificationCenterDelegateアラート通知の対話型処理にのみ関連します。
  • registerForRemoteNotifications()APN 登録プロセスをトリガーします。
  • UNUserNotificationCenter.current().delegate = self通知を開いたときのアプリのコードに戻りましょう。

(02:36) 登録が完了すると、システムは 2 つのコールバックのうち 1 つだけを受け取ります。失敗時にエラーを取得します。成功時にエラーが発生するDataフォーム内のデバイストークン。APN はターゲット デバイスを見つけるためにトークンを必要とするため、セッションではこのトークンをバックエンド プッシュ サーバーに送信することが明示的に必要です。

func application(_ application: UIApplication,
                   didFailToRegisterForRemoteNotificationsWithError error: Error) {
    // The token is not currently available.
    print("Remote notification is unavailable: \(error.localizedDescription)")
}

func application(_ application: UIApplication,
                   didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data) {
     // Forward the token to your provider, using a custom method.
     self.forwardTokenToServer(token: deviceToken)
}

キーポイント:

  • didFailToRegisterForRemoteNotificationsWithError登録失敗パスです。
  • didRegisterForRemoteNotificationsWithDeviceToken登録が成功するまでのパスです。
  • deviceTokenこれはローカル ストレージ用ではないため、プロバイダー サーバーのデバイス テーブルに入力する必要があります。
  • セッションの例では、転送アクションを次のようにカプセル化します。forwardTokenToServer(token:)

03:05)デバイストークン ですData。この例では、まず各バイトを 16 進文字列に変換し、それから文字列に綴ります。URLSessionサーバーに送信されました。

func forwardTokenToServer(token: Data) {
    let tokenComponents = token.map { data in String(format: "%02.2hhx", data) }
    let deviceTokenString = tokenComponents.joined()
    let queryItems = [URLQueryItem(name: "deviceToken", value: deviceTokenString)]
    var urlComps = URLComponents(string: "www.example.com/register")!
    urlComps.queryItems = queryItems
    guard let url = urlComps.url else {
        return
    }

    let task = URLSession.shared.dataTask(with: url) { data, response, error in
        // Handle data
    }

    task.resume()
}

キーポイント:

  • token.map各バイトを反復処理します。
  • String(format: "%02.2hhx", data)バイトを 2 桁の 16 進テキストに変換します。
  • joined()プロバイダーサーバーで共通に使用されるデバイストークン文字列を取得します。
  • URLQueryItem登録リクエストにトークンを入力します。
  • URLSession.shared.dataTaskネットワークリクエストを開始します。
  • task.resume()実際にリクエストを開始します。

通知許可をリクエストする

(03:47) アラート通知にもユーザーの承認が必要です。Session は、承認の要求はコンテキスト アクションの後に行われる必要があることを特に強調しています。この例では、ユーザーが通知を受信する意思を表明したばかりなので、これを購読ボタン内に配置します。

@IBAction func subscribeToNotifications(_ sender: Any) {
    let userNotificationCenter = UNUserNotificationCenter.current()
    userNotificationCenter.requestAuthorization(options: [.alert, .sound, .badge]) { (granted, error) in
        print("Permission granted: \(granted)")
    }
}

キーポイント:

  • UNUserNotificationCenter.current()通知センターを取得します。
  • requestAuthorizationシステム許可ポップアップ ウィンドウが表示されます。
  • .alert.sound.badgeそれぞれリマインダー、サウンド、バッジに対応します。
  • grantedユーザーの選択を表します。
  • 以降の呼び出しでは設定時の状態に戻り、ウィンドウが繰り返しポップアップ表示されることはありません。

アラート ペイロードを整理し、クリックを処理する

(04:43) プロバイダー サーバーから APN に送信されるペイロードは 2 つの層に分割されます。aps辞書は、通知を表示する方法をシステムに指示します。事業分野が置かれているapsさらに、ユーザーが通知をクリックすると、アプリは通知を読み取ります。

{
    "aps" : {
       "alert" : {
            "title" : "Check out our new special!",
            "body" : "Avocado Bacon Burger on sale"
        },
        "sound" : "default",
        "badge" : 1,
   },
    "special" : "avocado_bacon_burger",
    "price" : "9.99"
}

キーポイント:

  • apsシステムによって認識される通知辞書です。
  • alert.title短いタイトルです。
  • alert.bodyは完全なプロンプト テキストです。
  • sound使用"default"デフォルトのサウンドが再生されます。
  • badgeアプリケーションアイコンの下付き文字の絶対値です。
  • specialそしてpriceこれはアプリケーション自身のビジネス データです。

(06:11) ユーザーがアラート通知を開くと、UNUserNotificationCenterDelegatedidReceive response呼ばれます。からの例userInfoビジネスフィールドを取り出し、レストランのスペシャルメニューをショッピングカートに追加して、ショッピングカートインターフェイスを開きます。

func userNotificationCenter(_ center: UNUserNotificationCenter,
                            didReceive response: UNNotificationResponse,
                            withCompletionHandler completionHandler: @escaping () -> Void) {
    let userInfo = response.notification.request.content.userInfo
    guard let specialName = userInfo["special"] as? String,
          let specialPriceString = userInfo["price"] as? String,
          let specialPrice = Float(specialPriceString) else {
        // Always call the completion handler when done.
        completionHandler()
        return
    }

    let item = Item(name: specialName, price: specialPrice)
    addItemToCart(item)
    showCartViewController()
    completionHandler()
}

キーポイント:

  • response.notification.request.content.userInfoペイロード全体を削除します。
  • guardカスタムフィールドが存在し、形式が正しいかどうかを確認してください。
  • データが期待を満たさない場合にも呼び出されますcompletionHandler()
  • Item(name:price:)ペイロードをアプリ内モデルに変換します。
  • showCartViewController()通知関連のインターフェイスを開きます。
  • 成功したパスも最後に呼び出す必要がありますcompletionHandler()

バックグラウンド プッシュを使用してデータを事前更新する

(08:16) バックグラウンド通知でもリモート通知の登録とデバイストークンの取得が必要ですが、設定は必要ありませんUNUserNotificationCenterDelegate。セッションによって示される理由は非常に単純です。このデリゲートはアラート通知の対話のみを処理します。

class AppDelegate: UIResponder, UIApplicationDelegate {

    func application(_ application: UIApplication,
                     didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
       UIApplication.shared.registerForRemoteNotifications()
       return true
    }
}

キーポイント:

  • バックグラウンド プッシュでも APN のデバイス トークンが使用されます。
  • registerForRemoteNotifications()まだ入り口。
  • アラートのインタラクションがない場合、AppDelegate を通知センターの代理人として設定する必要はありません。

09:05)バックグラウンド通知のペイロードが変更されました。aps.content-availableこの通知がバックグラウンド更新用であることをシステムに伝えるキー フィールドです。

{
    "aps" : {
       "content-available" : 1
    },
    "myCustomKey" : "myCustomData"
}

キーポイント:

  • content-availableつけるaps辞書にあります。
  • 値は1、システムはそれをバックグラウンド通知として扱います。
  • myCustomKeyアプリケーション自体のデータを表します。
  • バックグラウンドペイロードは不要ですalert辞書。

(09:33) デバイスがリモート通知を受信した後、アプリケーションは次のことを行うことができます。didReceiveRemoteNotificationバックグラウンド更新を実行します。例としては、その日のメニューをダウンロードして使用するレストラン アプリがあります。UIBackgroundFetchResult結果をシステムに伝えます。

func application(_ application: UIApplication,
                     didReceiveRemoteNotification userInfo: [AnyHashable : Any],
                     fetchCompletionHandler completionHandler:
                     @escaping (UIBackgroundFetchResult) -> Void) {
    guard let url = URL(string: "www.example.com/todays-menu") else {
        completionHandler(.failed)
        return
    }

    let task = URLSession.shared.dataTask(with: url) { data, response, error in
        guard let data = data else {
            completionHandler(.noData)
            return
        }

        updateMenu(withData: data)
        completionHandler(.newData)
    }
}

キーポイント:

  • userInfoバックグラウンド通知ペイロードが含まれます。
  • fetchCompletionHandlerパラメータは次のとおりです。UIBackgroundFetchResult
  • URLの作成に失敗した場合に返されます.failed
  • データが取得できない場合はリターンします.noData
  • メニューが正常に更新された後に戻る.newData
  • システムはこれらの結果を使用して、今後アプリにバックグラウンド時間を与えるタイミングを決定します。

重要ポイント

期間限定の購入ページへの直接アクセス

何をすべきか: レストラン、電子商取引、または発券アプリケーション用のアラート プッシュを作成します。ユーザーがそれをクリックすると、対応する製品または注文ページに直接アクセスします。

実行する価値がある理由: Session のレストランの例が示していますaps.alert通知表示を担当し、specialそしてpriceビジネスジャンプに必要なデータを担当。

開始方法: リモート通知に登録し、デバイス トークンを取得してプロバイダー サーバーに送信します。サーバーはビジネスフィールドを含むペイロードを送信します。クライアントuserNotificationCenter(_:didReceive:withCompletionHandler:)分析userInfoそして目的のページを開きます。

アプリを開く前に最新のコンテンツをプリフェッチします

対処方法: ニュース、メニュー、在庫、または旅程のアプリケーションは、バックグラウンド プッシュを受信した後、まず最新のデータを取得し、ユーザーが開いたときに新しいコンテンツを確認できるようにします。

実行する価値がある理由: バックグラウンド通知では、アプリケーションがフォアグラウンドにないときに実行時間が得られるため、最新の状態に保つ必要があるデータの処理に適しています。

開始方法: サーバーは次の内容を含むメッセージを送信します。aps.content-available = 1ペイロード。クライアント実装application(_:didReceiveRemoteNotification:fetchCompletionHandler:)、使用URLSessionデータをプルし、完了したら返す.newData.noDataまたは.failed

デバイストークンのヘルスチェック

何をすべきか: バックグラウンド診断パネルを作成して、各デバイスが最後に APN を登録した時刻、トークンのアップロード結果、失敗の理由を記録します。

実行する価値がある理由: セッションはプッシュ リンクのキー識別子としてデバイス トークンを使用します。登録が失敗した場合、またはトークンがプロバイダー サーバーと同期していない場合、後続のペイロードは、それがどれほど正しくてもターゲット デバイスに配信できません。

開始方法:didRegisterForRemoteNotificationsWithDeviceTokenトークンをアップロードします。でdidFailToRegisterForRemoteNotificationsWithErrorエラーを記録します。サーバーはトークン、ユーザー、デバイス、更新時間を保存して、配信の問題のトラブルシューティングを容易にします。

アラートのプッシュとバックグラウンドのプッシュをシーンごとに分割する

対処方法: 同じ種類のビジネス イベントに対して 2 つのプッシュ戦略を確立します。ユーザーがすぐに処理する必要があるイベントにはアラート プッシュを使用し、ローカル データの更新のみが必要なイベントにはバックグラウンド プッシュを使用します。

実行する価値がある理由: セッションでは 2 種類の通知が明確に区別されます。アラート通知にはインタラクティブなリマインダーが表示されます。バックグラウンド通知はコンテンツを更新するために使用され、システムのバックグラウンド実行ポリシーの影響を受けます。

開始方法: イベント タイプ フィールドをプロバイダー サーバーに追加します。ペイロード生成時に書き込みを決定するaps.alertまだaps.content-available;クライアントは、アラートクリック処理とバックグラウンドフェッチ処理をそれぞれ実装します。

関連セッション

コメント

GitHub Issues · utterances