この記事は NewsPicks Advent Calendar 2025 の12日目の記事です。
ソーシャル経済メディア「NewsPicks」でiOSエンジニアをしている金子です。
NewsPicks iOSアプリにて、最近ちょっとユニークな取り組みをしてみました。
例年、新型iPhoneが発売された後の時期にアクティブユーザが減っていく傾向にあることがわかっています。
NewsPicksではプッシュ通知をトリガーにしてアプリを起動してくれるユーザ(逆に言うとあまり能動的には起動しないユーザ)が一定数いるのですが、機種変更によってこうしたユーザがアプリを起動しなくなり、結果としてアクティブユーザ数が減ってしまうのではないかと推測しています。
プッシュ通知を送信するためにはデバイストークンという端末固有の情報が必要であり、そのトークンはアプリを起動することによってサーバに送信される仕組みのため、機種変更後にアプリを起動してもらわないとプッシュ通知を送信することができません。
そこで、ウィジェットを起点としてアプリ本体を起動してもらう仕組みを作ってみました。
何をする仕組みなのか?
機種変更をしたとき、古い端末のデータをバックアップし、新しい端末にデータを復元すると思います。
バックアップにNewsPicksアプリが含まれている状態で、新しい端末にデータを復元した後、24時間NewsPicksアプリを起動しなかったら、以下のような通知が送信されます。

通知をタップしてもらえればアプリが起動し、デバイストークンがサーバに送信されて、プッシュ通知が再び送信できるようになります。
タップしてもらえなかったとしても、NewsPicksアプリの存在をユーザにお知らせできることは意味があります(後で自分から起動してくれるかもしれません)。
仕組みの解説
どのような仕組みで通知を送信しているのか、解説していきます。
古い端末のデータをバックアップして新しい端末にデータを復元すると、設置されていたウィジェットもそのまま新しい端末に復元されます。
これを利用し、ウィジェットからローカル通知を送信して、ユーザにアプリ本体の起動を促そうというものです。
具体的な設計に落とすとこうなります。
- アプリ起動直後に「復元判定ファイルの作成」と「通知フラグのセット(true)」を行う
- 復元判定ファイルはバックアップ対象外にする
- 新しい端末にデータを復元すると、復元判定ファイルは存在せず、通知フラグがtrueの状態になる
- ウィジェットの更新タイミングが来たときに、上記の条件(復元判定ファイル無し・通知フラグ=true)を満たしていたらローカル通知を送信する
コードを交えてもう少し具体的に解説します。
復元判定ファイルの作成と通知フラグのセット
アプリ本体の起動時に、「復元判定ファイル」と呼ぶものを作成します。
ただの空のファイルなんですが、ポイントは以下の2つです。
- App Groupの共有コンテナディレクトリにファイルを作成する
- バックアップ対象外にする
App Groupの共有コンテナディレクトリにファイルを作成することで、アプリ本体とウィジェットでファイルを共有することができます。
また、バックアップの対象外にすることで、「ファイルが存在しない = 復元が行われた」という判定をすることができます。
// ファイルのURLを取得 var url = FileManager.default .containerURL(forSecurityApplicationGroupIdentifier: "app-group")! .appendingPathComponent("device-restoration-marker.txt") // 空ファイルを作成する try Data("".utf8).write(to: url) // バックアップ対象外にする var resourceValues = URLResourceValues() resourceValues.isExcludedFromBackup = true try url.setResourceValues(resourceValues)
なお、復元判定ファイルだけだと、復元を判定するには不十分です。
例えば、NewsPicksをインストール後、アプリを起動せずにウィジェットを配置するケースだと、アプリ本体を起動していないので復元判定ファイルは存在しません。
また、本施策を実装する以前のバージョン(アプリをすでに利用している状態)から、実装済みのバージョンにアップデートした後にまだアプリ本体を起動していないケースでも、復元判定ファイルは存在しません。
これらのケースでは復元と判定するのは間違いです。
そこで、復元判定ファイルとは別に、通知フラグというものをアプリ起動時にセットします。
UserDefaultsの共有ストアに対してセットしておくことで、このフラグもアプリ本体とウィジェットで共有できます。
UserDefaults(suiteName: "app-group")! .set(true, forKey: "notificationEnabled")
この状態で復元を行うと、「復元判定ファイルは存在せず、通知フラグがtrue」の状態になります。
復元後にウィジェットの更新が発生したときにこの状態であることを検知したら、通知を送信可能であると判定することができます。
ウィジェット側の実装
復元の判定とローカル通知の送信は、TimelineProviderプロトコルのgetTimeline(in:completion:)に実装します。
現時点ではローカル通知は1回だけ送信するようにしているので、送信が完了したら通知フラグをfalseにします。
func getTimeline(in context: Context, completion: @escaping @Sendable (Timeline<ImportantNewsEntry>) -> Void) { // ファイルパスを取得 let url = FileManager.default .containerURL(forSecurityApplicationGroupIdentifier: "app-group")! .appendingPathComponent("device-restoration-marker.txt") let existsFile = FileManager.default.fileExists(atPath: url.path()) let notificationEnabled = UserDefaults(suiteName: "app-group")!.bool(forKey: "notificationEnabled") // 復元判定ファイルが存在しない、かつ、通知フラグがtrueの場合、ローカル通知を送信する if !existsFile && notificationEnabled { // ローカル通知とログの送信 } // 通知フラグをfalseにする UserDefaults(suiteName: "app-group")! .set(false, forKey: "notificationEnabled") }
復元とウィジェットの更新タイミング
復元を行った後、ウィジェットの更新はいつ発生するのでしょうか?
実際に復元をして検証してみました。
まず、新しい端末で復元を開始します。
復元が完了すると端末が起動し、最初のセットアップ手順を経て、ホーム画面に到達します。
このときに、復元対象のアプリが自動でインストールされます。
NewsPicksアプリのインストールが完了すると、ウィジェットも自動で復元されます。
このときにgetTimeline(in:completion:)が呼ばれ、ローカル通知がセットされます(通知は24時間後に送信されるように指定している)。
通常、ウィジェットはウィジェットが設置された画面を表示しないと更新が発生しませんが、復元直後の初回はウィジェットが設置された画面を表示せずとも更新が発生しました。
普段見られない場所にウィジェットが設置され、忘れ去られている場合でも通知を送ることができるのは良いですね。
効果検証
では、本施策の効果はどうだったのでしょうか?
- 復元検知UU数:
3515 - ローカル通知送信UU数:
1965(通知が許可されていないと送信できないため復元検知数より少なくなる) - ローカル通知タップUU数数:
269 - ローカル通知タップ率(対 ローカル通知送信数):
13.7% - ローカル通知タップ率(対 復元検知数):
7.7%
ここまでお読みになった方はお気づきの通り、この施策はウィジェットが設置されていることが前提となっているため、そもそもの母数が多くありません。
それでも、機種変更をきっかけにNewsPicksから離れてしまうリスクのあるユーザを少しでも繋ぎ止め、継続につなげるきっかけを作れることは多少なりとも意味はあると思います。
また、この仕組みは一度作ってしまえば運用コストはかかりませんし、魅力あるウィジェットを作り、母数を増やしていけば効果は少しずつでも伸びていきます。
あるいは、復元を行ったユーザを特定することはできているので、メールなど別のチャネルからアプリの継続利用を促す方法も考えられます。
最後に
この施策のアイディアは同じモバイルチームの方からいただいたのですが、僕には思いもつかない発想で、かつなんとかしてビジネスの課題を解決しようとする姿勢に感心させられました。
現時点では効果としては大きいものではありませんが、「創造性がなければ意味がない」、「迷ったら挑戦する道を選ぶ」といった弊社のバリューが体現されている事例だと感じました。
モバイルアプリエンジニアだからこそできるビジネス貢献の形はたくさんあると思います。
これからも自分たちらしい発想で様々なことに挑戦し、発信していこうと思います。