6月の半ば、Rork Max が Claude Code と Opus 4.6 を動力に、React Native ではなくネイティブ Swift を生成するようになったという話を読みました。同じ週に Rork が $15M を調達したという報も流れていて、AI でアプリを組み立てる流れが、いよいよ「クロスプラットフォームの最大公約数」から「ネイティブの深い部分」へ踏み込んできたのだ、と静かに感じています。
正直に書くと、最初の反応は期待半分、警戒半分でした。期待は分かりやすいので置いておきます。警戒のほうは、長く iOS アプリを保守してきた感覚から来るものです。ネイティブ Swift は、生成された瞬間が完成ではありません。むしろそこから、審査・更新・課金・計測といった「公開後の長い時間」が始まります。だからこそ私は、AI に気持ちよく任せられる層と、自分の手元に意識的に残しておきたい層を、いちど自分の言葉で線引きしておこうと思いました。
なぜ「ネイティブ Swift 生成」は React Native 生成と質的に違うのか
React Native(Expo) の生成は、ある意味で扱いやすいものでした。出てくるのは JavaScript / TypeScript で、Expo というレールの上を走っている限り、挙動の予測がつきやすい。ところがネイティブ Swift になると、触れられる範囲が一気に広がります。Live Activities、Dynamic Island、HealthKit、Core ML、NFC、App Clips。Rork Max が解放すると言っているのは、まさにこの「React Native では届きにくかった領域」です。
届く範囲が広いということは、壊れたときに調べる範囲も広い、ということでもあります。私が個人開発の壁紙アプリや癒し系アプリで実装してきた中でも、ウィジェットや通知まわりは「動いて当たり前」に見えて、OS バージョンや端末密度の差で静かに崩れる場所でした。AI が一気に書いてくれるのはありがたい一方で、崩れたときに自分が説明できない状態は避けたい。この緊張感が、層を分ける動機になっています。
第1の層:UI の骨格は、気持ちよく任せる
まず、任せていいと考えている層から書きます。画面のスキャフォールド、つまり SwiftUI のビュー階層やレイアウトの下書きは、AI に出してもらうのが一番速いと感じています。ここは「正解が一意に決まらない」かわりに「間違っても被害が小さい」領域です。
struct WallpaperGridView: View {
let items: [Wallpaper]
private let columns = [GridItem(.adaptive(minimum: 110), spacing: 8)]
var body: some View {
ScrollView {
LazyVGrid(columns: columns, spacing: 8) {
ForEach(items) { item in
WallpaperCell(wallpaper: item)
.aspectRatio(9/16, contentMode: .fit)
}
}
.padding(8)
}
}
}こうしたコードは、生成されたものをそのまま受け取っても、後から自分で読み替えるのが容易です。私はレビューのとき、LazyVGrid を使っているか(一覧で VStack を素朴に積むとスクロールが重くなります)、adaptive で端末幅に追従しているか、の2点だけを見ます。細部の余白やアニメーションは、動かしながら手で詰めれば十分です。骨格は任せ、肌触りは自分で、という分担です。
第2の層:権限・課金・計測の「境界」は、手元に残す
ここが、今回いちばん書きたかったところです。私は、アプリと外の世界が接する境界——課金状態、AdMob の広告の出し分け、計測、そして OS の許可を扱う層——は、AI に丸ごと任せず、自分の判断を必ず一枚かませると決めています。理由は単純で、ここがずれると「静かに、しかし致命的に」壊れるからです。全文無料で読めない不具合は気づけますが、課金済みユーザーに広告が出てしまう不具合は、しばらく気づけません。
たとえば「広告を消してよいか」の判定は、複数の入力(買い切り課金、リワード視聴による一時的な無広告、サブスク)を必ず1か所に集約します。生成コードが各画面で個別に if 文を書いていたら、私はそれを必ずこの形に寄せ直します。
struct AdFreeState {
let hasLifetimePurchase: Bool
let hasActiveSubscription: Bool
let rewardGrantedUntil: Date?
// 広告を出すかどうかは、必ずこの1か所だけが決める
func isAdFree(now: Date = .now) -> Bool {
if hasLifetimePurchase || hasActiveSubscription { return true }
if let until = rewardGrantedUntil, until > now { return true }
return false
}
}判定をひとつの関数に閉じ込めておくと、新しい収益施策が増えても、触る場所はここだけで済みます。AI に画面を量産してもらうほど、この「単一の真実」を持っておく価値は上がっていく、というのが実感です。課金まわりの設計は、StoreKit 2 のサブスクリプション基盤の考え方を別記事でも整理しているので、深掘りしたい方はそちらもどうぞ。
同じことが Core ML のオンデバイス推論にも言えます。Rork Max は Core ML 対応をうたっていますが、「推論をどこで走らせ、結果をどう扱うか」は設計判断そのものです。
func classify(_ image: CGImage) throws -> String? {
let model = try WallpaperTagger(configuration: .init())
let input = try WallpaperTaggerInput(imageWith: image)
let output = try model.prediction(input: input)
// 信頼度がしきい値未満なら「分からない」を返す——勝手に断定させない
guard let top = output.classLabelProbs.max(by: { $0.value < $1.value }),
top.value >= 0.6 else { return nil }
return top.key
}この 0.6 のしきい値のような小さな判断は、AI が書いた初期コードにはまず入っていません。けれど、こうした「自信がないときに黙る」設計こそが、公開後のクレームを減らす肝になります。ネイティブ Swift をどこまで Xcode なしで扱えるかはMac なしでネイティブ Swift を公開する話に詳しく書きましたが、ツールが進んでも、こうした境界の判断だけは人が持ち続けるべきだと考えています。
第3の層:生成コードを「保守できる状態」に整える
3つ目は、AI が書いたコードを、半年後の自分が読める状態にしておく作業です。これは層というより習慣に近いのですが、ネイティブ Swift の生成が当たり前になるほど、ここが効いてくると感じています。
私が最低限やっているのは、命名と責務の境界を生成直後にそろえることです。AI は文脈ごとに微妙に違う名前を付けがちなので、Manager と Store と Service が混在していたら、自分のプロジェクトの語彙に寄せます。それから、第2の層で挙げた境界(課金・計測・権限)には、必ず小さなテストを1本だけでも置いておきます。網羅は狙いません。「壊れたら即わかる」最小の番兵を置くイメージです。生成と保守をどう両立させるかはExpo からネイティブへの出口を考えた記録とも地続きの話です。
ツールの寿命に賭けるか、という別の問い
最後に、$15M 調達の話に触れておきます。資金が集まったというニュースは、それ自体が品質を保証するものではありません。ただ、個人開発でツールを選ぶとき、私が気にしているのは「このツールが明日消えても、自分の資産が手元に残るか」という一点です。生成されたネイティブ Swift のソースが自分のリポジトリにあり、Xcode で開けて、課金や計測の境界を自分が握っている——そうであれば、ツールの寿命にすべてを賭けずに済みます。調達のニュースは歓迎しつつ、依存しすぎない構えだけは保っておきたい、というのが今の率直な気持ちです。
賭けるかどうかの判断軸そのものはRork Max の料金をどう捉えるかでも別の角度から書いています。
もし今あなたが Rork Max を試そうとしているなら、まずは小さな1画面だけを生成して、課金状態の判定を上の AdFreeState のような形に自分で寄せ直してみてください。AI に任せる層と手元に残す層の感触が、その一回で驚くほどはっきりします。共に手を動かしながら、ちょうどいい線引きを探していけたら嬉しいです。