RORK LABEN
NATIVE — Rork Max は純粋な Swift と SwiftUI を生成するため、AR や LiDAR、Metal を使った 3D、ウィジェット、Dynamic Island、Live Activities、HealthKit、NFC、Core ML まで手が届きますCHOICE — 逆にいえば、こうした Apple 固有の機能を使わないアプリならクロスプラットフォームの通常の Rork で足ります。Max を選ぶ理由はこの一点に集約されますPLAY — 8月31日から、Google Play の新規アプリと既存アプリの更新は対象 API レベル36(Android 16)が必要になります。残り9日ですPLAY — 更新を出さない既存アプリでも、より新しい Android を載せた端末の新規ユーザーへ配信を続けるには対象 API レベル35 以上が要ります。見落とすと新規インストールだけが止まりますGRACE — Play Console の延長フォームから申請すれば11月1日まで猶予されます。自動ではないため、8月31日までに申請そのものを済ませておく必要がありますEXPO — expo prebuild がネイティブディレクトリを破棄して再生成するのが既定になりました。手を入れている場合は no-clean を渡します。API 36 対応の最中は事故になりやすい箇所ですNATIVE — Rork Max は純粋な Swift と SwiftUI を生成するため、AR や LiDAR、Metal を使った 3D、ウィジェット、Dynamic Island、Live Activities、HealthKit、NFC、Core ML まで手が届きますCHOICE — 逆にいえば、こうした Apple 固有の機能を使わないアプリならクロスプラットフォームの通常の Rork で足ります。Max を選ぶ理由はこの一点に集約されますPLAY — 8月31日から、Google Play の新規アプリと既存アプリの更新は対象 API レベル36(Android 16)が必要になります。残り9日ですPLAY — 更新を出さない既存アプリでも、より新しい Android を載せた端末の新規ユーザーへ配信を続けるには対象 API レベル35 以上が要ります。見落とすと新規インストールだけが止まりますGRACE — Play Console の延長フォームから申請すれば11月1日まで猶予されます。自動ではないため、8月31日までに申請そのものを済ませておく必要がありますEXPO — expo prebuild がネイティブディレクトリを破棄して再生成するのが既定になりました。手を入れている場合は no-clean を渡します。API 36 対応の最中は事故になりやすい箇所です
記事一覧/Rork入門
Rork入門/2026-08-22初級

変えられる名前と、変えられない名前 — Rork で最初の1本を公開する前の30分

アプリには名前が3種類あり、公開後に変えられるのは表示名だけです。Rork が既定で入れる ID をそのまま提出する前に、依存ゼロのスクリプトで検査する手順をまとめました。

Rork542Expo178App Store89Google Play28bundleIdentifierはじめて

壁紙アプリ6本を1つのコードベースから出し分ける構成に切り替えたときのことです。ビルド設定を書き換えながら、ふと手が止まりました。

android.package の文字列が、6本のうち2本だけ命名の規則からずれていました。

その2本は、私がまだ「あとで整えればいい」と思っていた時期に公開したものです。整えることは、もうできませんでした。

アプリには名前が3種類あります。ストアに表示される名前、開発中に使う slug、そしてアプリそのものを識別する ID です。このうち公開したあとで変えられるのは、最初の1つだけです。

Rork は生成の時点で ID を自動的に埋めてくれます。動くものがすぐ手元に届くので、そのまま提出まで進んでしまいがちです。けれど提出を押した瞬間、その文字列は固定されます。

最初の1本を出す前の30分で確かめておきたいことを、順に整理していきます。

公開したあとで変えられるのは、表示名だけです

ストアに関わる項目を、変更できるものとできないもので分けると次のようになります。

項目App StoreGoogle Play公開後の変更
表示名(アプリ名)新しいバージョンの審査と一緒に変更ストア掲載情報から変更できます
アイコン・スクリーンショットバージョン単位で差し替えいつでも差し替えできます
説明文・カテゴリ審査と一緒に変更随時変更できます
Bundle ID / パッケージ名App Store Connect に登録した時点で固定公開した時点で固定できません
SKU登録時に決めたまま該当なしできません
ストアの URL登録時に割り当てられる数値 IDパッケージ名がそのまま URL に入りますできません

注目していただきたいのは最後の行です。Google Play のストア URL には、パッケージ名がそのまま文字列として現れます。つまり Android では、ID は開発者だけの都合ではなく、利用者の目に触れる情報でもあります。

App Store の Bundle ID は「公開後」ではなく「App Store Connect にアプリを登録した時点」で固定されます。まだ審査に出していなくても、登録を済ませていれば変えられません。ここは私も一度取り違えました。

なお署名鍵は少し事情が異なり、Google Play ではアップロード鍵の入れ替えを申請できます。鍵の不一致でつまずいた場合は、Rorkアプリを Google Play にアップロードできない署名キーエラーの直し方に手順をまとめてあります。

Rork が入れる ID は、雛形のままのことがあります

Rork が書き出したプロジェクトの app.json(もしくは app.config.ts)には、次のような記述が入っています。

{
  "expo": {
    "name": "wallpaper",
    "slug": "wallpaper",
    "ios": { "bundleIdentifier": "com.anonymous.wallpaper" },
    "android": { "package": "com.anonymous.wallpaper" }
  }
}

com.anonymous. で始まる文字列は Expo の既定値です。生成物としては正しく、ビルドも通り、実機にも入ります。動いてしまうので、疑う理由がありません。

ここを自分のものに置き換える基準は、そう複雑ではありません。

  • ドメインを持っているなら、逆順にしたものを先頭に置きます(dolice.net なら net.dolice.
  • 持っていないなら、実在するアカウント名に紐づけます(io.github.<アカウント名>. など)
  • 末尾はプロダクトを表す短い語にします

避けたほうがよいのは、後から意味がずれる語を入れてしまうことです。freev22026ios といった語を末尾に足すと、無料版を有料化したときや作り直したときに、ID だけが過去の判断を指し続けます。私は一度これをやって、2本目以降の命名を引きずりました。

公開前に、機械で確かめます

目視での確認は、6本目あたりから確実に精度が落ちます。私は次のスクリプトを提出前に走らせるようにしました。依存パッケージはありません。Node があれば動きます。

// check-app-ids.mjs
import { readFileSync, existsSync } from "node:fs";
 
const RESERVED = new Set(["abstract","assert","boolean","break","byte","case","catch",
  "char","class","const","continue","default","do","double","else","enum","extends",
  "final","finally","float","for","goto","if","implements","import","instanceof","int",
  "interface","long","native","new","package","private","protected","public","return",
  "short","static","strictfp","super","switch","synchronized","this","throw","throws",
  "transient","try","void","volatile","while"]);
 
const PLACEHOLDER = [/^com\.anonymous\./i, /^host\.exp\./i, /^com\.example\./i,
  /\byourname\b/i, /\byourcompany\b/i, /\bmyapp\b/i, /\btest\b/i];
 
function readConfig(path) {
  if (!existsSync(path)) return null;
  const raw = JSON.parse(readFileSync(path, "utf8"));
  return raw.expo ?? raw;
}
 
function checkAndroidPackage(pkg) {
  const errs = [];
  if (!pkg) return ["android.package が未設定です"];
  const segs = pkg.split(".");
  if (segs.length < 2) errs.push("ドット区切りが2つ未満です");
  for (const s of segs) {
    if (!/^[a-zA-Z][a-zA-Z0-9_]*$/.test(s)) errs.push(`セグメント "${s}" が英字始まりの条件を満たしません`);
    if (RESERVED.has(s)) errs.push(`セグメント "${s}" は Java の予約語です`);
    if (/[A-Z]/.test(s)) errs.push(`セグメント "${s}" に大文字が含まれます`);
  }
  return errs;
}
 
function checkBundleId(id) {
  const errs = [];
  if (!id) return ["ios.bundleIdentifier が未設定です"];
  if (!/^[A-Za-z0-9.-]+$/.test(id)) errs.push("使用できない文字が含まれます");
  if (id.split(".").length < 2) errs.push("ドット区切りが2つ未満です");
  if (/_/.test(id)) errs.push("アンダースコアは使えません");
  return errs;
}
 
const path = process.argv[2] ?? "app.json";
const cfg = readConfig(path);
if (!cfg) { console.error(`✗ ${path} が見つかりません`); process.exit(2); }
 
const ios = cfg.ios?.bundleIdentifier;
const android = cfg.android?.package;
const findings = [];
 
for (const e of checkBundleId(ios)) findings.push(["BLOCK", "ios.bundleIdentifier", e]);
for (const e of checkAndroidPackage(android)) findings.push(["BLOCK", "android.package", e]);
 
for (const [key, val] of [["ios.bundleIdentifier", ios], ["android.package", android]]) {
  if (val && PLACEHOLDER.some((re) => re.test(val))) {
    findings.push(["BLOCK", key, `"${val}" は雛形のままの疑いがあります`]);
  }
}
if (ios && android && ios.toLowerCase() !== android.toLowerCase()) {
  findings.push(["WARN", "ios/android", `ID が一致しません(iOS: ${ios} / Android: ${android})`]);
}
if (!cfg.name) findings.push(["WARN", "name", "表示名が未設定です"]);
if (!cfg.slug) findings.push(["WARN", "slug", "slug が未設定です"]);
 
console.log(`# ${path}`);
console.log(`  ios  : ${ios ?? "(なし)"}`);
console.log(`  and  : ${android ?? "(なし)"}`);
if (findings.length === 0) { console.log("✓ 公開前に固定してよい状態です"); process.exit(0); }
for (const [lv, key, msg] of findings) console.log(`  ${lv === "BLOCK" ? "✗" : "!"} [${lv}] ${key}: ${msg}`);
process.exit(findings.some(([lv]) => lv === "BLOCK") ? 1 : 0);

検査したいのは3つです。雛形のまま残っていないか、Android のパッケージ名に使えない語が入っていないか、iOS と Android で食い違っていないか。

2つ目について補足させてください。Expo は app.jsonandroid.package を、Gradle の applicationId と、生成される Java / Kotlin のパッケージ名の両方に使います。そのため newclass のような予約語をセグメントに入れると、設定としては自然に見えるのにコンパイルで落ちます。原因がパッケージ名にあるとは思いにくい種類の失敗です。

手元の4パターンで実際に走らせた出力が、次のものです。

$ node check-app-ids.mjs a-default.json
# a-default.json
  ios  : com.anonymous.wallpaper
  and  : com.anonymous.wallpaper
  ✗ [BLOCK] ios.bundleIdentifier: "com.anonymous.wallpaper" は雛形のままの疑いがあります
  ✗ [BLOCK] android.package: "com.anonymous.wallpaper" は雛形のままの疑いがあります
exit=1
 
$ node check-app-ids.mjs b-mismatch.json
# b-mismatch.json
  ios  : net.dolice.calmwall
  and  : net.dolice.calm_wall
  ! [WARN] ios/android: ID が一致しません(iOS: net.dolice.calmwall / Android: net.dolice.calm_wall)
exit=0
 
$ node check-app-ids.mjs c-reserved.json
# c-reserved.json
  ios  : net.dolice.new.zentimer
  and  : net.dolice.new.zentimer
  ✗ [BLOCK] android.package: セグメント "new" は Java の予約語です
exit=1
 
$ node check-app-ids.mjs d-ok.json
# d-ok.json
  ios  : net.dolice.zentimer
  and  : net.dolice.zentimer
✓ 公開前に固定してよい状態です
exit=0

2番目を BLOCK ではなく WARN にしてある理由も書いておきます。calm_wall は Android では正当なパッケージ名です。アンダースコアが使えないのは iOS 側だけで、両者を揃えようとした結果としてずれただけ、という場合があります。機械が判断を代わりに下せない箇所なので、止めずに知らせるにとどめました。

exit の値を返すようにしてあるので、npm run のスクリプトやビルド前のフックにそのまま挟めます。

複数本を出すつもりなら、規則を先に決めます

1本で終わるつもりでも、うまくいけば2本目を作ることになります。そのときに効いてくるのが、最初の1本で決めた規則です。

私が6本で使っている形は <逆ドメイン>.<プロダクト略称> だけの2〜3セグメントです。プラットフォーム名も、版数も、配布形態も入れません。ID に情報を持たせるほど、情報が古くなったときに動かせない部分が増えていきます。

表示名のほうには、いくらでも情報を持たせて構いません。そちらは後から変えられるからです。変えられる場所に変わりうる情報を置き、変えられない場所には変わらない情報だけを置く、という切り分けが結局いちばん長持ちしました。

1つのコードベースから複数本を出し分ける具体的な構成は、壁紙アプリ6本を1つのコードベースから出し分ける — app.config.ts と EAS で組むホワイトラベル構成に、ビルドプロファイルの分け方まで含めて書いてあります。

すでに公開してしまった場合に取れる道

もう出してしまった、という方もいらっしゃると思います。取れる道は2つです。

そのまま使い続ける。 ID は Android のストア URL を除けば、利用者の目にほとんど触れません。表示名を変えれば見た目の問題は解消します。私の6本のうち、規則からずれた2本はこの判断で残してあります。数年後に見返す自分だけが気にする種類の不揃いだと考えたためです。

新しい ID で出し直す。 引き継げないものが多いことは、先に把握しておいたほうがよいと思います。ダウンロード数、評価とレビュー、ストア内の掲載順位の履歴、既存ユーザーの自動更新は、いずれも引き継げません。旧アプリから新アプリへ誘導する導線を自分で作る必要があります。コード・アセット・課金商品の定義は移せますが、課金商品はストア側で作り直しになります。

ひとつだけ補足させてください。アプリの登録を削除しても、ID は解放されません。App Store Connect では、一度アプリに割り当てた Bundle ID は登録を消したあとも自分のアカウントに予約されたまま残ります。Google Play も、内部テストを含むいずれかのトラックにビルドを上げた時点でパッケージ名が恒久的に予約されます。取り戻す道を探すより、新しい ID を決めるほうが早い場面です。

判断の分かれ目は、レビュー件数だと思っています。レビューがまだ数件であれば出し直しの損失は小さく、三桁に届いているなら、ID の不揃いを抱えたまま進むほうがおそらく合理的です。

公開前の30分でやること

  1. app.json または app.config.ts を開き、ios.bundleIdentifierandroid.package を確認します
  2. 上のスクリプトを走らせ、BLOCK が0件になるまで直します
  3. 末尾に free v2 2026 ios のような、後から意味がずれる語が入っていないか読み返します
  4. 2本目を出すときにどう伸ばすかを、1行でよいのでメモに残します

この4つを終えてから App Store Connect と Play Console にアプリを登録してください。順番が逆になると、直せるものが直せなくなります。

個人で開発していると、審査に出すまでの工程を全部ひとりで抱えることになります。だからこそ、機械に任せられる確認は機械に渡しておきたいと私は考えています。上のスクリプトは、その最初の一枚です。

最初の1本を出す日は、決めることが多すぎて、名前のような地味な項目が後回しになりがちです。私もそうでした。この記事が、その日の30分を守るのに役立てば嬉しく思います。お読みいただきありがとうございました。

シェア

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

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

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

もしこの記事がお役に立ちましたら、チップ(¥150)で応援いただけると大変励みになります。広告なしでの運営を続けるため、皆さまのご支援が大きな力になっています。

関連記事

Rork 入門2026-08-19
審査がいつ明けるか読めないなら、公開日は審査の外側で決めます
審査の所要時間は開発者側で短縮できませんが、公開の瞬間は自分で決められます。App Store Connect の3つのリリース設定と Google Play の公開の管理、そして eas submit との噛み合わせを整理します。
Rork 入門2026-04-18
RorkアプリをiOS・Android同時公開したときの手順と、プラットフォームごとに詰まったこと
RorkアプリをApp StoreとGoogle Playに同時公開した実体験をもとに、スクリーンショット仕様・プライバシー設定・審査期間の違いなど、両プラットフォームで詰まりやすいポイントを具体的に解説します。
Rork 入門2026-08-17
Rork のコードをエクスポートしたら、git init より先に .gitignore を用意します
Rork からエクスポートしたプロジェクトをそのまま git init すると、鍵や .env まで履歴に載ります。除外設定を先に置いた場合と後から足した場合の差を実際に測り、効く書き方と効かない書き方を確かめました。
📚RECOMMENDED BOOKS
大規模言語モデル入門
山田育矢
LLM開発
生成AIプロンプトエンジニアリング入門
我妻幸長
プロンプト
Claude CodeによるAI駆動開発入門
平川知秀
AI駆動開発
※ アフィリエイトリンクを含みます
もっと見る →