import { Callout } from '@/components/ui/callout';
無料枠を試したとき、私が最初に外したのは残りメッセージ数の読み方ではありませんでした。作ったものを Rork の外へ持ち出す作業を、枠が尽きてから始めたことです。
生成されたプロトタイプは画面の中では動いていました。けれど手元に降ろしてみると、ビルドが始まる前に止まりました。足りなかったのはコードではなく、コードの外側にある識別子でした。
無料枠の価値は「何画面作れたか」では測れません。Rork の外で動くものが残ったかどうかで測るほうが、課金の判断がずっと速くなります。ここではその測り方を、実際に走る検査スクリプトと一緒に置いておきます。
無料枠を止めるのは日数ではなくメッセージ数です
「無料トライアルは何日間ですか」という聞き方をすると、Rork の制約は見えてきません。止まる理由は日数ではないためです。
| 思い込み | 実際に効いている制約 |
|---|---|
| 期限が来たら止まる | メッセージ枠を使い切ったら止まる。何もしなければ何日でも残る |
| 失敗した生成はノーカウント | 意図と違う出力でも1往復は消費される。曖昧なプロンプトほど早く減る |
| 有料に上げれば残りが合算される | プランは月単位。切り替えると新しい月の枠が始まる |
| 実機で動かすところから有料 | Companion での実機確認は無料の範囲で体験できる |
1往復=1メッセージという単位なので、消費量を決めるのは作るものの規模ではなく、指示のやり直し回数です。私の場合、画面1枚の小さなアプリで30〜80、機能が3つ4つ絡むと150〜300というあたりに落ち着きました。作り込みではなく手戻りで倍近く変わる、という感覚です。
プラン名と金額は改定が入ります。金額を根拠に判断するときは、記事の表ではなく Rork の料金ページで当日の数字を確認してください。プラン間の考え方の違いはRorkの料金プランを正直に比較するに整理しています。
無料枠の成果は「Rork の外で動くか」で測ります
Rork には Export があり、React Native + Expo のプロジェクト一式を GitHub へ出せます。無料枠のうちに一度これを通しておくと、トライアルの成果が消えません。
ただし、書き出したプロジェクトがそのままビルドできるとは限りません。Rork の画面で動いている間は不要だった設定が、外に出た瞬間に必須になるためです。しかも npm install を回してから気付くので、確認が後ろにずれます。
そこで、依存を入れる前に不足を洗い出す小さなスクリプトを使っています。Node の標準モジュールだけで動きます。
#!/usr/bin/env node
// audit-export.mjs — Rork から書き出した Expo プロジェクトが
// Rork の外側でビルドできる状態かを、npm install より前に判定する。
import { readFileSync, existsSync, readdirSync, statSync } from "node:fs";
import { join, extname } from "node:path";
const root = process.argv[2] ?? ".";
const read = (p) => JSON.parse(readFileSync(join(root, p), "utf8"));
const problems = [];
const flag = (level, msg, why) => problems.push({ level, msg, why });
// 1) app.json(app.config.js は評価が要るのでここでは対象外)
if (!existsSync(join(root, "app.json"))) {
flag("FAIL", "app.json がありません", "app.config.js なら expo config --json で出力してから再実行してください");
} else {
const { expo = {} } = read("app.json");
if (!expo.ios?.bundleIdentifier) flag("FAIL", "ios.bundleIdentifier 未設定", "EAS build が開始前に停止します");
if (!expo.android?.package) flag("FAIL", "android.package 未設定", "同上(Android 側)");
if (!expo.extra?.eas?.projectId) flag("FAIL", "extra.eas.projectId 未設定", "eas build / submit がプロジェクトを解決できません");
if (!expo.scheme) flag("WARN", "scheme 未設定", "ディープリンクと認証リダイレクトが戻ってきません");
}
// 2) コード中の EXPO_PUBLIC_* と .env の突き合わせ
const walk = (dir) =>
readdirSync(dir).flatMap((name) => {
if (name === "node_modules" || name.startsWith(".")) return [];
const p = join(dir, name);
return statSync(p).isDirectory()
? walk(p)
: [".ts", ".tsx", ".js", ".jsx"].includes(extname(name)) ? [p] : [];
});
const envFile = existsSync(join(root, ".env")) ? readFileSync(join(root, ".env"), "utf8") : "";
const defined = new Set([...envFile.matchAll(/^([A-Z0-9_]+)=/gm)].map((m) => m[1]));
const used = new Map();
for (const file of walk(root)) {
const src = readFileSync(file, "utf8");
for (const m of src.matchAll(/process\.env\.(EXPO_PUBLIC_[A-Z0-9_]+)/g)) {
if (!used.has(m[1])) used.set(m[1], file);
}
}
for (const [name, file] of used) {
if (!defined.has(name)) flag("FAIL", `${name} が .env にありません`, `${file} で参照されています`);
}
// 3) 出力
const fails = problems.filter((p) => p.level === "FAIL").length;
console.log(`export audit: ${root} (EXPO_PUBLIC_* 参照 ${used.size} 件)`);
for (const p of problems) console.log(`[${p.level}] ${p.msg}\n → ${p.why}`);
console.log(problems.length === 0 ? "問題なし" : `blockers ${fails} / warnings ${problems.length - fails}`);
process.exit(fails > 0 ? 1 : 0);書き出した直後の Expo プロジェクト(画面2枚・環境変数2つを参照)に当てたときの出力です。
export audit: . (EXPO_PUBLIC_* 参照 2 件)
[FAIL] ios.bundleIdentifier 未設定
→ EAS build が開始前に停止します
[FAIL] android.package 未設定
→ 同上(Android 側)
[FAIL] extra.eas.projectId 未設定
→ eas build / submit がプロジェクトを解決できません
[WARN] scheme 未設定
→ ディープリンクと認証リダイレクトが戻ってきません
[FAIL] EXPO_PUBLIC_ANALYTICS_KEY が .env にありません
→ app/index.tsx で参照されています
blockers 4 / warnings 1app.json に3項目を足し、.env に足りないキーを1行追加してから再実行すると、こうなります。
export audit: . (EXPO_PUBLIC_* 参照 2 件)
問題なし終了コードで判定しているので、そのまま CI にも置けます。無料枠の最終日にこれが 問題なし で返れば、トライアルは資産を残したことになります。
監査が指摘する項目の意味
上の5項目は、いずれも「Rork の中では要らなかったもの」です。中身を知っておくと、指摘が出たときに迷いません。
| 項目 | 外に出た瞬間に必要になる理由 | 後から変えられるか |
|---|---|---|
| ios.bundleIdentifier | App Store Connect 上のアプリの同一性そのもの。EAS はこれが無いと開始しない | 公開後は実質不可 |
| android.package | Google Play 側の同一性。iOS と揃えるのが管理上は楽 | 公開後は不可 |
| extra.eas.projectId | ビルドと OTA 更新の宛先。ローカルには影響しないがクラウドビルドで詰まる | 変更可 |
| scheme | ディープリンクと認証コールバックの戻り先 | 変更可(既存リンクは切れる) |
| EXPO_PUBLIC_* | Rork 側に置いた値は書き出しに含まれないことがある | 変更可 |
右端の列が本題です。上2つは後から取り返しがつきません。無料のうちに仮の値で埋めてしまうと、そのまま公開して固定される事故が起きます。命名で迷ったら変えられる名前と、変えられない名前を先に読んでおくと安全です。
EXPO_PUBLIC_ は名前のとおりクライアントに埋め込まれます。秘密鍵をここに置かないでください。値は必ず YOUR_API_KEY のようなプレースホルダから始め、実鍵はサーバー側かビルド時のシークレットに寄せます。
無料枠を溶かす4つの落とし穴
曖昧なプロンプトで往復が増える
枠を減らすのは機能の多さではなく、やり直しの回数です。最初の1往復に画面構成・データの形・使う API を書き切るほうが、結果的に安く済みます。
大きい機能を一度に頼む
一度に頼むと、直したい箇所だけを直せなくなります。1機能ずつ完成させて、そのつど Companion で実機に降ろす進め方が、枠の消費としても一番読みやすくなります。無料の実機テストをどこまで引っ張れるかはRork Companion の無料実機テストをどこまで引っ張れるかにまとめました。
手で直したコードが再生成で消える
書き出したあとに手で直し、また Rork 側で生成を回すと、直した箇所が戻ることがあります。境界の引き方はRork の再生成と手動修正を共存させるが詳しいです。無料枠のうちに、この境界を体で覚えておくと後が楽になります。
「いつか試す」で取得して失効させる
無料クレジットには有効期限があります。私も、使うつもりで取得したまま忘れて失効させたことがあります。アカウントを作るのは、触る時間を確保できた日にしてください。
課金に踏み切る線引き
判断材料は、無料枠が終わる時点で手元に揃っています。
| 無料枠の終わり方 | 次にやること |
|---|---|
監査が 問題なし。実機で触って手応えがある | 課金して公開まで進める。審査の往復まで含めると、1本のリリースで200〜400メッセージは見ておく |
| 監査は通るが、作りたいものがまだ言葉になっていない | 課金しない。仕様が固まっていない状態の往復が、枠を最も速く溶かす |
| UI は出るが、必要な機能が Rork だけでは閉じない | 書き出したプロジェクトを Expo CLI 側で進める判断も含めて比較する |
| 触る時間が取れないまま枠が残っている | 課金しない。枠ではなく時間が制約になっている |
| 2本目以降を並行して作る予定がある | プロジェクトを横断できる上位プランのほうが1本あたりは下がる |
私自身は、無料枠を「作る場所」ではなく「相性を確かめる場所」として使うようになってから、判断が速くなりました。作るのは課金してからで間に合います。確かめるのは、無料のうちにしかできません。
次の一手
まだ触っていなければ、Rork でアカウントを作り、画面を1枚だけ生成して、その日のうちに Export まで通してください。そのうえで上のスクリプトを走らせれば、blockers の数が最初の成果になります。数字が0になった時点で、課金するかどうかの材料は揃っています。
無料枠は、使い切るためのものではなく、判断を早めるためのものだと考えています。お読みいただきありがとうございました。