RORK LABEN
TOOLING — Rorkの開発者向けリポジトリが動き続けています。rork-xcodeが7月16日、rork-deviceが7月15日、rork-plistが7月13日に更新されましたOPUS46 — RorkでClaude Opus 4.6が稼働しています。Rork MaxはClaude Codeを土台にアプリを組み立てる設計ですSIM — ブラウザ上で動くクラウドのiOSシミュレータを備え、実機へのインストールは1クリック、App Storeへの提出は2クリックと案内されていますMAX — Rork MaxはReact Nativeではなく純粋なSwiftを出力します。iPhone・iPad・Apple Watch・Apple TV・Vision Pro、そしてiMessageまでが射程ですNATIVE — HealthKit、ARKitとLiDAR、NFC、Dynamic Island、Live Activities、Metalによる3D、Core MLのオンデバイス推論まで扱えますSEED — RorkはLeft Lane Capitalが主導する1,500万ドルのシードラウンドを実施し、Peak XVとa16z Speedrunが参加しましたTOOLING — Rorkの開発者向けリポジトリが動き続けています。rork-xcodeが7月16日、rork-deviceが7月15日、rork-plistが7月13日に更新されましたOPUS46 — RorkでClaude Opus 4.6が稼働しています。Rork MaxはClaude Codeを土台にアプリを組み立てる設計ですSIM — ブラウザ上で動くクラウドのiOSシミュレータを備え、実機へのインストールは1クリック、App Storeへの提出は2クリックと案内されていますMAX — Rork MaxはReact Nativeではなく純粋なSwiftを出力します。iPhone・iPad・Apple Watch・Apple TV・Vision Pro、そしてiMessageまでが射程ですNATIVE — HealthKit、ARKitとLiDAR、NFC、Dynamic Island、Live Activities、Metalによる3D、Core MLのオンデバイス推論まで扱えますSEED — RorkはLeft Lane Capitalが主導する1,500万ドルのシードラウンドを実施し、Peak XVとa16z Speedrunが参加しました
記事一覧/AIモデル
AIモデル/2026-04-27上級

Rork Max で SwiftUI ネイティブアプリを生成する — 設計から App Store リリースまでの実装ガイド

Rork Max は SwiftUI ネイティブアプリ生成の精度が一段上がりました。私が実際に App Store 申請まで通したアプリで使った設計判断・プロンプト・修正パターンを、コードと共に体系化します。

Rork Max229SwiftUI63iOS開発15ネイティブアプリ20App Store78実装ガイド

Rork Max が SwiftUI を生成するようになった当初は、正直「動くけれど App Store には出せない」コードがほとんどでした。それが 2026年春時点では、App Store 審査基準を意識した設計になっています。

私が2014年から個人アプリ開発をやってきた経験と、今年初めて Rork Max で App Store まで通したアプリの過程を、具体的なプロンプトとコード修正パターンで共有します。

Rork Max の SwiftUI 生成精度を理解する

まず知っておくべきは、Rork Max が生成する SwiftUI コードは「概要レベルの正確さ」です。つまり、アーキテクチャは正しいが、細部(State 管理、エラーハンドリング、Accessibility)では修正が必要な場合がほとんどです。

これを理解すれば、「Rork Max は自動化の道具ではなく、設計を高速化する道具」という使い方が見えてきます。

生成される SwiftUI の特徴

Rork Max が得意とするのは:

  • UI レイアウト: VStack、HStack、ZStack の構成が正確
  • 基本的な State 管理: @State、@ObservedObject の宣言が機能する
  • データバインディング: TextField、Picker などのバインディング構文が正確

修正が必要になるのは:

  • 複雑な State 管理: 複数の @State が互いに依存する場合、ロジックが重複・矛盾することがある
  • 非同期処理: async/await の実装が正確さを欠き、Race Condition や キャンセル漏れが起きる
  • Accessibility: VoiceOver 対応の accessibilityLabelaccessibilityHint がなく、見落とし多数

Rork Max で「破綻しない SwiftUI」を引き出す6つの設計指針

指針1: 画面設計を「State ツリー」として描写する

プロンプトで単に「ユーザーが タスクを入力できる画面を作って」と書くのではなく、その画面で管理すべき State を明示します。

悪い例:

タスク管理アプリを作ってください。ユーザーが新しいタスクを入力できる機能が必要です。

良い例:

タスク管理アプリの「新規タスク追加」画面を作ってください。

State:
- taskTitle: String(入力フィールドの値)
- selectedDate: Date(期日ピッカー)
- priority: Priority(enum: low, medium, high)
- isLoading: Bool(保存中フラグ)
- errorMessage: String?(エラー表示)

挙動:
- 空の状態では「保存」ボタンを無効化する
- 保存中は入力フィールドを無効化する
- エラー時は赤いテキストで errorMessage を表示する

この詳細度で書くことで、Rork Max が State の構造を正確に把握でき、生成コードの矛盾が劇的に減ります。

指針2: 非同期処理は「タスク」単位で分離する

Rork Max は async/await を使いますが、複数の async 処理が同じ関数内で動作すると、Race Condition を作りやすくなります。

// ❌ Rork Max が生成しやすい(危険)
func saveAndFetch() async {
    isLoading = true
    await saveTask()
    await fetchTasks()
    isLoading = false
}
 
// ✅ 修正版:タスク分離と キャンセル管理
@State private var savingTask: Task<Void, Never>?
@State private var fetchingTask: Task<Void, Never>?
 
func saveTask() async {
    savingTask = Task {
        isLoading = true
        defer { isLoading = false }
        try await api.save(task)
    }
    await savingTask?.value
}
 
func fetchTasks() async {
    fetchingTask?.cancel()
    fetchingTask = Task {
        let tasks = try await api.fetch()
        self.tasks = tasks
    }
}

プロンプトで「非同期処理は Task{ } で管理して、キャンセル可能にしてください」と明示することで、Rork Max の出力品質が上がります。

指針3: @StateObject と @EnvironmentObject を明確に分離する

Rork Max は時々、ローカル State と グローバル State を混在させます。

// プロンプト内で明示
// ローカル State: @State var isLoading: Bool
// グローバル State: AppState(@EnvironmentObject)をインジェクト
// AppState 内の変更:
//   - appState.tasks の更新
//   - appState.currentUser の参照

このように明確に書くことで、Rork Max が State の責任範囲を正確に理解します。

指針4: エラーハンドリングを「3段階」で指定

Rork Max がよく忘れるのが、エラーの「種類」による使い分けです。

// ❌ よくある問題
if let error = error {
    Text("エラーが発生しました")
}
 
// ✅ 指定すべき要件
// 1. ネットワークエラー:「インターネット接続を確認」と表示
// 2. バリデーションエラー:「タイトルは1文字以上入力」と表示
// 3. サーバーエラー:「もう一度お試しください」と表示

プロンプトでこの3段階を列挙すると、Rork Max の出力に if-else チェーンで対応が含まれるようになります。

指針5: View が肥大化しないよう「責任分割」を指示

Rork Max は「1つの View に全機能」を詰め込みやすくなります。

// プロンプト内で明示
struct TaskListView: View {
    // 記述:表示ロジックのみ
}
 
struct TaskListHeader: View {
    // 記述:ヘッダー部のみ
}
 
struct TaskRowView: View {
    // 記述:1行アイテムのみ
}
 
struct TaskListViewModel: ObservableObject {
    // 記述:フェッチ・削除・フィルタリングの全ロジック
}

この分割を事前に指示しておくと、Rork Max の出力がそれに従う傾向が強まります。

指針6: Accessibility を「最初から組み込む」指示

App Store 審査では Accessibility の不備で リジェクトされることも増えています。

// プロンプト内で明示
Text("タスク: \(task.title)")
    .accessibilityLabel("タスク")
    .accessibilityValue(task.title)
 
Button("削除") { deleteTask() }
    .accessibilityLabel("タスクを削除")
    .accessibilityHint("このアクションは取り消せません")

「最初から accessibilityLabel と accessibilityHint を含めてください」と指示すると、Rork Max の出力にもそれが反映される傾向があります。

実装事例:完全なプロンプト履歴

私が App Store 通過まで使ったアプリの、実際のプロンプトを3段階で示します。

Batch 1: 基本設計(初稿要求)

Rork Max:以下のタスク管理アプリの初版を SwiftUI で生成してください。

【アプリの概要】
- ユーザーがタスクを追加・編集・削除できる
- タスクは「今日」「明日」「今週」の3つのセクションに分かれる
- 各セクションの下部には「セクション内の全完了数」を表示

【State 設計】
- tasks: [Task](全タスク配列)
- selectedDate: Date(現在のフォーカス日)
- isEditing: Bool(編集モード フラグ)
- editingTaskID: UUID?(編集中のタスク)
- showAddModal: Bool(新規追加モーダル表示)

【UI コンポーネント】
- TaskListView: 親コンテナ
- TaskSectionView: セクションごとの表示(「今日」「明日」「今週」)
- TaskRowView: 1行アイテム
- AddTaskModal: 新規・編集モーダル

【非同期処理】
- addTask() async: 新規タスクをサーバーに POST
- updateTask() async: 既存タスクを PATCH
- deleteTask() async: タスクを DELETE

生成時の注意:
- 各 View は責任を明確に分割する
- errorMessage: String? で エラー表示を管理する
- isLoading: Bool で ボタンと入力フィールドの無効化を制御する

Rork Max の出力

  • 基本的な View 構造が正確に生成される
  • State の宣言が完全
  • ただし非同期処理の詳細にエラーハンドリングが不足

Batch 2: 修正指示(非同期・エラーハンドリング改善)

Batch 1 の TaskListViewModel に以下の修正を加えてください:

【非同期処理の改善】
1. addTask() で以下の分岐を実装:
   - ネットワークエラー:errorMessage = "インターネット接続を確認してください"
   - バリデーションエラー:errorMessage = "タスク名は1文字以上入力してください"
   - サーバーエラー(500以上):errorMessage = "サーバーエラー。もう一度お試しください"

2. deleteTask() でキャンセル管理を追加:
   - deletingTaskID: UUID? で削除中タスクを追跡
   - 削除中は該当行の操作ボタンを無効化
   - 削除完了またはキャンセルで deletingTaskID = nil

3. fetchTasks() でキャンセル可能にする:
   - @State private var fetchTask: Task<Void, Never>? で管理
   - 新しいフェッチ要求時に fetchTask?.cancel() を実行
   - Race Condition を防止

【エラーハンドリングの詳細】
// 以下のコード例を参考に実装
do {
    let response = try await api.addTask(task)
    tasks.append(response)
    errorMessage = nil
} catch URLError.notConnectedToInternet {
    errorMessage = "インターネット接続を確認してください"
} catch let validationError as ValidationError {
    errorMessage = validationError.message
} catch {
    errorMessage = "予期しないエラーが発生しました"
}

Rork Max の出力

  • エラーハンドリングが詳細化
  • ただし Race Condition 対策が依然不完全な場合がある
  • @State private var fetchTask の宣言は生成されるが、キャンセル実装が抜ける可能性

Batch 3: 最終調整(Accessibility・App Store 対応)

完成版に以下の要件を追加してください:

【Accessibility 対応】
全ての以下のテキスト・ボタンに accessibilityLabel を付与:
- タスク行:accessibilityLabel = "タスク: \(task.title), 期日: \(task.dueDate)"
- 完了チェック:accessibilityLabel = "完了", accessibilityHint = "このタスクを完了状態に切り替え"
- 削除ボタン:accessibilityLabel = "削除", accessibilityHint = "このアクションは取り消せません"

【App Store 審査対応】
1. プライバシーポリシーへのリンク:
   - フッターに「プライバシーポリシー」を表示
   - タップで Safari で https://example.com/privacy を開く

2. 利用規約へのリンク:
   - フッターに「利用規約」を表示
   - タップで Safari で https://example.com/terms を開く

3. お問い合わせリンク:
   - 設定画面に「サポート」ボタンを追加
   - メールアプリで support@example.com を開く

4. バージョン表示:
   - 設定画面に「バージョン x.x.x」を表示
   - Bundle.main.infoDictionary?["CFBundleShortVersionString"] から取得

【パフォーマンス対応】
- 100件以上のタスクを効率的に表示するため、TaskListView に LazyVStack を使用
- リスト外部の無駄なレンダリング削除

Rork Max の出力

  • accessibilityLabel は手厚く追加される
  • App Store リンク(プライバシー・利用規約)が正確に生成される
  • バージョン表示のバンドル取得も正確

実際の修正点

// Rork Max の出力(Bundle取得)
Text("バージョン \(Bundle.main.infoDictionary?["CFBundleShortVersionString"] as? String ?? "1.0.0")")
 
// 修正:型安全性向上
if let version = Bundle.main.infoDictionary?["CFBundleShortVersionString"] as? String {
    Text("バージョン \(version)")
}

生成コードの典型的な落とし穴と修正テンプレート

落とし穴1: State の重複や矛盾

症状

@State var taskName: String = ""
@State var taskTitle: String = ""  // taskName と重複している

修正テンプレート

// 単一の真実源(Single Source of Truth)にまとめる
@State private var editingTask: Task = Task(id: UUID(), title: "", dueDate: Date())
 
// 変更時は editingTask 全体を更新
func updateTaskName(_ name: String) {
    var updatedTask = editingTask
    updatedTask.title = name
    editingTask = updatedTask
}

落とし穴2: 非同期処理の Race Condition

症状

@State var fetchTask: Task<Void, Never>?
 
func refetch() async {
    fetchTask = Task {
        tasks = try await api.fetch()  // 前のリクエストをキャンセルしない
    }
}

修正テンプレート

@State var fetchTask: Task<Void, Never>?
 
func refetch() async {
    fetchTask?.cancel()  // 前のタスクをキャンセル
    fetchTask = Task {
        guard !Task.isCancelled else { return }
        tasks = try await api.fetch()
    }
}

落とし穴3: エラー時の State リセット忘れ

症状

func deleteTask() async {
    isLoading = true
    do {
        try await api.deleteTask(taskID)
    } catch {
        errorMessage = "削除に失敗しました"
        // isLoading = false が抜けている
    }
}

修正テンプレート

func deleteTask() async {
    isLoading = true
    defer { isLoading = false }  // 成功・失敗どちらでも必ず実行
    do {
        try await api.deleteTask(taskID)
    } catch {
        errorMessage = "削除に失敗しました"
    }
}

落とし穴4: Accessibility の VoiceOver 対応不足

症状

HStack {
    Image(systemName: "checkmark.circle")  // VoiceOver が読み上げられない
    Text(task.title)
}

修正テンプレート

HStack {
    Image(systemName: "checkmark.circle")
        .accessibilityHidden(true)  // 装飾なので隠す
    Text(task.title)
}
.accessibilityElement(children: .combine)  // テキストだけ読み上げ
.accessibilityLabel("完了済み:\(task.title)")

落とし穴5: List/LazyVStack でのキーの不適切な使用

症状

List(tasks) { task in  // id を明示していない
    TaskRowView(task: task)
}

修正テンプレート

List(tasks, id: \.id) { task in  // UUID や id フィールドで明示
    TaskRowView(task: task)
}
// または
LazyVStack(spacing: 8) {
    ForEach(tasks, id: \.id) { task in
        TaskRowView(task: task)
    }
}

App Store 申請で私が直面した落とし穴

1. プライバシーポリシー URL がハードコードのまま

App Store 審査で「プライバシーポリシーの URL が app.example.com になっていて、実在しない」というリジェクトをもらいました。

修正

// ❌ Rork Max が生成(ダミー)
Link("プライバシーポリシー", destination: URL(string: "https://app.example.com/privacy")!)
 
// ✅ 修正版:実際の URL に置換
Link("プライバシーポリシー", destination: URL(string: "https://mycompany.com/privacy")!)

2. Accessibility の最低基準に達していない

VoiceOver テストで「Image が読み上げられない」というリジェクトが返ってきました。

修正

// ❌ Rork Max が生成
Image("icon")
 
// ✅ 修正版:最低限の accessibilityLabel
Image("icon")
    .accessibilityLabel("アプリアイコン")
    .accessibilityHidden(task.isDecorative)  // 装飾なら隠す

3. Crash Log への対応不足

beta テスター報告で「特定条件で Crash」が判明。Rork Max の出力に Force Unwrap(!)が複数ありました。

修正

// ❌ Rork Max が生成(危険)
let url = URL(string: imagePath)!  // crash の可能性
 
// ✅ 修正版:ガード句
guard let url = URL(string: imagePath) else {
    errorMessage = "画像 URL が無効です"
    return
}

4. App Store 連絡先フィールドが空

審査チームが「サポート連絡先がない」とリジェクト。Rork Max はこのフィールド自体を生成していませんでした。

修正: 設定画面に以下を追加:

Section("サポート") {
    Link("お問い合わせ", destination: URL(string: "mailto:support@example.com")!)
}

継続開発フローへの組み込み方

App Store リリース後、機能追加時の Rork Max 活用パターン。

パターン1: 新しい Screen の追加

【既存】
- TaskListView:タスク一覧
- TaskDetailView:タスク詳細
- AddTaskModal:新規作成モーダル

【追加要件】
TaskFiltersView を追加:
- フィルタ種別:「カテゴリ」「優先度」「期日範囲」
- State:selectedCategory, selectedPriority, dateRange
- TaskListView に filter(by:) を呼び出し

このように既存構造を明示した上でプロンプトを出すと、Rork Max は一貫性のあるコードを生成します。

パターン2: API 仕様変更への対応

【既存】
Task 構造体:
  - id: UUID
  - title: String
  - dueDate: Date

【変更】
Task 構造体:
  - id: UUID
  - title: String
  - dueDate: Date
  - tags: [String](新規)
  - isArchived: Bool(新規)

【修正要件】
1. Task 構造体に tags, isArchived を追加
2. TaskRowView で tags を小さなバッジで表示
3. TaskListView で isArchived == false のみフィルタ
4. API 呼び出しで tags と isArchived を送受信

次に試すべきこと

1. Rork Max の学習フィードバック

Rork Max で生成したコードが App Store を通った場合、その成功パターンをプロンプトに組み込むと、次世代の生成品質が上がります。私が「非同期処理は Task で管理」と指示するようになったのも、過去の成功パターンを体系化したからです。

2. ローカルテストの自動化

Xcode の XCTest と組み合わせて、Rork Max が生成したコードの自動テストを組むことで、修正が必要な箇所をすぐに発見できます。

final class TaskListViewModelTests: XCTestCase {
    func testAddTaskValidation() async {
        let viewModel = TaskListViewModel()
        await viewModel.addTask(Task(title: "", dueDate: Date()))
        XCTAssertEqual(viewModel.errorMessage, "タスク名は1文字以上入力してください")
    }
}

3. Accessibility 自動監査

VoiceOver に頼らず、アクセシビリティの機械的な監査ツール(AccessibilityInspector)を組み込むことで、修正漏れを防止できます。

4. Version ごとのプロンプト進化

Rork Max のバージョンが上がるたびに、新しいプロンプト仕様が有効になります。定期的にプロンプトを見直し、最新の言語機能や API を活用することが大切です。

結論:Rork Max は「初期設計者」である

Rork Max を使い始めて感じるのは、このツールは「完全な自動化」ではなく、開発プロセスの「設計フェーズを高速化する」ものだということです。

2014年から個人アプリ開発をやってきた経験では、SwiftUI コードの品質は「初期設計がどれだけ精密か」で9割決まります。Rork Max は、その初期設計を 1/10 の時間で作ってくれる。残り 1割の修正(エラーハンドリング、Accessibility、App Store 対応)は、開発者の責務です。

この関係を理解した上で Rork Max と付き合えば、App Store デビューまでの時間は大幅に短縮できます。

シェア

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

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

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

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

関連記事

AI モデル2026-05-04
Rork Max の SwiftUI 生成は「どこまでできるのか」— 実機テストで見えた可能性と限界2026
Rork MaxのSwiftUI/ネイティブアプリ生成能力を実機テストで検証。何が得意で何が苦手かを正直にまとめました。機能の宣伝ではなく、実際に使った評価です。
AI モデル2026-05-02
Rork Max で SwiftUI ネイティブアプリを生成する実務ワークフロー — 仕様書から App Store 提出までの流れ
Rork Max を使って SwiftUI のネイティブ iOS アプリを生成する際、仕様書の準備から TestFlight、App Store 申請までを通しでどう運用するか。私が個人開発で確立したワークフローを実例ベースで共有します。
AI モデル2026-04-23
Rork Max で作るネイティブ SwiftUI アプリ — Xcodeなしで個人開発者が本当にできること
Rork Max で SwiftUI ネイティブ iOS アプリを作る流れを、個人開発者の実務視点で解説します。React Native 版との違い、App Store 公開までの2クリック公開フロー、Vision Framework や Core ML を組み込む手順、そして向かないケースまで整理しました。
📚RECOMMENDED BOOKS
大規模言語モデル入門
山田育矢
LLM開発
生成AIプロンプトエンジニアリング入門
我妻幸長
プロンプト
Claude CodeによるAI駆動開発入門
平川知秀
AI駆動開発
※ アフィリエイトリンクを含みます
もっと見る →