Rork Max の SwiftUI ネイティブ生成機能を使い始めて、最初に感じたのは「ここまで動くのか」という驚きでした。次に感じたのは「ここから先は手を入れないといけない」という現実でした。
私は2013年からスマートフォンアプリの個人開発を続けており、累計5,000万DLを超えるアプリ群のうち、壁紙・ヒーリング系のiOSアプリ4本を今も運営しています。Beautiful HD Wallpapers や Ukiyo-e Wallpapers といったアプリの開発を通じて、UIKit から SwiftUI への移行、AdMob メディエーション、StoreKit 2 対応など、実際の本番環境でしか分からない問題に繰り返し向き合ってきました。
その経験をベースに、Rork Max の SwiftUI 生成が「実際の本番開発」の文脈でどこまで使えるのかを検証しました。比較軸は単純です。私が実際に動かしているアプリに存在する機能を、Rork Max に同じ要件で作らせてみた、ということです。
検証の前提条件と環境
検証に使ったのは次の環境です。Rork Max は2026年5月時点の最新バージョン、Xcode 16.3(macOS Sequoia)、iPhone 16 Pro の実機と iOS シミュレータ(iOS 18.3)を組み合わせて使いました。対象の機能は Beautiful HD Wallpapers の主要UI群です。
Beautiful HD Wallpapers は2013年にリリースして以来、継続的にアップデートを続けているアプリで、2026年5月に v2.1.0 をリリースしたばかりです。このアップデートでは、v2.0.0 リリース後に顕在化した RecyclerView の IndexOutOfBoundsException(28日間で50ユーザー以上に影響)、Glide 5.0.5 + AGP 9.x 環境での Java 8 Supplier の NoClassDefFoundError(Android 6.0.1 ユーザー全員のクラッシュの原因)といった問題を修正しました。いずれも実運用でしか遭遇しないタイプのバグです。
iOSアプリ側は SwiftUI と UIKit の混在構成で動いています。Rork Max の SwiftUI 生成がどこまでこの「実際の複雑さ」に近づけるのかを確認することが目的でした。
「そのまま使えた」機能 — 生成コードの完成度が高かったケース
壁紙グリッドレイアウト(LazyVGrid)
壁紙一覧を表示する LazyVGrid は、生成クオリティが最も高かった機能の一つです。要件として渡したのは「3カラムのグリッド、各セルは正方形で角丸あり、タップでフルスクリーン遷移、無限スクロールで追加ロード」というシンプルな説明でした。
Rork Max が出力したコードの骨格はほぼ以下の形でした。
struct WallpaperGridView : View {
@StateObject private var viewModel = WallpaperViewModel ()
let columns = [ GridItem (. flexible ()), GridItem (. flexible ()), GridItem (. flexible ())]
var body: some View {
ScrollView {
LazyVGrid ( columns : columns, spacing : 4 ) {
ForEach (viewModel.wallpapers) { wallpaper in
WallpaperCell ( wallpaper : wallpaper)
. onAppear { viewModel. loadMoreIfNeeded ( current : wallpaper) }
}
}
. padding ( 4 )
}
. onAppear { viewModel. fetchWallpapers () }
}
}
struct WallpaperCell : View {
let wallpaper: Wallpaper
@State private var showDetail = false
var body: some View {
AsyncImage ( url : URL ( string : wallpaper.thumbnailURL)) { image in
image. resizable (). aspectRatio ( 1 , contentMode : .fill)
} placeholder : {
Color.gray. opacity ( 0.3 )
}
. clipShape ( RoundedRectangle ( cornerRadius : 8 ))
. onTapGesture { showDetail = true }
. fullScreenCover ( isPresented : $showDetail) {
WallpaperDetailView ( wallpaper : wallpaper)
}
}
}
この構造は私が実際に使っているコードに非常に近い形です。onAppear ベースのページネーションも正しいアプローチで実装されていました。実際の本番コードとの主な差異は ViewModel の細部(ページネーションのオフセット管理、エラーハンドリング)でしたが、これは要件として渡していない部分なので当然です。UIの骨格は正確に出てきました。
フルスクリーン壁紙プレビュー(ピンチズーム付き)
壁紙アプリの核となるフルスクリーンプレビューも、精度が高かった機能の一つです。ピンチズームと壁紙設定ボタン群まで含めて、実用できるレベルのコードが生成されました。
struct WallpaperDetailView : View {
let wallpaper: Wallpaper
@State private var scale: CGFloat = 1.0
@State private var lastScale: CGFloat = 1.0
@Environment (\.dismiss) private var dismiss
var body: some View {
ZStack ( alignment : .topTrailing) {
GeometryReader { geometry in
AsyncImage ( url : URL ( string : wallpaper.fullURL)) { image in
image
. resizable ()
. aspectRatio ( contentMode : .fit)
. frame ( maxWidth : . infinity , maxHeight : . infinity )
. scaleEffect (scale)
. gesture (
MagnificationGesture ()
. onChanged { value in scale = lastScale * value }
. onEnded { value in
lastScale = scale
if scale < 1.0 { scale = 1.0 ; lastScale = 1.0 }
if scale > 5.0 { scale = 5.0 ; lastScale = 5.0 }
}
)
} placeholder : {
ProgressView (). frame ( maxWidth : . infinity , maxHeight : . infinity )
}
}
. ignoresSafeArea ()
. background (Color.black)
Button ( action : { dismiss () }) {
Image ( systemName : "xmark.circle.fill" )
. font (.title)
. foregroundColor (.white)
. padding ()
}
}
}
}
スケール制限(1.0〜5.0)は仕様に含めていなかったにもかかわらず、自動的に生成されていました。「壁紙アプリのフルスクリーンビューア」という文脈から適切な設計を推論していることがわかります。
「手を入れた」機能 — 生成後に修正が必要だったケース
カテゴリ別タブと画面遷移
Beautiful HD Wallpapers では「Nature」「Architecture」「Abstract」などカテゴリ別に壁紙を分けて表示する構造になっています。これをRork Maxに生成させたところ、TabView ベースの実装が返ってきました。
TabView 自体は正しいアプローチですが、私のアプリでは TabView を使っていません。理由は、TabView の場合は全タブのビューが初期化時にメモリ上に展開されてしまうからです。壁紙アプリのように高解像度画像を大量に扱う場合、この差は無視できません。実際に TabView で組んでいた初期バージョンでは、カテゴリが6つ以上になった段階でメモリ警告が頻発するようになり、カスタムタブ実装に切り替えた経緯があります。
Rork Max が「正しい実装」をしたことに問題はありません。ただ、「このアプリが抱えるパフォーマンス上の制約」は明示的に伝えない限り考慮されません。以下のプロンプト補足を加えることで、レイジーロード型のカスタムタブが生成されるようになりました。
「カテゴリタブはTabViewではなく、ScrollViewとPageTabViewStyle相当のカスタム実装にしてください。
理由:壁紙アプリで全タブを同時にメモリ展開するとRAM使用量が急増するため、
表示中のカテゴリのみを初期化したい形にしてください」
スライドショー機能とページジャンプスライダー
iOS版の Beautiful HD Wallpapers では、スライドショー機能とページジャンプ用のスライダーを組み合わせた機能があります。これはAndroid版から iOS 版に逆移植した機能で、私自身が設計した実装です。
Rork Max にスライドショー機能の実装を頼んだところ、自動タイマーとページング付きの基本的な実装が生成されました。問題は Timer の扱いでした。生成されたコードは onAppear で Timer を開始しますが、onDisappear でキャンセルする処理がありません。ビューが消えた後も Timer が動き続け、クラッシュの原因になります。
さらに、アプリがバックグラウンドに移行した時の処理もありませんでした。壁紙アプリでは「スライドショーをかけたまましばらく放置する」という使い方を想定しているユーザーが多いため、バックグラウンド移行時の処理は重要です。
修正後のパターンはこうなります。
class SlideShowTimer : ObservableObject {
var timer: Timer ?
func start ( interval : TimeInterval, action : @escaping () -> Void ) {
stop ()
timer = Timer. scheduledTimer ( withTimeInterval : interval, repeats : true ) { _ in
action ()
}
}
func stop () {
timer ? . invalidate ()
timer = nil
}
}
struct SlideShowView : View {
@StateObject private var slideTimer = SlideShowTimer ()
@State private var currentIndex = 0
let wallpapers: [Wallpaper]
var body: some View {
TabView ( selection : $currentIndex) {
ForEach (wallpapers. indices , id : \. self ) { index in
WallpaperFullView ( wallpaper : wallpapers[index]). tag (index)
}
}
. tabViewStyle (. page ( indexDisplayMode : .never))
. onAppear {
slideTimer. start ( interval : 3.0 ) {
withAnimation {
currentIndex = (currentIndex + 1 ) % wallpapers. count
}
}
}
. onDisappear { slideTimer. stop () }
}
}
Timer を ObservableObject に切り出して onAppear/onDisappear で対称的に制御する、このパターンが安全です。
「生成を諦めた」機能 — Rork Max では対応が難しかったケース
UIKit ViewRepresentable ブリッジ
Beautiful HD Wallpapers の壁紙設定機能(ホーム画面・ロック画面への壁紙設定)は、UIKit の UIImageWriteToSavedPhotosAlbum と Photos フレームワークを組み合わせて実装しています。
この部分を SwiftUI で実装する際は UIViewControllerRepresentable を使う必要がありますが、Rork Max が生成した実装は動作しませんでした。生成されたコードが Photos フレームワークの権限フローを正しく処理しておらず、権限未付与の状態でクラッシュするものでした。
特に問題だったのは、PHPhotoLibrary.requestAuthorization の非同期処理と SwiftUI の ViewModel を正しく繋ぐ部分です。Rork Max はここを同期的に処理するコードを生成し、権限が付与されていない状態での動作が不定になっていました。正しいパターンは async/await を使った以下の形です。
func requestPhotoLibraryAccess () async -> Bool {
let status = PHPhotoLibrary. authorizationStatus ( for : .addOnly)
switch status {
case .authorized, .limited :
return true
case .notDetermined :
let granted = await PHPhotoLibrary. requestAuthorization ( for : .addOnly)
return granted == .authorized || granted == .limited
default:
return false
}
}
この機能については、既存の実装をそのまま流用することにしました。生成に頼るより手書きが安全だと判断した最初のケースです。
ATT プロンプトの順序制御
ATT(App Tracking Transparency)プロンプトのタイミング制御も、Rork Max での自動生成は難しい部分です。2026年現在、MobileAds 初期化前に ATTrackingManager.requestTrackingAuthorization を呼び出す必要があります。これを逆順にすると、一部のデバイスで ATT ダイアログが表示されない問題が起きます。
私自身が iOS版の4アプリ全てでこの問題を経験しており、順序を修正することで解決しました。Rork Max が生成するコードではこの順序が逆になるケースが多く、表面上のテストでは分からないまま本番に出てしまうリスクがあります。ATT 関連は必ず手動で確認することを強くお勧めします。
Rork Max で壁紙アプリを作る場合の実用的な指針
1997年、16歳でインターネットに出会い独学でプログラミングを始めた頃から変わらないと感じることがあります。それは「ツールの限界を知ることが、ツールを使いこなす前提条件だ」ということです。
今回の検証を通じて、Rork Max の SwiftUI 生成機能に対して持つ実用的な指針は4つです。
UIレイヤーは信頼して任せる : グリッドレイアウト、アニメーション、遷移、カスタムコンポーネントといった「見た目の実装」については、Rork Max の生成精度は高く、手戻りは少ないです。特に SwiftUI の宣言的な性質と相性がよく、複雑なアニメーションも適切な実装パターンで出てくることが多いと感じています。
ライフサイクルと非同期処理は必ずレビューする : onAppear/onDisappear の対称性、非同期処理のキャンセル、バックグラウンド移行時の処理など、「見えない動作」に関わるコードは必ずレビューしてください。動作するように見えて、エッジケースで問題が起きるパターンが多い部分です。Beautiful HD Wallpapers の v2.1.0 リリース直前にも、スライドショー機能のタイマーがバックグラウンドから復帰した後に二重起動するバグを発見しました。実機で長時間使って初めて分かった問題でした。
OS固有の権限フローは手書きが安全 : ATT プロンプト、Photos ライブラリアクセス、プッシュ通知の権限フローなど、Apple のプラットフォームポリシーと直結する部分は、自分で書くか公式サンプルを参照した上で生成コードを検証することをお勧めします。ここでの問題はテスト段階では見えにくく、審査リジェクトや本番クラッシュとして顕在化することが多いです。
仕様書に「なぜ」を加えると精度が上がる : 「何を作るか」だけでなく「なぜそう設計するか」をプロンプトに加えることで、生成コードの品質が明確に上がります。特にパフォーマンス上の制約(メモリ、バッテリー、バックグラウンド動作)については意識的に補足することを習慣にしています。
AdMob メディエーションとの共存で気をつけること
壁紙アプリの収益柱の一つは AdMob 広告です。Rork Max で生成したビューに AdMob バナーを組み込む際の注意点をお伝えします。
iOS 版の4アプリでは、2026年5月のアップデートで Liftoff・InMobi・Unity Ads の3つのメディエーションパートナーを追加しました。AdMob × AppLovin MAX のウォーターフォール設定で20グループへの追加を完了しており、最適化トグルを有効にすることで eCPM ベースの動的調整が機能しています。
Rork Max で生成したビューに広告を組み込む際の主なポイントは rootViewController の取得方法です。
struct BannerAdView : UIViewRepresentable {
let adUnitID: String
func makeUIView ( context : Context) -> GADBannerView {
let banner = GADBannerView ( adSize : GADAdSizeBanner)
banner.adUnitID = adUnitID
// iOS 15+ での正しい取得方法(keyWindow は deprecated)
banner.rootViewController = UIApplication.shared.connectedScenes
. compactMap { $0 as? UIWindowScene }
. first ? .windows
. first ? .rootViewController
banner. load ( GADRequest ())
return banner
}
func updateUIView ( _ uiView: GADBannerView, context : Context) {}
}
Rork Max の生成コードでは UIApplication.shared.keyWindow を使うケースがありますが、これは iOS 15 以降で deprecated です。現在は警告で済んでいても、将来のSDK更新で動作が変わる可能性があります。connectedScenes 経由での取得を使ってください。
複数解像度への対応とiPhone新モデル対応
壁紙アプリで特に重要な問題の一つが、iPhone モデルごとの解像度対応です。2026年現在、iPhone Air(420×912)、iPhone 17 Pro(402×874)、iPhone 16/17 Pro Max(440×956)など、対応すべき解像度が年々増加しています。
Beautiful HD Wallpapers の iOS版では DefineManager.h に29箇所の三項演算子を追加して各解像度に対応しています。Rork Max に解像度対応の実装を依頼すると、基本的な解像度分岐のコードは生成されますが、最新モデルの解像度定数は含まれないことが多いです。Apple の Device Screen Specifications を参照した上で、定数を手動で追加する作業は避けられません。
// 解像度別の壁紙サイズ定義(2026年5月時点)
struct WallpaperSize {
static var optimal: CGSize {
let screen = UIScreen.main.bounds. size
let scale = UIScreen.main.scale
switch ( Int (screen.width * scale), Int (screen.height * scale)) {
case ( 1290 , 2796 ) : return CGSize ( width : 1290 , height : 2796 ) // iPhone 14/15/16 Pro Max
case ( 1320 , 2868 ) : return CGSize ( width : 1320 , height : 2868 ) // iPhone 16 Pro Max
case ( 1206 , 2622 ) : return CGSize ( width : 1206 , height : 2622 ) // iPhone 15/16 Pro
default: return CGSize ( width : screen.width * scale, height : screen.height * scale)
}
}
}
この部分は最新モデルが出るたびに更新が必要であり、Rork Max の生成に頼るより公式のリリースノートを参照しながら自分で管理する方が確実です。
SwiftUI のアニメーション生成:Rork Max が特に強い領域
グリッドやビューアとは別に、アニメーション関連の生成精度が高いことも発見でした。壁紙アプリでよく使うフェードイン・スケールアップ・スライドインといったアニメーションは、仕様に軽く触れるだけで適切なコードが生成されます。
たとえば「壁紙をタップした時にハートアイコンをアニメーションで表示する」という要件に対して、以下のような実装が出てきました。
struct LikeButton : View {
@State private var isLiked = false
@State private var showHeart = false
var body: some View {
Button ( action : {
isLiked. toggle ()
if isLiked {
showHeart = true
withAnimation (. spring ( response : 0.4 , dampingFraction : 0.5 )) {
// animation triggers
}
DispatchQueue.main. asyncAfter ( deadline : . now () + 1.2 ) {
showHeart = false
}
}
}) {
Image ( systemName : isLiked ? "heart.fill" : "heart" )
. foregroundColor (isLiked ? .red : .white)
. font (.title2)
. scaleEffect (showHeart ? 1.4 : 1.0 )
. animation (. spring ( response : 0.3 , dampingFraction : 0.6 ), value : showHeart)
}
}
}
spring アニメーションのパラメータ選択(response 0.3〜0.4、dampingFraction 0.5〜0.6)は実際の壁紙アプリで使っているものに近く、生成されたそのまま使えるクオリティでした。アニメーション設計は Rork Max が得意とする領域の一つだと感じています。
宮大工だった両祖父が「手を動かすことが一つの信心」と言っていたと、子どもの頃に聞かされた記憶があります。ものを作る際の丁寧さへの信念は、私自身の開発でも大切にしていることです。アニメーションの細部にこだわることは、その延長線上にあります。Rork Max がその部分で一定の精度を出してくれることは、こうした姿勢と相性がいいと感じています。
Rork Max 生成コードと既存 UIKit コードの混在戦略
Beautiful HD Wallpapers は SwiftUI と UIKit の混在構成です。既存の UIKit コードを残しながら、新規画面を Rork Max で SwiftUI として生成していく場合の戦略についても触れておきます。
基本的なアプローチは UIHostingController を使って SwiftUI ビューを UIKit のフローに組み込む方法です。これは Rork Max でも生成できますが、ナビゲーションスタックの一貫性(SwiftUI の NavigationStack と UIKit の UINavigationController が混在する問題)については手動での調整が必要になることがあります。
私の場合は新規追加した画面から SwiftUI 化を進めており、既存の UIKit 画面には手を入れないというルールで作業しています。この「段階的移行」の考え方は、Rork Max を使う場合も同様です。全画面を一度に生成し直そうとするのではなく、新規機能を Rork Max で生成しながら既存コードと組み合わせていくアプローチが現実的です。
特に注意が必要なのは、UIKit と SwiftUI の境界でのデータの受け渡しです。Rork Max が生成するコードは SwiftUI の @State や @Binding を前提とした設計になっていることが多く、UIKit 側との連携には UIViewControllerRepresentable や Coordinator パターンを挟む必要があります。この橋渡し部分は Rork Max の生成精度がやや下がる領域なので、手書きで書くか既存の実績あるパターンを流用することをお勧めします。
StoreKit 2 への移行と Rork Max の活用
2026年の iOS アプリ開発において避けられないのが StoreKit 2 への移行です。旧 SKPayment API は iOS 18 以降で完全非推奨となり、Transaction.currentEntitlements を使った async sequence ベースのアプローチに移行する必要があります。
Rork Max に StoreKit 2 の実装を依頼すると、基本的な購入フローは正しく生成されます。ただし、壁紙アプリのような「広告削除型の IAP」と「サブスクリプション型」を組み合わせたケースでは、エンタイトルメントの判定ロジックが複雑になります。私の実装では BillingManager と AdFreeManager を分離して、isAdFree || isRewardAdFree という合成判定を必ず経由する設計にしています。
actor BillingManager {
static let shared = BillingManager ()
func isAdFree () async -> Bool {
for await result in Transaction.currentEntitlements {
if case . verified ( let transaction) = result {
if transaction.productID == "net.dolice.adfree" && transaction.revocationDate == nil {
return true
}
}
}
return false
}
}
この actor ベースの実装は Rork Max が生成できる形に近いですが、revocationDate のチェックを忘れるケースがありました。払い戻しされた購入に対してもコンテンツを表示してしまう問題につながるため、必ず確認してください。
Rork Max を使う前後で変わったこと — 開発サイクルの具体的な変化
Rork Max を取り入れる前と後で、私の開発サイクルが具体的にどう変わったかを書いておきます。
以前は「新しい画面の実装」から始めると、画面の基本レイアウト作成に半日、動作確認と調整にさらに半日を費やすことが多かったです。1画面あたり1日が目安でした。
Rork Max を使い始めてからは、基本レイアウトの生成が30〜60分に短縮されました。ただし、前述のライフサイクル周りのレビューと、アプリ固有の要件に合わせた調整で1〜2時間を追加で使います。結果として1画面あたり2〜3時間が目安になっています。単純計算で1/3〜1/4の時間になった計算です。
この短縮効果が最も大きく出るのは、UIが複雑で仕様が明確な画面です。逆に、「こういう感じにしたい」という曖昧な要件の場合は、生成とレビューを繰り返す分だけ時間がかかることもあります。
時間の使い方が変わって気づいたことは、以前はUI実装に費やしていた時間が、ビジネスロジックやパフォーマンス改善に使えるようになったことです。壁紙の読み込み速度の最適化や、メモリ使用量の削減といった「ユーザーには見えないが体験を左右する部分」に時間を割けるようになったと感じています。
国際芸術賞を17冠受賞した作品づくりも、アプリ開発も、「どこに時間と注意を注ぐか」の判断が品質を決めると感じています。Rork Max は、その判断をより良い方向に向ける助けをしてくれるツールです。
もう一つ変化があります。コードレビューの質が変わりました。以前は「このコードは動くか」を確認するレビューが中心でしたが、今は「このコードのどの部分を信頼して、どの部分を疑うべきか」という視点でレビューするようになりました。生成されたコードの責任は最終的に自分にあります。その意識がある限り、AI生成コードは品質を下げる要因にはなりません。むしろ、自分が見落としがちなUI実装の細部をカバーしてくれる、経験豊富なコードレビュアーのような存在として活用できています。
全体評価:12年の個人開発経験から見た Rork Max の SwiftUI 機能
今回の検証を通じて感じたことを率直に言うと、「UIの実装コストを大幅に下げるが、プラットフォームの深い部分は開発者の知識が必要」です。
2014年に月刊 MacPeople の7月号でiPhoneデベロッパーとして取材を受けた当時と比較すると、アプリ開発の入り口は劇的に低くなりました。Rork Max のような AI 生成ツールが登場したことで、以前なら2週間かかっていたUIの実装が数時間で形になります。これは本当の意味での生産性向上です。
しかし同時に、「動くように見えるコード」と「本番で安定して動くコード」の差は変わっていないとも感じています。タイマーのライフサイクル管理、権限フローの非同期処理、メモリ効率を考慮したアーキテクチャ選択——これらは「実際にアプリを長期運用して初めて分かる」種類の知識であり、生成 AI が補うのが難しい部分です。
Rork Max の SwiftUI 生成は、UIの骨格を素早く形にして、そこから先を自分の経験で詰めていく使い方に最も向いていると感じています。私自身、次回のiOSアプリアップデートでも、新画面の UI は Rork Max で生成してから調整するというフローを使う予定です。
まず手を動かしてみることが最善の学び方だと、1997年にインターネットに出会って以来変わらず感じています。Rork Max の SwiftUI 生成機能を試したことがない方は、既存アプリの小さな画面一つから試してみてください。得られる学びと、自分の経験で埋めるべき部分の両方が、すぐに見えてきます。
最後までお読みいただきありがとうございました。実装の参考になれば幸いです。