シミュレータの中では、生成されたばかりの SwiftUI アプリはとても気持ちよく動きます。リストが並び、チェックボックスが弾み、画面遷移も滑らかです。ここで「もう出せる」と判断してしまうと、たいてい実機か審査でつまずきます。
写真ライブラリを開いた瞬間にアプリが落ちる。原因は Info.plist の利用目的文字列が空のままだった、それだけ。個人開発でこの種のつまずきを踏んだ方は、少なくないと思います。
生成の速さと、出荷できる状態は別物。まず Rork Max の SwiftUI 生成がどこまでを引き受けるのかを整理し、そのうえで生成後に人間が手を入れる箇所を、実際に書き直したコードとともに並べていきます。
Rork Max の SwiftUI 生成が引き受ける範囲
Rork Max は、Rork の最上位プランです。通常の Rork では React Native ベースのクロスプラットフォーム開発が中心ですが、Rork Max では SwiftUI を使用したネイティブ iOS アプリの自動生成機能が利用できます。
SwiftUI は Apple の推奨フレームワークで、iOS 13 以降に標準搭載されています。ネイティブ開発のメリットは:
- パフォーマンス: React Native よりも軽量で、バッテリー消費が少ない
- OS 機能への直接アクセス: より新しい iOS 機能をいち早く使える
- App Store の信頼性: ネイティブアプリは審査時に有利な傾向
- ユーザー体験: iPhone のプラットフォーム標準に準拠した自然な UI/UX
Rork Max を使えば、このネイティブアプリのメリットを、自然言語プロンプトだけで引き出せます。
SwiftUI 自動生成の仕組み:どのようにネイティブコードが生成されるのか
Rork Max の SwiftUI 生成エンジンは、複数の段階で動作します。
ステップ1: 自然言語の解析と UI スキーマの抽出
まず、あなたが入力した日本語(または英語)のプロンプトから、UI 要素を自動抽出します。
例)「ユーザーが買い物リストを作成・編集・削除できるアプリ。メイン画面にはリスト一覧、各アイテムの右側にはチェックボックス」
Rork Max のエンジンは、ここから以下を読み取ります:
- 画面構成:メイン画面+詳細画面
- UI 部品:リスト表示、チェックボックス、テキスト入力フィールド
- ユーザーアクション:作成・編集・削除
ステップ2: iOS デザイン標準への自動マッピング
抽出された UI 要素が、SwiftUI のネイティブコンポーネントに自動変換されます。
// 例: リスト表示の生成
List {
ForEach(items, id: \.id) { item in
HStack {
Text(item.name)
Spacer()
Image(systemName: item.isChecked ? "checkmark.circle.fill" : "circle")
}
}
}ステップ3: ナビゲーションと状態管理の自動実装
画面遷移やデータの永続化についても、Rork Max が自動的に実装します。
- Navigation:
NavigationStackまたはNavigationViewで構造化 - State Management:
@Stateや@StateObjectで自動配置 - データ永続化: UserDefaults または簡易 SQLite での保存
Rork Max で SwiftUI アプリを作る実践手順
では、実際に Rork Max で SwiftUI ネイティブアプリを作ってみましょう。
1. Rork Max プランにアップグレード
Rork のダッシュボードから、プラン変更画面へ進みます。
- 現在のプラン表示:Free / Starter / Pro / Max から選択可能
- Max を選択 → 決済情報を入力 → サブスクリプション開始
Rork Max の利用可能後、ダッシュボードの左サイドバーに「SwiftUI」オプションが追加されます。
2. 新規プロジェクトを「SwiftUI」モードで作成
ダッシュボード → 「新規プロジェクト」→「iOS (SwiftUI)」を選択します。
- プロジェクト名:例)「Shopping List」
- 説明:アプリの概要を入力(オプション)
- 言語:English / 日本語 から選択
3. プロンプトでアプリ構想を記述
Rork Max の AI エディタに、詳細なアプリの仕様書をプロンプトで記述します。
「買い物リストアプリを作ります。
- メイン画面:今月の買い物リスト一覧
- 各アイテムの左にチェックボックス、右に金額表示
- 下部に「追加」ボタンで新しいアイテムを入力可能
- リスト内の各アイテムをタップすると詳細編集画面へ遷移
- 詳細画面:品名、単価、購入予定日時、カテゴリーを入力可能
- カテゴリーは「食材」「日用品」「衣類」から選択
- 長押しで削除確認ダイアログを表示
」
ポイント: 曖昧さを避け、画面構成・UI 要素・ユーザーアクション を明確に。
4. 自動生成されたコードを確認
プロンプトを送信すると、Rork Max が自動で以下を生成します:
- SwiftUI のソースコード(
.swiftファイル複数) - Assets(アイコン、色定義)
- プロジェクト設定(Info.plist)
生成されたコードは、リアルタイムで iOS シミュレータでプレビュー可能です。
5. ローカル環境へのエクスポート
完成したプロジェクトを Xcode で開けるネイティブプロジェクト形式でダウンロードします。
- 「ダウンロード」ボタン → Xcode プロジェクト(
.xcodeproj)をダウンロード - ローカル Mac で Xcode を開く →
File > Openでプロジェクトを指定 - 必要に応じてカスタマイズ(API 連携、複雑なビジネスロジック等)
生成されたコードのカスタマイズとトラブルシューティング
Rork Max が生成したコードは、SwiftUI の標準的な構造で書かれているため、カスタマイズは容易です。
よくあるカスタマイズ例
API 連携を追加する
生成直後のコードは、多くの場合こういう形で出てきます。動くには動くのですが、このまま出荷すると後で困ります。
// 生成されがちな形(このままにしない)
func fetchItems() {
let url = URL(string: "https://api.example.com/items")!
URLSession.shared.dataTask(with: url) { data, response, error in
if let data = data {
do {
let items = try JSONDecoder().decode([Item].self, from: data)
DispatchQueue.main.async {
self.items = items
}
} catch {
print("Decode error: \(error)")
}
}
}.resume()
}気になる点が 4 つあります。
URL(string:)!の強制アンラップ。URL を設定値やユーザー入力から組み立てるようになった瞬間、ここがクラッシュ地点に変わります。errorを握り潰しています。通信失敗も、サーバーの 500 応答も、JSON の不一致も、画面上ではすべて「何も起きない」に見えます。- HTTP ステータスを見ていません。多くの API はエラー時にエラー内容の JSON を返すため、デコードだけが失敗し、原因の切り分けに時間を取られます。
- 画面を離れてもリクエストが止まりません。戻る操作を繰り返すと、その分だけ通信が積み上がります。
書き直すとこうなります。
enum FetchError: Error {
case badURL
case badStatus(Int)
}
@MainActor
final class ItemStore: ObservableObject {
@Published private(set) var items: [Item] = []
@Published private(set) var errorMessage: String?
func loadItems() async {
do {
guard let url = URL(string: "https://api.example.com/items") else {
throw FetchError.badURL
}
let (data, response) = try await URLSession.shared.data(from: url)
guard let http = response as? HTTPURLResponse else {
throw URLError(.badServerResponse)
}
guard (200..<300).contains(http.statusCode) else {
throw FetchError.badStatus(http.statusCode)
}
items = try JSONDecoder().decode([Item].self, from: data)
errorMessage = nil
} catch let urlError as URLError where urlError.code == .cancelled {
// 画面を離れただけ。ユーザーには何も表示しない
} catch {
errorMessage = "リストを読み込めませんでした"
}
}
}呼び出す側は onAppear ではなく .task を使います。
struct ItemListView: View {
@StateObject private var store = ItemStore()
var body: some View {
List(store.items, id: \.id) { item in
Text(item.name)
}
.task {
await store.loadItems()
}
}
}.task は、ビューが消えるときに中の非同期処理を自動でキャンセルします。onAppear の中で Task { } を起こす形だと、この自動キャンセルは効きません。だからキャンセル時は URLError.Code.cancelled として戻ってくるので、上のコードではそこだけエラー表示から除外しています。一覧と詳細を何度も往復したときの挙動が、ここで分かれます。
クラス全体に @MainActor を付けているのは、@Published の更新をメインスレッドに固定するためです。元のコードの DispatchQueue.main.async は、書き忘れた箇所が一つでもあると紫色の実行時警告になって跳ね返ってきます。型のレベルで縛ってしまう方が、後から自分が触っても壊れません。iOS 17 以降を切れるなら、ObservableObject + @Published を @Observable マクロに置き換えると、記述はさらに短くなります。
複雑な計算ロジックを追加する
// カート内の合計金額を計算
var totalPrice: Double {
items.reduce(0) { $0 + ($1.price * Double($1.quantity)) }
}よくあるトラブルと対処法
| トラブル | 原因 | 対処方法 |
|---|---|---|
| シミュレータでビルドエラー | Xcode バージョンが古い | Xcode の最新版に更新 |
| API 呼び出しが失敗する | HTTP 接続を ATS が遮断している | まず API 側を HTTPS(TLS 1.2 以上)にする。NSExceptionDomains による例外指定は審査で理由の説明を求められるため最後の手段 |
| 写真やカメラを開いた瞬間に落ちる | Info.plist の利用目的文字列が未設定 | NSPhotoLibraryUsageDescription 等を、何にどう使うかが伝わる文言で追加 |
| 紫色の実行時警告が出る | バックグラウンドから UI の状態を更新している | 更新箇所を @MainActor で固定する |
| 画像が表示されない | Assets に画像が未登録 | Xcode の Asset Catalog に画像を追加 |
| UI が崩れて表示される | SwiftUI のバージョン不整合 | ターゲット iOS バージョンを確認 |
生成コードと出荷コードの差が出やすい 5 か所
書き出したプロジェクトを Xcode で何度も開いていると、手が入る場所が毎回だいたい同じであることに気づきます。裏を返せば、ここだけ先に見ておけば実機で慌てずに済みます。
1. Info.plist の利用目的文字列
写真・カメラ・位置情報・通知のうち、プロンプトで触れた機能に対応するキーが入っているかを確認します。空欄や「アプリの機能のため」といった曖昧な文言は、クラッシュか審査での差し戻しにつながります。どのデータを、ユーザーのどの体験のために使うのかを書きます。
2. 状態の持ち主
生成コードは @State を多用しがちです。画面をまたいで共有する値や、ビューが作り直されても残ってほしい値は @State では守れません。@StateObject(iOS 17 以降なら @Observable と @State の組み合わせ)に移し替えます。判断の基準は「この値はビューのものか、アプリのものか」の一点です。
3. 永続化の粒度
UserDefaults が既定で選ばれることが多いのですが、これは設定値のための入れ物です。ユーザーが増やしていくレコードを入れると、起動のたびに全件を読み書きすることになります。件数が伸びる見込みがあるなら、早い段階で SwiftData か SQLite に寄せた方が後が楽になります。
4. リストの一意キー
id に毎回新しい UUID を割り当てていると、再描画のたびに別物として扱われ、アニメーションが飛んだり選択状態が消えたりします。Identifiable の id が、データそのものに紐づく安定した値になっているかを見ます。
5. ダークモードとダイナミックタイプ
生成された色は、リテラル指定のまま残っていることがあります。Color(.systemBackground) などのセマンティックカラーに置き換え、文字サイズを最大にした状態で画面を一巡します。個人開発では見落としやすく、そしてユーザーレビューで指摘されやすい部分でもあります。
App Store 申請に向けた最終確認ステップ
Rork Max で生成したアプリを実際に App Store へ申請する前に、以下をチェックしてください。
1. App ID と Bundle Identifier の設定
Xcode プロジェクトの「General」タブで確認:
- Bundle Identifier:
com.yourcompany.appname形式(逆向きドメイン記法) - Team:Apple Developer アカウントが紐付けられているか
2. バージョンと Build 番号
- Version:
1.0.0形式 - Build:整数値(毎回インクリメント)
3. App Store Connect での事前登録
Apple Developer アカウント → App Store Connect で新規アプリを作成。
- App Name:日本語での正式名称
- Primary Language:日本語
- SKU:一意の内部識別子(例:
shoppinglist.001) - Bundle ID:Xcode と同じ値
4. App の審査ガイドラインへの準拠確認
Apple の App Store Review Guidelines で特に確認すべき項目:
- プライバシー: ユーザーデータ(位置情報、カメラ等)を使う場合は Info.plist で明記
- 安定性: クラッシュテスト、メモリリーク検査を実施
- UI/UX: iOS の標準インタラクション(スワイプ、ジェスチャー等)に準拠しているか
5. 実機でのテスト
iOS デバイス(iPhone)を Mac に接続して、実際の環境でテスト:
# Xcode から実機選択 → ビルド&実行
# または、Product > Scheme > Edit Scheme で実機を指定App Store 申請までの最終チェックリスト:
- [ ] Bundle Identifier 設定完了
- [ ] Version / Build 番号を記録
- [ ] App Store Connect でアプリ登録完了
- [ ] プライバシーポリシー URL を用意
- [ ] スクリーンショット(複数言語版)を用意
- [ ] App の説明文・キーワード・カテゴリーを登録
- [ ] 実機テスト完了・クラッシュなし
- [ ] App Store Review Guidelines を熟読
これらの準備が整えば、「Submit for Review」を実行して申請できます。通常、審査には 24〜48 時間かかります。
生成の速さを、出荷の速さに変えるために
Rork Max が縮めてくれるのは、頭の中にある画面を触れる形にするまでの時間です。そこから先、実機で落ちない状態にたどり着くまでの距離は、以前と変わりません。生成が速くなった分、その距離だけが手つかずで残っている、という言い方の方が近いかもしれません。
今すぐ一つだけ手を動かすとしたら、手元の生成プロジェクトを Xcode で開いて URLSession.shared.dataTask を検索してみてください。見つかったら、この記事の .task と @MainActor の形に置き換えます。一覧と詳細を往復したときの挙動が変わるのが、その場で確かめられるはずです。
私自身、生成コードの読み直しは今も毎回やっています。ここまでお付き合いくださり、感謝いたします。