RORK LABEN
BUILD — Rork Max はクラウド上の実機 Mac に Xcode と iOS SDK を載せ、SwiftUI を書き、ビルドし、エラーを読んで直す反復を回します。コードを吐いて終わりではない点が生成物の質に効いていますNATIVE — 生成されるのは React Native ではなく純粋な Swift / SwiftUI です。AR や Metal、ウィジェットなど React Native からは届かない機能に手が届くことが、他ビルダーとの実質的な差になりますPLATFORMS — 対応は iPhone・iPad・Apple Watch・Apple TV・Vision Pro に加えて iMessage まで含みます。ウォッチや拡張から入る設計を試すのに向いていますCOMPANION — Rork Companion により、有料の Apple Developer アカウントなしで生成したアプリを実機の iPhone で確認できます。最初の一本を試す段階の障壁が一段下がりましたPRICING — 無料で開始でき有料プランは月額25ドルから、Rork Max は月額200ドルの Max プランです。回収できる本数と規模を先に見積もっておく価値がありますDEADLINE — Google Play は2026年8月31日以降、新規アプリと更新の双方に対象 API レベル36 以上を要求します。残り10日で、生成物の targetSdkVersion は自分で確認が必要ですBUILD — Rork Max はクラウド上の実機 Mac に Xcode と iOS SDK を載せ、SwiftUI を書き、ビルドし、エラーを読んで直す反復を回します。コードを吐いて終わりではない点が生成物の質に効いていますNATIVE — 生成されるのは React Native ではなく純粋な Swift / SwiftUI です。AR や Metal、ウィジェットなど React Native からは届かない機能に手が届くことが、他ビルダーとの実質的な差になりますPLATFORMS — 対応は iPhone・iPad・Apple Watch・Apple TV・Vision Pro に加えて iMessage まで含みます。ウォッチや拡張から入る設計を試すのに向いていますCOMPANION — Rork Companion により、有料の Apple Developer アカウントなしで生成したアプリを実機の iPhone で確認できます。最初の一本を試す段階の障壁が一段下がりましたPRICING — 無料で開始でき有料プランは月額25ドルから、Rork Max は月額200ドルの Max プランです。回収できる本数と規模を先に見積もっておく価値がありますDEADLINE — Google Play は2026年8月31日以降、新規アプリと更新の双方に対象 API レベル36 以上を要求します。残り10日で、生成物の targetSdkVersion は自分で確認が必要です
記事一覧/アプリ開発
アプリ開発/2026-06-27上級

端末の時計を戻されても「今日の1枚」が崩れない設計 — 日替わりコンテンツの日付境界とストリーク整合

日替わりの「今日のコンテンツ」がタイムゾーン移動や端末時計の巻き戻しで二重表示・欠落・ストリーク消失を起こす問題を、決定論的な日付キーと単調クロックで防ぐ設計を、Rork(Expo)アプリの実コードでまとめます。

Rork539Expo175日付処理ストリークリテンション設計2

プレミアム記事

引き寄せや癒し系の「今日の1枚」を配信するアプリを個人開発で運用していると、ある時期から少数のユーザーから決まった報告が届くようになりました。「昨日と同じ画像が今日も出る」「連続記録が急にゼロに戻った」。再現条件を聞き取ると、海外渡航中だったり、ゲームの報酬目当てで端末の時計を進めて戻した直後だったりします。リテンションの肝になる機能だけに、こうした戸惑いはそのまま離脱につながります。

日替わりコンテンツは、一見すると「日付が変わったら次を出すだけ」の単純な機能です。しかし new Date() をそのまま信じた瞬間に、タイムゾーン移動・サマータイム・端末時計の手動変更という三つの揺らぎが全部そこに流れ込んできます。これは本番運用で実際に踏んだ罠でもあります。ここでは、その揺らぎを一箇所に閉じ込めて「今日」を決定論的に確定させる設計を、Rork(Expo) で動くコードとしてまとめます。

「今日」を決める場所を一点に絞る

最初にやめるべきは、画面のあちこちで new Date().getDate() を呼ぶことです。比較に使う「今日」が呼び出し箇所ごとにわずかにずれ、深夜0時前後で表示と保存が食い違います。これが二重表示と欠落の温床になります。

代わりに、その瞬間が属する「日」を YYYY-MM-DD の文字列キー(dayKey)に畳み込む関数を一つだけ用意し、アプリ全体がそこだけを通るようにします。

// lib/dayKey.ts
// 基準タイムゾーンは「コンテンツを切り替えたい時計」を一つ決めて固定する。
// 日本向けの日替わりアプリなら Asia/Tokyo を採用すると、
// 海外渡航中のユーザーも「日本の今日」で同じ1枚を見られる。
const CONTENT_TZ = "Asia/Tokyo";
 
export function dayKeyFor(date: Date, timeZone: string = CONTENT_TZ): string {
  // Intl で「そのタイムゾーンでの年月日」を取り出す。端末のロケール非依存。
  const parts = new Intl.DateTimeFormat("en-CA", {
    timeZone,
    year: "numeric",
    month: "2-digit",
    day: "2-digit",
  }).formatToParts(date);
 
  const get = (t: string) => parts.find((p) => p.type === t)?.value ?? "";
  return `${get("year")}-${get("month")}-${get("day")}`; // 例: "2026-06-27"
}
 
export function todayKey(timeZone: string = CONTENT_TZ): string {
  return dayKeyFor(new Date(), timeZone);
}

en-CA ロケールを使うのは、YYYY-MM-DD 形式が標準で得られ、文字列比較がそのまま日付の前後比較になるからです。dayKey 同士は辞書順で比較でき、"2026-06-27" < "2026-06-28" が常に成り立ちます。コンテンツの選択も、ストリークの判定も、保存も、すべてこの文字列だけを見て行います。私はこの一点集約を強く推奨します。

ローカル時刻・固定タイムゾーン・サーバ時刻のどれを基準にするか

「今日」をどの時計で決めるかは、アプリの性格で変わります。バックエンドを持たない個人開発のアプリでは、サーバ時刻に毎回問い合わせる前提は重すぎます。現実的な使い分けを整理します。

基準挙動向くアプリ弱点
端末ローカル時刻ユーザーの現地の0時で切り替わる習慣化・日記など「その人の生活時間」が主役時計の手動変更をそのまま信じる
固定タイムゾーン常に同じ地域の0時で切り替わる「日本の今日の運勢」など配信元が主役のコンテンツ深夜帯の海外ユーザーが直感とずれる
サーバ時刻権威ある時刻で切り替わる不正対策が重要な報酬・ランキングオフライン不可・通信コスト・遅延

私が運用している壁紙・癒し系の日替わりアプリでは、配信するコンテンツ側に「その日らしさ」があるため、固定タイムゾーン(Asia/Tokyo)を基準にしています。これで海外にいる読者も「日本の今日の1枚」を共有でき、App Store のサポート問い合わせに来ていた「人によって違う絵が出る」という戸惑いがなくなりました。一方、純粋な習慣トラッカーなら、その人の現地0時で締めるローカル時刻基準のほうが自然です。個人的には、基準は一つに決め、CONTENT_TZ を空文字にしたらローカル、という分岐は作らないことをおすすめします。混在は必ず後で破綻します。

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

この記事の続きを読む

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

この記事で得られること
Date を直接使わず decision を一点に集約する dayKey 関数の実装と、ローカル時刻・固定タイムゾーン・サーバ時刻の使い分け基準
AppState 復帰時に深夜0時のロールオーバーを取りこぼさない再計算と、setTimeout に頼ったときに起きる欠落の回避
端末時計の巻き戻しを単調クロックで検出し、旅行者のタイムゾーン移動でも連続記録を二重カウント・誤消失させないストリーク判定
Stripe による安全な決済 · いつでもキャンセル可能

この記事を購入する

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

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

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

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

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

関連記事

アプリ開発2026-06-27
アップデート後の「新着情報」を、しつこくなく一度だけ出す設計 — バージョン境界とseen状態の作り方
更新後に出すアプリ内「新着情報」画面が、新規インストールにまで出たり毎回出たりして嫌われる問題を、バージョン境界とseen状態の設計で一度だけ確実に届ける。Rork(Expo)の実コードと、複数アプリで使い回す型までまとめます。
アプリ開発2026-08-18
Rork のプロジェクトで targetSdkVersion 36 を通すまでに直した3か所
app.json に targetSdkVersion 36 と書いてあるのに、ビルドが読んでいた値は 35 でした。実効値を報告するスクリプトと、引き上げ・更新停止・延長申請の三択の分け方を残します。
アプリ開発2026-08-16
グラフが壊れて見えるのは、データが多いときではなく最初の数日でした
記録アプリの折れ線グラフが、データの少ない最初の数日だけ線が消える。原因はY軸の幅が0になり座標がNaNになることでした。実測した挙動と、描画前に置く小さな正規化層を紹介します。
📚RECOMMENDED BOOKS
大規模言語モデル入門
山田育矢
LLM開発
生成AIプロンプトエンジニアリング入門
我妻幸長
プロンプト
Claude CodeによるAI駆動開発入門
平川知秀
AI駆動開発
※ アフィリエイトリンクを含みます
もっと見る →