運用中の壁紙系アプリに「ユーザーが自分で挿絵を作れる機能」を足せないかと検討したことがあります。サーバー側で生成 API を叩く構成を見積もったところ、生成単価は1枚あたりおよそ2円から10円。ユーザー数が伸びるほど赤字が膨らむ構造で、モデレーションの運用負荷も個人開発の規模には重すぎて、一度は棚上げにしました。
その前提を変えたのが、端末内で完結する Image Playground です。生成コストはゼロ。出力は Apple 側のスタイルとポリシーで制約されるため、モデレーションの大部分をフレームワークが肩代わりしてくれます。
Rork Max はネイティブ Swift を生成できるので、この Image Playground にも手が届きます。ただし実装の中心は「生成そのもの」ではなく、可用性の判定とフォールバック にあります。ここを飛ばすと、対応端末では動くのに非対応端末でボタンが沈黙する、という一番印象の悪い壊れ方をします。
サーバー型生成と何が違うのか
まず設計判断の材料として、サーバー型の画像生成 API と Image Playground の性格の違いを整理しておきます。
観点 サーバー型生成 API Image Playground 生成コスト リクエスト課金(積み上がる) ゼロ(端末内で完結) ネットワーク 必須 不要(オフラインで動作) スタイル 自由(写実も可) アニメーション・イラスト・スケッチ系に限定 モデレーション 自前で設計が必要 フレームワーク側で制約済み 対応端末 全端末 Apple Intelligence 対応端末・対応地域のみ
重要なのは最後の行です。写実的な人物画像が作れないなどの制約は、UGC を扱う個人開発者にとってはむしろ安全側に働きます。一方で「動く端末が限られる」ことだけは、アプリ側の設計で吸収するしかありません。
可用性の判定を最初に設計する
Image Playground が使えるかどうかは、機種・OS バージョン・地域・Apple Intelligence の設定状態に依存します。SwiftUI では環境値で判定できます。
import SwiftUI
import ImagePlayground
struct IllustrationButton : View {
// Apple Intelligence が使える端末かどうかを環境値で受け取る
@Environment (\.supportsImagePlayground) private var supportsPlayground
@State private var showsSheet = false
var body: some View {
if supportsPlayground {
Button ( "挿絵を生成する" ) { showsSheet = true }
} else {
// 非対応端末にはボタン自体を出さず、既存のプリセット選択に誘導する
PresetIllustrationPicker ()
}
}
}
UIKit 側から使う場合は ImagePlaygroundViewController.isAvailable で同じ判定ができます。
ここでの設計判断はひとつです。非対応端末では「無効化したボタン」を出すのではなく、機能の入口ごと差し替える こと。押せないボタンは「壊れている」と受け取られますが、プリセット選択やテンプレートが並んでいれば、それはそれで完結した機能に見えます。私自身、機能の出し分けはこの「入口ごと差し替える」方式に統一してから、レビューでの不具合報告と誤解が目に見えて減りました。
シートを出す最小実装
UI 任せで生成する場合は imagePlaygroundSheet モディファイアを使います。ユーザーは Apple 標準の生成 UI の中でコンセプトを調整し、結果だけがアプリに返ってきます。
struct DiaryEntryView : View {
@Environment (\.supportsImagePlayground) private var supportsPlayground
@State private var showsPlayground = false
@State private var illustrationURL: URL ?
let entryText: String
var body: some View {
VStack {
if let url = illustrationURL,
let image = UIImage ( contentsOfFile : url.path) {
Image ( uiImage : image). resizable (). scaledToFit ()
}
if supportsPlayground {
Button ( "この日記から挿絵を作る" ) { showsPlayground = true }
}
}
. imagePlaygroundSheet (
isPresented : $showsPlayground,
concepts : [. extracted ( from : entryText, title : nil )]
) { url in
// 生成結果は一時ディレクトリの URL で返る。必要なら自前の保存先へコピーする
illustrationURL = url
}
}
}
押さえておきたいのは2点です。
concepts には自由文をそのまま渡すのではなく、.extracted(from:title:) で長文から要素を抽出させる形が安定します。日記やメモのような長いテキストを扱うアプリなら、この API が一番相性の良い入口です。
生成結果の URL は一時領域 を指します。シートを閉じた後も使い続けるなら、Documents 配下など自前の保存先へ即座にコピーしておきます。ここを怠ると、後からサムネイルが虫食いになる報告に化けます。
ImageCreator でプログラム主導の生成に切り替える
UI を出さずにアプリのロジック側で生成したい場合は、iOS 18.4 で追加された ImageCreator を使います。バッチ的に候補を並べる UI や、生成結果を選ばせるカルーセルを自作したいときはこちらです。
import ImagePlayground
func generateCandidates ( for text: String ) async throws -> [CGImage] {
// 端末が非対応の場合はここで throw されるため、呼び出し側で捕捉する
let creator = try await ImageCreator ()
let concepts: [ImagePlaygroundConcept] = [. extracted ( from : text, title : nil )]
var results: [CGImage] = []
// limit で生成数を制御する。多いほど時間がかかるため 2〜4 が実用域
let images = creator. images ( for : concepts, style : .illustration, limit : 3 )
for try await generated in images {
results. append (generated.cgImage)
}
return results
}
ImageCreator() の初期化は、非対応端末や Apple Intelligence 無効時に例外を投げます。つまり判定と生成が同じ場所で失敗してくれる ので、呼び出し側は do-catch を一段書くだけで済みます。SwiftUI の環境値判定と二重にしておくと、UI 表示前とロジック実行時の両方で守れます。
スタイルは .animation .illustration .sketch から選びます。アプリのトーンに合わせて1つに固定することを推奨します。ユーザーにスタイルまで選ばせると、生成結果のばらつきがそのままアプリの見た目のばらつきになります。
Rork Max への指示は「判定→生成→保存」の順で分ける
Rork Max にこの機能を作らせるとき、ひとつのプロンプトに全部を詰めると、可用性判定が抜けたコードが返ってくることがあります。私はこの順で3回に分けて指示するようにしています。
判定だけ : 「supportsImagePlayground 環境値で出し分けるコンポーネントを作って。非対応時はプリセット選択ビューを表示」
生成だけ : 「対応端末向けに imagePlaygroundSheet で日記テキストから挿絵を生成する機能を追加。concepts は extracted を使う」
保存だけ : 「生成結果の一時 URL を Documents 配下へコピーして永続化。ファイル名は UUID」
分ける理由は、各ステップの生成コードをその場で検証できるからです。特に 1 の判定は、非対応シミュレータと対応実機の両方で挙動を見てから次へ進むと、後戻りがなくなります。
ImageCreator を使う場合は if #available(iOS 18.4, *) の分岐が必要になる点も、プロンプトに明示しておくと確実です。指示に OS バージョンを書かないと、最低ターゲットによってはビルドが通らないコードが返ります。
運用で詰まりやすい点
実装後の運用フェーズでつまずきやすい点と、その対処を挙げます。
審査メタデータとの整合 。生成機能をスクリーンショットで訴求するなら、非対応端末でその画面に到達できない設計になっていることを審査ノートで補足しておくと、リジェクトの往復を減らせます。
生成数の期待値調整 。limit を増やせば候補は増えますが、端末温度と待ち時間も増えます。個人開発のアプリで計測した範囲では、3枚生成を上限にして「気に入らなければ再生成」の導線にする方が、待ち時間の体感が安定しました。
地域とアカウント設定 。端末スペックが足りていても、Apple Intelligence の言語・地域設定次第で supportsImagePlayground は false になります。サポート問い合わせで「対応機種なのに使えない」と来たら、まず設定状態を案内する定型を用意しておくと、エラー報告のやり取りが一往復で解決します。
サーバーコストゼロでここまで動くのは、画像生成を諦めていた個人開発者にとって現実的な再挑戦の機会だと感じています。まずは判定の出し分けだけでも組み込んで、対応端末での体験を確かめてみてください。