Rork Max で iOS アプリを素早く立ち上げられるようになりましたが、App Store 審査を意識したプロダクションレベルの品質に仕上げるには、Xcode 側での手作業が依然として必要です。ここで扱うのはRork Max が出力した SwiftUI プロジェクトを Xcode で開き、プロ品質に仕上げるまでのワークフローを、工程ごとに整理してお届けします。
個人アプリ開発者として Rork Max を半年ほど本格運用してきた経験から、「ここで時間を溶かした」というハマりどころも合わせて共有します。
Rork Max 生成プロジェクトの構造を理解する
まず Xcode で Rork Max のプロジェクトを開いたときに、ディレクトリ構成を正しく把握することが出発点です。
典型的な構成は次のような形になります。
MyApp/
├── MyApp.xcodeproj/
├── Sources/
│ ├── App/ ← アプリケーションのエントリポイント
│ ├── Views/ ← SwiftUI View 定義
│ ├── Models/ ← データモデル・Codable 定義
│ ├── Services/ ← API クライアント・永続化層
│ └── Utils/ ← 拡張関数・ヘルパー
├── Resources/
│ ├── Assets.xcassets/
│ ├── Localizations/
│ └── Fonts/
└── .rork/ ← Rork 生成メタデータ(重要)
この中で最も注意が必要なのは .rork/ ディレクトリです。これは Rork Max がプロジェクトの再生成や更新に使う内部メタデータで、触ると Rork 側からの再生成がおかしくなります。絶対に手動で編集しないでください。
Sources 配下のファイルは自由に編集できますが、次に Rork Max を使ってプロジェクトを更新するとき、構造を大きく変えたファイルは「衝突あり」として扱われます。後述するマージ戦略を必ず押さえてから改変してください。
Instruments で診断すべき 3 つのプロファイル
Rork Max が生成するコードは十分に動きますが、初期状態では「とりあえず動く」レベルに留まっていることが多いです。プロダクション品質に仕上げるには、Instruments を使った診断が必須です。特に見るべきプロファイルは次の 3 つです。
1. Time Profiler — CPU 時間の見直し
アプリ起動時やタブ切り替え時の体感速度が遅いと感じたら、最初に Time Profiler で計測します。Rork 生成コードで典型的に遅いのは、次のパターンです。
// 遅い: 毎回すべての要素を再計算
var body: some View {
List(items) { item in
Row(item: item, score: computeScore(for: item))
}
}computeScore が重い計算を含んでいると、スクロール時に毎フレーム再計算が走って体感速度が落ちます。対策は @State でキャッシュするか、コンピュテッドプロパティを事前計算しておくことです。
// 速い: 事前計算してキャッシュ
@State private var cachedScores: [UUID: Double] = [:]
var body: some View {
List(items) { item in
Row(item: item, score: cachedScores[item.id] ?? 0)
}
.task {
await computeAllScores()
}
}2. Allocations — メモリ使用量の把握
Rork Max が生成するコードは、画像の扱いで不要なメモリを消費するケースがあります。Allocations プロファイルで「Transient」ではなく「Persistent」なメモリの増加を追うと、解放漏れが見えてきます。
特に AsyncImage や UIImage(data:) を多用するリストは要注意です。画像のダウンサンプリングを入れるだけで、メモリ使用量が 1/5 になることもあります。
// 画像のダウンサンプリング関数
func downsample(image data: Data, to pointSize: CGSize, scale: CGFloat) -> UIImage? {
let imageSourceOptions = [kCGImageSourceShouldCache: false] as CFDictionary
guard let imageSource = CGImageSourceCreateWithData(data as CFData, imageSourceOptions) else {
return nil
}
let maxDimensionInPixels = max(pointSize.width, pointSize.height) * scale
let downsampleOptions = [
kCGImageSourceCreateThumbnailFromImageAlways: true,
kCGImageSourceShouldCacheImmediately: true,
kCGImageSourceCreateThumbnailWithTransform: true,
kCGImageSourceThumbnailMaxPixelSize: maxDimensionInPixels
] as CFDictionary
guard let downsampledImage = CGImageSourceCreateThumbnailAtIndex(imageSource, 0, downsampleOptions) else {
return nil
}
return UIImage(cgImage: downsampledImage)
}3. SwiftUI — ビュー更新の可視化
SwiftUI は宣言的なゆえに、どの View がどのタイミングで再描画されるかが直感と合わないことがあります。SwiftUI Instrument を使うと、予期しない再描画が可視化されます。
Rork 生成コードで典型的に起こる問題は、ObservableObject のプロパティを監視している View が、関係ないプロパティの変更でも再描画されることです。これは @ObservedObject を @Published 個別の値に分解して渡す、あるいは EquatableView を使うことで改善できます。