RORK LABEN
MAX — Rork MaxがネイティブSwiftアプリを生成。iPhone・iPad・Watch・TV・Vision Pro・iMessageに対応しますNATIVE — AR/LiDARスキャン、Metalの3Dゲーム、ウィジェット、Live Activities、Core MLまで踏み込めますFUNDING — Rorkがa16zから$2.8Mを調達。月間74.3万訪問・成長率85%と勢いがありますPRICING — 無料で開始でき、有料プランは月額$25から利用できますFLOW — アイデアを英語で説明すると動くコードを生成。共有リンク発行やiOS/Androidビルドに対応しますCOMPARE — 従来のRorkはExpo/React Nativeでクロスプラットフォーム。用途で使い分けられますMAX — Rork MaxがネイティブSwiftアプリを生成。iPhone・iPad・Watch・TV・Vision Pro・iMessageに対応しますNATIVE — AR/LiDARスキャン、Metalの3Dゲーム、ウィジェット、Live Activities、Core MLまで踏み込めますFUNDING — Rorkがa16zから$2.8Mを調達。月間74.3万訪問・成長率85%と勢いがありますPRICING — 無料で開始でき、有料プランは月額$25から利用できますFLOW — アイデアを英語で説明すると動くコードを生成。共有リンク発行やiOS/Androidビルドに対応しますCOMPARE — 従来のRorkはExpo/React Nativeでクロスプラットフォーム。用途で使い分けられます
記事一覧/アプリ開発
アプリ開発/2026-06-27中級

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

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

Rork460Expo114オンボーディング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-06-27
「本当に削除しますか?」を出す前に — 取り消せる削除という選択肢
リストの項目を消すたびに確認ダイアログを出すと、ユーザーは反射的にOKを押すようになります。Rork(Expo)アプリで「取り消せる削除」を実装し、確認を出すべき場面と出さない場面を切り分ける設計ノートです。
アプリ開発2026-06-27
アラビア語に切り替えても画面が鏡像化されない — Rork(Expo)アプリのRTL対応と再起動の罠
Rork が生成した Expo アプリにアラビア語を足したのに、画面が左右反転せず戻るボタンが逆側に残る——その原因は I18nManager.forceRTL が再起動を要求することにあります。expo-localization での方向判定、Updates.reloadAsync での確実な反映、marginStart への置き換え、矢印だけの鏡像化まで、動くコードで RTL 対応の本番設計を示します。
📚RECOMMENDED BOOKS
大規模言語モデル入門
山田育矢
LLM開発
生成AIプロンプトエンジニアリング入門
我妻幸長
プロンプト
Claude CodeによるAI駆動開発入門
平川知秀
AI駆動開発
※ アフィリエイトリンクを含みます
もっと見る →