Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Background Notificationで新聞紙面の大きい画像の自動ダウンロードを実現する

Go Takagi
September 20, 2020

Background Notificationで新聞紙面の大きい画像の自動ダウンロードを実現する

iOSDC2020 Day 1 Track A 10:50〜
Background Notificationで新聞紙面の大きい画像の自動ダウンロードを実現する

本セッションでは、大きい画像コンテンツを対象にした自動ダウンロード機能について説明します。
実装の過程で直面した問題への対策やアプリやサーバーサイドを含めた全体のアーキテクチャを解説するとともに、日本経済新聞 紙面ビューアーにおける新聞紙面画像での運用事例を紹介します。
大規模サービスにおける運用を通じて得られた安定して自動ダウンロードを成功させるための課題の解決方法を説明します。

https://www.youtube.com/watch?v=KI8sGk6hhNU
https://fortee.jp/iosdc-japan-2020/proposal/6bb15eb4-09f9-4fb9-b451-ae8fbd530790
https://iosdc.jp/2020/

Go Takagi

September 20, 2020
Tweet

More Decks by Go Takagi

Other Decks in Technology

Transcript

  1. Background Notificationで
    新聞紙⾯の⼤きい画像の
    ⾃動ダウンロードを実現する
    Go Takagi (@shimastriper)
    iOSDC JAPAN Day #Track A : 〜

    View full-size slide

  2. ⾃⼰紹介
    ‣ Go Takagi
    • ⽇本経済新聞社 社会⼈2年⽬
    • ⽇経電⼦版 / 紙⾯ビューアー / Nikkei Wave iOSアプリ開発
    • Twitter: @shimastriper
    • 柴⽝とAppStoreのストーリーを毎⽇読むのが好き

    View full-size slide

  3. ⾃動ダウンロードするためのアジェンダ
    ‣ 紙⾯ビューアーへ新聞データを快適に届けたい
    ‣ iOSのBackground、制限厳しい
    ‣ 全体的なアーキテクチャ
    ‣ 端末だけが問題じゃないぞ!サーバー側へ
    ‣ やっぱり端末、そして性能差......
    ‣ "安定して" 届けるための対策を振り返る
    電⼦版広報⽝デンシバ

    View full-size slide

  4. コンテンツの⾃動ダウンロード
    ‣ 快適なユーザー体験
    • アプリを開いたユーザーが待たずに利⽤できる
    • ユーザーの定期的な⾏動意欲にも繋がる
    ‣ 通信への配慮
    • 重いコンテンツをWi-Fi時にダウンロード
    • 実際に使うときはオフラインでも最新の状態にしておける

    View full-size slide

  5. 紙⾯ビューアー: 新聞紙⾯の画像ビューアー
    ‣ 紙⾯ビューアー
    • 紙ベースで新聞を読める
    • iPadユーザーが多い
    ‣ 快適なオフライン動作の需要
    • Wi-Fiで⼀括DLして通信量節約
    • iPadはCellularがついていないモデルも多い
    • 出かける前にDLしておく必要性
    朝⼣刊‧他媒体を紙で読める

    View full-size slide

  6. 新聞の紙⾯データは定期配信コンテンツ
    ‣ 寝てる間にアプリへ配達
    • 通勤で利⽤するユーザーがDLで億劫にならない
    • 朝まるで郵便受けに新聞が届いているように...
    ‣ ユーザーを逃さない
    • ⾃動DLを有効にするというのはポジティブな証拠
    • ユーザーへの強いリテンションにも貢献する
    • ユーザーからの要望も⾮常に多かった

    View full-size slide

  7. 紙⾯画像データの通信量はそれなりのサイズ
    ‣ 画像容量はかさむ
    • ⽂字の可読性
    ‣ 枚数も多い
    • 最⼤ページ数は48P
    ‣ 3段階の画質
    • ⼩‧中‧⼤
    ‣ 合計30MB〜 MB (WebP)

    View full-size slide

  8. iOSのBackground、制限厳しい

    View full-size slide

  9. Background Mode
    ‣ アプリのバックグラウンド実⾏
    • Foregroundにいない状態でもアプリ‧コードを実⾏できる
    • アプリからのリクエストや特定のイベントをトリガーに実⾏
    ‣ iOSでは⽤途に応じて利⽤できるAPIが存在
    • 位置情報の取得
    • Bluetooth通信
    • サーバーと同期‧モデルの訓練
    • コンテンツのダウンロード‧アップデート
    • etc...
    https://developer.apple.com/documentation/usernotifications/setting_up_a_remote_notification_server/pushing_background_updates_to_your_app
    'PSFHSPVOE

    View full-size slide

  10. Background Modeの制約
    ‣ APIごとに様々な制限が存在する
    • Power : バッテリーへの考慮
    • Performance : メインアプリとのバランス
    • Privacy : 何をしているかユーザーに明⽰する
    ‣ 例えばどんな制限があるか?
    • 制限時間‧なるべく早く終える
    • 端末の状況で実⾏されないときもある
    • ユーザーからの許諾が必須
    「バックグラウンド更新」の
    許可も基本必要
    https://developer.apple.com/videos/play/wwdc / /

    View full-size slide

  11. Background Mode
    ‣ アプリのバックグラウンド実⾏
    • Foregroundにいない状態でもアプリ‧コードを実⾏できる
    • アプリからのリクエストや特定のイベントをトリガーに実⾏
    ‣ iOSでは⽤途に応じて利⽤できるAPIが存在
    • 位置情報の取得
    • Bluetooth通信
    • サーバーと同期‧モデルの訓練
    • コンテンツのダウンロード‧アップデート
    • etc...
    https://developer.apple.com/documentation/usernotifications/setting_up_a_remote_notification_server/pushing_background_updates_to_your_app
    'PSFHSPVOE

    View full-size slide

  12. Backgroundでダウンロードする⽅法
    ‣ Background App Refresh Task ( iOS . + )
    • Background Fetch ( iOS . - . ) の後継
    ‣ Background Notification ( iOS . + )
    ‣ Background Transfer Service ( iOS . + )
    ‣ Background Processing Task ( iOS . + )

    View full-size slide

  13. Background App Refresh Task ( iOS . + )
    ‣ 特徴
    • Background Fetch ( iOS . - . ) の後継
    • OSが判断して定期的に実⾏する
    • App Refresh Taskは都度リクエスト
    • 30秒の実⾏時間
    • 実⾏間隔を指定
    • 使⽤頻度に応じて実際の間隔は増減
    • 1⽇を通してアプリを最新に保てる
    この例はユーザーが使う直前に実⾏
    ⼀定間隔の定期Refresh
    https://developer.apple.com/videos/play/wwdc / /

    View full-size slide

  14. Background Notification ( iOS . + )
    ‣ 特徴
    • サーバー経由で通知を送信して実⾏
    • 通知の許諾も必要ない
    • 受信時にOSが判断して実⾏
    • 30秒の実⾏時間
    • Foreground時は必ず実⾏
    • Background時はバッテリー‧通信状況に応じて実⾏
    • ⽬安として、多くても1時間に2,3回に留める
    明⽰的なRefresh要求を投げれる
    特定タイミングのRefresh
    https://developer.apple.com/videos/play/wwdc / /

    View full-size slide

  15. Background Transfer Service ( iOS . + )
    ‣ 特徴
    • URLSessionのBackground mode
    • ⼤きなファイル向けの通信
    • アプリと別プロセスでOSが実⾏
    • 時間制限も基本ない
    • Background Push経由でリクエストも可能
    • isDiscretionaryプロパティで遅延リクエストをする
    • OSが適切なタイミングで実⾏させる (Wi-Fiや充電時)
    • Backgroundで呼ばれると強制 isDiscretionary
    ⻑めのDL/ULを遅延予約
    遅延して適切なタイミングでDL
    https://developer.apple.com/videos/play/wwdc / /

    View full-size slide

  16. BackgroundでコンテンツをRefreshする⽅法
    ‣ Background App Refresh Task ( iOS . + )
    • Background Fetch ( iOS . - . ) の後継
    ‣ Background Notification ( iOS . + )
    ‣ Background Transfer Service ( iOS . + )
    ‣ Background Processing Task ( iOS . + )
    ← 採⽤
    最新OS限定、検討もせず

    View full-size slide

  17. Background Notificationで実装した理由 1
    ‣ コンテンツとの相性 (朝⼣刊)
    • 配信タイミングで実⾏がBEST
    • 移動前に動いてほしい
    • ユーザーの⾏動に依らない
    • 定期的にRefreshするものでない
    • ⼣刊を夜中にDLしても意味無し
     








    
    
    
    
    
    
    
    
    
    
    
    
    
    
    鵟ⳝ
    䅯㱝 消洣 ⩗꧅
    ☼◄

    View full-size slide

  18. Background Notificationで実装した理由 1
    ‣ コンテンツとの相性 (朝⼣刊)
    • 配信タイミングで実⾏がBEST
    • 移動前に動いてほしい
    • ユーザーの⾏動に依らない
    • 定期的にRefreshするものでない
    • ⼣刊を夜中にDLしても意味無し
     








    
    
    
    
    
    
    
    
    
    
    
    
    
    
    鵟ⳝ
    䅯㱝
    僼⮍ ☽㨥⛮
    㛗⮍
    消洣 ⩗꧅
    ☼◄
    実⾏したいタイミングは 2つの時間帯に集中

    View full-size slide

  19. Background Notificationで実装した理由 2
    ‣ 通信環境の考慮
    • Wi-Fi + Cellularもサポートしたかった
    • Androidと仕様を揃える
    • ユーザーに選択できるようにしたい
    ‣ ビューアーは iOS までサポート (今はiOS )
    • Background FetchはiOS 以降で分岐対応
    • 挙動が⼤きく違ったら担保できるか?という懸念
    Android版はCellularも対応

    View full-size slide

  20. 全体的なアーキテクチャ

    View full-size slide

  21. サーバー側アーキテクチャ〜アプリへ届くまで〜
    "84-BNCEB
    'JSFCBTF$MPVE.FTTBHJOH
    碊ꪫلٖ٭ؓ٭
    "NB[PO4

    View full-size slide

  22. サーバー側アーキテクチャ〜アプリへ届くまで〜

    デバイスに最適化した
    紙⾯画像が随時プール
    "84-BNCEB
    'JSFCBTF$MPVE.FTTBHJOH
    碊ꪫلٖ٭ؓ٭
    "NB[PO4

    View full-size slide

  23. サーバー側アーキテクチャ〜アプリへ届くまで〜

    定期的にDL可能な
    今⽇の新聞リストを抽出
    朝刊 + 他媒体...
    "84-BNCEB
    'JSFCBTF$MPVE.FTTBHJOH
    碊ꪫلٖ٭ؓ٭
    "NB[PO4
    デバイスに最適化した
    紙⾯画像が随時プール

    View full-size slide

  24. サーバー側アーキテクチャ〜アプリへ届くまで〜
    Background Push

    朝刊 (+α) OK!!
    定期的にDL可能な
    今⽇の新聞リストを抽出
    朝刊 + 他媒体...
    "84-BNCEB
    'JSFCBTF$MPVE.FTTBHJOH
    碊ꪫلٖ٭ؓ٭
    "NB[PO4
    デバイスに最適化した
    紙⾯画像が随時プール

    View full-size slide

  25. サーバー側アーキテクチャ〜アプリへ届くまで〜
    アカウントがDL可能な
    媒体をダウンロード

    Background Push
    朝刊 (+α) OK!!
    定期的にDL可能な
    今⽇の新聞リストを抽出
    "84-BNCEB
    'JSFCBTF$MPVE.FTTBHJOH
    碊ꪫلٖ٭ؓ٭
    "NB[PO4
    デバイスに最適化した
    紙⾯画像が随時プール

    View full-size slide

  26. Background Notificationの送り⽅ (APNS)
    ‣ Header (iOS から必須)
    ‣ Body
    apns-priority =
    apns-push-type = background
    {
    "aps" : {
    "content-available" :
    },
    "acme " : "bar",
    "acme " :
    }
    https://developer.apple.com/documentation/usernotifications/setting_up_a_remote_notification_server/pushing_background_updates_to_your_app
    Capabilitiesをチェック

    View full-size slide

  27. Background Notification受信時の動作
    ‣ なるべく早くcompletionHandlerを呼ぶ
    • newData / failed / noData のステータスをつけて終わらせる
    • OSが設定した時間(Max 秒)を超えても強制的に終了
    func application(_ application: UIApplication,
    didReceiveRemoteNotification userInfo: [AnyHashable : Any],
    fetchCompletionHandler completionHandler: @escaping (UIBackgroundFetchResult) -> Void) {
    URLSession.shared.dataTask(with: url) { data, response, error in
    completionHandler(.newData)
    }.resume()
    }

    View full-size slide

  28. 成功時にローカル通知を発⾏
    DL済みの媒体が表⽰される
    DLして、ローカル通知
    (⾒出しはサンプルです)

    View full-size slide

  29. 細かいTips
    五⽉⾬に話します!

    View full-size slide

  30. Background Notificationの性質に対処
    ‣ 実⾏確率を上げるため、複数回送る (基本1回では⾜りない)
    • 実⾏済みならnoDataを通知する
    • データが重いならDLを中断‧再開できる仕組みにする
    • それかBackground Transferにリクエストする
    ‣ Background Notification⾃体を複数種類作らない
    • 受信‧実⾏が特定のものに偏るおそれ
    • あくまで実⾏後に実際どの処理をさせるか分岐するといい
    • 例えば、APNSのPayloadにやりたいことの優先順位をつけておく

    View full-size slide

  31. OS側が⽌めてしまうことを前提としたロジック
    ‣ 排他制御のミスに気をつける
    • Lockをかけて⾮同期処理のcompletedを待って解除するとか
    • OSによって強制終了されると⼀⽣ロックされてしまう
    ‣ 最⼩限の単純なロジックで動作するようにする
    • 時間もメモリも⼤切に、いらないリクエストをしない
    • サーバー側で解決できることは寄せておいたほうがいい
    • (やっぱりデバッグも⼤変なので......)

    View full-size slide

  32. 提⽰できる状態はなるべく明⽰的に周知
    ‣ 実⾏するタイミングが不定だからこそ重要
    • ユーザーにとっては何が原因で動かないのかよくわからない
    ‣ ユーザーによる意図的なアプリの終了
    • Background Notificationも受け取らなくなる
    • 反射的に終了されないように説明する

    View full-size slide

  33. Background Notificationに通知の許諾はいらない
    ‣ background notification
    • UIApplication.registerForRemoteNotifications() でdeviceTokenは取得可能
    • UNUserNotificationCenter.requestAuthorization() は不要
    • 受信時もapplication関数にdelegateされる
    ‣ alert notification (⼀般的な通知の⽅)
    • UserNotifications.framework ( iOS + ) に集約されてる
    • UNUserNotificationCenter
    • UNUserNotificationCenterDelegate
    完了後のローカル通知には必要
    https://developer.apple.com/videos/play/wwdc / /

    View full-size slide

  34. 「設定アプリ」の許諾状態の表⽰
    ‣ アプリに対する許諾を取得することができる
    • UIBackgroundRefreshStatus (UIKit) ( iOS . + )
    • 「Appのバックグラウンド更新」の可否
    • UNNotificationSettings (UNNotificationCenter) ( iOS . + )
    • 「通知」の可否

    View full-size slide

  35. (許諾が⾜りなければ) アプリ上でも明⽰的に伝える

    View full-size slide

  36. Background Push Notificationの動作確認
    ‣ 通知が必要なので、基本的に実機が必須
    • Xcode . からSimulatorで操作できる機能がついた (が!!次ページ!!)
    ‣ Foregroundで受信
    • 確実に動く
    ‣ (Xcodeの) DebuggerをつなげたままBackgroundで受信
    • アプリが停⽌状態にならないため⾼確率で実⾏
    ‣ Wi-Fi + 充電したままBackgroundで受信
    • OSの判断で状況により実⾏しない、時間制限もある
    ‣ 環境を厳しくしていくと.......
    • より実⾏機会が減っていく

    View full-size slide

  37. (ちなみに) Simulatorで通知を操作する
    ‣ Xcode . から使⽤可能に!
    • SimulatorにDrag & Dropで通知が出せる !!
    ‣ ただし、BG Notificationはバグってる
    • BG Fetchで使う関数が⽴ち上がる...
    • application(_:performFetchWithCompletionHandler:)
    • Xcode . でも直っておらず
    Simulator supports simulating remote push notifications,
    including background content fetch notifications.
    https://developer.apple.com/documentation/xcode-release-notes/xcode- _ -release-notes
    https://developer.apple.com/forums/thread/
    {
    "Simulator Target Bundle" : ,
    "aps" : {
    "alert": "Drag & Dropするだけ",
    "badge":
    }
    }
    {
    "Simulator Target Bundle" : ,
    "aps" : {
    "content-available":
    }
    }

    View full-size slide

  38. よし、完成!?
    端末だけが問題じゃないぞ!サーバー側へ

    View full-size slide

  39. APIサーバーに瞬間的にリクエストが殺到
    ⼀部公開でも⼤変な量に

    View full-size slide

  40. 運⽤すると課題が⾒えてくる
    ‣ サーバーアクセスのピークへの対処
    • 元々通知がないアプリだったため、対処が⾜りてなかった
    ‣ どういう対策を⼊れるか
    • 適当にタイマーを発⽣させて遅延させる (Android)
    • 30秒以下という時間制限だとiOSでは厳しい
    • Requestの徹底整理
    • CDN‧APIサーバー、適切にキャッシュをもたせる
    • ユーザーデータなどのキャッシュが難しいものへの対処‧削除
    • それでもダメなら.... → 受信対象群を分割

    View full-size slide

  41. CDNで捌いて負荷対策

    View full-size slide

  42. やっぱり端末、そして性能差......

    View full-size slide

  43. 端末による成功率の違いが如実に
    ‣ Cellular通信時、Notification起動率が⼤きく違う
    • iPhone XまではCellularでも安定実⾏
    • iPhone s, iPad Airなどは3⽇ほど放置して⼀切受信しないことも
    ‣ 当時: iOS サポート
    • 古い端末が⼀定数利⽤されていることが確認されている
    • 機能として安定しないものを公開するのは......
    ‣ 結論
    • ⼀旦Cellularは無しでWi-Fiのみでリリースしました (⾟い)

    View full-size slide

  44. 実⾏チャンスを増やすには......
    ‣ 他のBackground Modeも同時併⽤する
    • BGTask‧Background Transfer
    ‣ Notification Service Extensionは確実に実⾏する
    • 本来、アラート通知の情報を追加取得 (サムネイルなど)するための処理
    • (通知は必ず表⽰するため) DLが終わらなければ「失敗しました」と表⽰
    ‣ LocationAPIでアプリを起こして受信確率を上げる
    • もちろん元々位置情報を使う真っ当な理由が必須
    • 限られたアプリでしか適⽤できない

    View full-size slide

  45. OSによって落とされにくいアプリへと改善する
    ‣ 逆に考えよう
    • 起こすのが⼤変なら落とされにくいアプリを⽬指せばいい!
    ‣ OSによってメモリから落とされる原因として推察できること
    • メモリ⾷い過ぎ
    • 重い他メインアプリの動作を優先
    • バグがあってひっそりクラッシュ......
    • MetricKitがアップデートしてBackground Killを特定できそう!
    • ユーザーによる⼿動Killすら拾えるみたい
    • 2020のリンクを載せておくので詳細は動画を各⾃で
    アプリ本体の改善
    https://developer.apple.com/videos/play/wwdc / /

    View full-size slide

  46. "安定して" 届けるための対策を振り返る

    View full-size slide

  47. 安定して受け取るために......
    ‣ 利⽤するBackground Modeの選択
    • コンテンツの性質は?それに合わせたAPIは?
    ‣ 動かしたい時間帯の考察
    • 通信環境?
    • 充電環境?
    • 他のアプリは動いている?
    • 他のBackground処理は動いてそう?
    ‣ アプリ本体は健康?
    • なるべくOSから落とされにくい状態を堅持
    • 使⽤メモリやバグ⼤丈夫?

    View full-size slide

  48. 安定して受け取るために......
    ‣ 利⽤するBackground Modeの選択
    • コンテンツの性質は?それに合わせたAPIは? 配信タイミングでBackground Notification
    ‣ 動かしたい時間帯の考察
    • 通信環境? Wi-Fi (いずれ Cellular も...)
    • 充電環境? 朝⽅は多くの端末は充電してるはず
    • 他のアプリは動いている? 多くの端末では動いてない時間帯
    • 他のBackground処理は動いてそう? AM : のようなキリのいい時間は避ける? (恣意的ですが)
    ‣ アプリ本体は健康?
    • なるべくOSから落とされにくい状態を堅持
    • 使⽤メモリやバグ⼤丈夫?
    (サーバーログからはOSによる遅延調整はあまり無かった)
    iOS でもっとやるぞ!!
    ⽇経の場合は

    View full-size slide

  49. まとめ
    ‣ BackgroundNotificationで新聞を毎朝届けられた!
    • とはいえ安定して成功するのは⼤変
    • Background時の処理は必要最⼩限にしましょう
    • サーバー側で解決できるものはしておく
    • 重い場合、中断‧再開できる仕組みにして保険をかける
    • アクセスが集中するのでサーバー側の負荷チェックを忘れずに
    • アプリ全体の振る舞いを最適化して実⾏機会を増やす
    • 通信‧電源環境は特に⼤変、⾃社がサポートする端末スペックを要確認

    View full-size slide

  50. [参考] セッション動画 (おすすめ視聴順)
    The Push Notifications primer
    iOSの通知全体を通して学べる
    Advances in App Background Execution
    iOSのBackground execution全体の解説
    Background execution demystified
    Background executionの実⾏条件のより細かい制約を紹介してくれている
    What's New with Multitasking
    iOS で⼊ったBackgroundでAppを更新するためのAPI群の主な解説
    Why is my app getting killed?
    MetricKitを⽤いたAppのBackground Killの原因と特定⽅法の解説
    WWDC ,
    WWDC ,
    WWDC ,
    WWDC ,
    WWDC ,

    View full-size slide

  51. [参考] ドキュメント
    Pushing Background Updates to Your App
    Background Notification
    https://developer.apple.com/documentation/usernotifications/setting_up_a_remote_notification_server/pushing_background_updates_to_your_app
    Downloading Files in the Background
    Background Transfer
    https://developer.apple.com/documentation/foundation/url_loading_system/downloading_files_in_the_background?language=objc
    設定画⾯の情報取得
    バックグラウンドの更新ステータス‧通知の許諾ステータス
    https://developer.apple.com/documentation/uikit/uiapplication/ -backgroundrefreshstatus
    https://developer.apple.com/documentation/usernotifications/unnotificationsettings
    Simulatorで通知をハンドリング
    Simulator上で操作する⼿順‧Background Notificationハンドリング時のバグらしき挙動の報告
    https://developer.apple.com/documentation/xcode-release-notes/xcode- _ -release-notes
    https://developer.apple.com/forums/thread/

    View full-size slide