RORK LABEN
MAX — Rork MaxはClaude CodeとClaude Opus 4.6を基盤に、React NativeではなくネイティブSwiftアプリを直接生成しますAPPLE — Rork MaxはiPhone・iPad・Apple Watch・Apple TV・Vision Pro・iMessageまでAppleエコシステム全体を対象にしますWORKFLOW — 実運用ではAIに足場を作らせ、状態管理とデータ層は自分で書き直す分担に落ち着いてきたという声が目立ちますSEED — 4月にLeft Lane Capital主導で$15Mのシードを調達しました(Peak XV・True Ventures・a16z Speedrunが参加)PAPERLINE — アプリビルダーPaperlineを買収。エンジニア人材の獲得へ今後も買収を続ける方針ですREVIEW — 3ヶ月使ったユーザーの再訪レビューが増え、得意・不得意と向く用途の見極めが進んでいますMAX — Rork MaxはClaude CodeとClaude Opus 4.6を基盤に、React NativeではなくネイティブSwiftアプリを直接生成しますAPPLE — Rork MaxはiPhone・iPad・Apple Watch・Apple TV・Vision Pro・iMessageまでAppleエコシステム全体を対象にしますWORKFLOW — 実運用ではAIに足場を作らせ、状態管理とデータ層は自分で書き直す分担に落ち着いてきたという声が目立ちますSEED — 4月にLeft Lane Capital主導で$15Mのシードを調達しました(Peak XV・True Ventures・a16z Speedrunが参加)PAPERLINE — アプリビルダーPaperlineを買収。エンジニア人材の獲得へ今後も買収を続ける方針ですREVIEW — 3ヶ月使ったユーザーの再訪レビューが増え、得意・不得意と向く用途の見極めが進んでいます
記事一覧/アプリ開発
アプリ開発/2026-06-27中級

アップデート後の「新着情報」を、しつこくなく一度だけ出す設計 — バージョン境界とseen状態の作り方

更新後に出すアプリ内「新着情報」画面が、新規インストールにまで出たり毎回出たりして嫌われる問題を、バージョン境界とseen状態の設計で一度だけ確実に届ける。Rork(Expo)の実コードと、複数アプリで使い回す型までまとめます。

Rork502Expo139オンボーディング10リテンション設計2リリース運用2

プレミアム記事

新機能を入れても気づかれない、という壁に個人開発で何度もぶつかってきました。App Store の「このバージョンの新機能」欄はほとんど読まれませんし、プッシュ通知は許可していない人には届きません。残る確実な接点は、更新後に最初に開いたその瞬間の画面です。リテンションに効く機能ほど、ここで気づいてもらえるかどうかが効いてきます。

ところがこの「新着情報」画面は、作り方を一つ間違えると一気に嫌われます。インストールしたばかりの人にまで「新機能のお知らせ」が出る、開くたびに同じ案内が出る、こうなると体験を損ねるだけです。これは私自身が初期に踏んだ落とし穴でもあります。境界を正しく引いて「更新した人に、一度だけ」を成立させる設計をまとめます。

「更新した人にだけ」をどう判定するか

判定に必要なのは二つの値です。いま動いているアプリの「現在バージョン」と、このユーザーが「前回どのバージョンを使っていたか」。後者を毎回保存しておき、起動時に前者と突き合わせます。

  • 保存値が無い → 新規インストール。新着情報は出さない。
  • 保存値 < 現在 → 更新された。新着情報を出す候補。
  • 保存値 = 現在 → 同じバージョンで再起動しただけ。出さない。

現在バージョンは expo-application から取得します。app.jsonversion を手で持ち回るより確実です。

// lib/appVersion.ts
import * as Application from "expo-application";
 
// "1.4.0" のような表示用バージョン。null になり得るのでフォールバックを置く。
export const CURRENT_VERSION = Application.nativeApplicationVersion ?? "0.0.0";
 
// 文字列バージョンを比較可能な配列にする素朴なセムバー比較。
export function isNewer(a: string, b: string): boolean {
  const pa = a.split(".").map((n) => parseInt(n, 10) || 0);
  const pb = b.split(".").map((n) => parseInt(n, 10) || 0);
  for (let i = 0; i < Math.max(pa.length, pb.length); i++) {
    const x = pa[i] ?? 0;
    const y = pb[i] ?? 0;
    if (x !== y) return x > y;
  }
  return false;
}

前回バージョンの記録と判定をまとめる

判定ロジックは小さな関数一つに閉じ込めます。ここで大事なのは「新着情報を出すか」と「前回バージョンを更新するか」を分けて考えることです。前回バージョンの保存は、新着情報を出したかどうかに関わらず、必ず行います。さもないと、新着情報のデータがまだ無い更新でも、次の本命の更新時に「2バージョン分まとめて」案内が出てしまいます。これが見落としやすい注意点です。

// lib/whatsNew.ts
import AsyncStorage from "@react-native-async-storage/async-storage";
import { CURRENT_VERSION, isNewer } from "./appVersion";
 
const LAST_VERSION_KEY = "whatsNew.lastVersion";
 
export type WhatsNewDecision =
  | { show: false }
  | { show: true; fromVersion: string; toVersion: string };
 
export async function evaluateWhatsNew(): Promise<WhatsNewDecision> {
  const last = await AsyncStorage.getItem(LAST_VERSION_KEY);
 
  // 前回バージョンは常に最新へ更新する(判定の後始末を先に確定)。
  const persist = () =>
    AsyncStorage.setItem(LAST_VERSION_KEY, CURRENT_VERSION);
 
  // 新規インストール: 記録だけ残して何も出さない。
  if (!last) {
    await persist();
    return { show: false };
  }
 
  // 同一 or 何らかの理由で現在が古い: 出さない。
  if (!isNewer(CURRENT_VERSION, last)) {
    await persist();
    return { show: false };
  }
 
  // 更新された: 出す候補。fromVersion を呼び出し側に渡す。
  const decision: WhatsNewDecision = {
    show: true,
    fromVersion: last,
    toVersion: CURRENT_VERSION,
  };
  await persist();
  return decision;
}

新規インストールで persist() だけして show: false を返すのが肝です。これで初回は静かに記録だけ取り、次回以降の更新から正しく一度だけ出せます。この挙動は実機の新規インストールで必ず確認することを推奨します。

ここまでお読みいただきありがとうございます。

この記事の続きを読む

この先には、実装コードやベンチマーク結果など、実務でお役に立てる内容をご用意しています。このサイトは広告を掲載しておらず、サーバーや開発にかかる費用はメンバーの皆様のご支援で成り立っています。もしお役に立てていましたら、ご支援いただけますと大変ありがたいです。

この記事で得られること
新規インストールには出さず、更新したユーザーにだけ一度出すための『前回バージョン記録』と判定ロジックの実装
expo-application で現在バージョンを取り、seen状態をバージョン単位で持つ取りこぼしのない仕組み
壁紙アプリを6本並行運用する立場での、新着情報データを一箇所に集約して各アプリへ流す共有設計
Stripe による安全な決済 · いつでもキャンセル可能

この記事を購入する

この先の内容をすべてお読みいただけます。一度のご購入で、いつでも何度でもアクセスできます。このサイトは広告を掲載しておらず、皆さまのご支援がサーバー費用などの運営を支えています。

または
メンバーシップなら全記事が読み放題 →
シェア

お読みいただきありがとうございます

Rork Lab は広告なしで運営しており、サーバー費用などの運営コストはメンバーシップのご支援で賄っています。実装コード・ベンチマーク・本番設計パターンなど、実務でお役立ていただける記事を毎日更新しています。もし読んでよかったと感じていただけましたら、ぜひご覧ください。

  • コピー&ペーストで使える実装コード付き
  • 毎日新しい上級ガイドを追加
  • ¥580/月 または ¥1,480 の永久アクセス
メンバーシップを見る →

関連記事

アプリ開発2026-06-27
端末の時計を戻されても「今日の1枚」が崩れない設計 — 日替わりコンテンツの日付境界とストリーク整合
日替わりの「今日のコンテンツ」がタイムゾーン移動や端末時計の巻き戻しで二重表示・欠落・ストリーク消失を起こす問題を、決定論的な日付キーと単調クロックで防ぐ設計を、Rork(Expo)アプリの実コードでまとめます。
アプリ開発2026-07-07
高さの異なる画像を2列で美しく並べる—Rork の Expo アプリで壁紙ギャラリーを段組みにする
縦横比のばらつく画像を numColumns では美しく並べられない理由から、列の高さを見て振り分ける段組みアルゴリズム、FlashList の masonry での仮想化、依存を増やさない実装まで、壁紙ギャラリーを実コードで組みます。
アプリ開発2026-07-05
ワンタイムコードの入力欄をExpoで作る — SMS自動入力とセグメント表示を両立させる
6桁の確認コードを入れる画面は、見た目こそ単純ですが、SMSの自動入力・貼り付け・1文字ずつの削除まで含めると意外と作り込みが必要です。iOSとAndroidの自動入力を最初に押さえ、単一のTextInputで崩れにくいセグメント表示を組む実装をまとめました。
📚RECOMMENDED BOOKS
大規模言語モデル入門
山田育矢
LLM開発
生成AIプロンプトエンジニアリング入門
我妻幸長
プロンプト
Claude CodeによるAI駆動開発入門
平川知秀
AI駆動開発
※ アフィリエイトリンクを含みます
もっと見る →