RORK LABEN
GROWTH — Rorkは月間74.3万訪問・成長率85%と、利用が伸び続けていますMAX — Rork MaxはネイティブSwiftアプリを生成し、iPhone・iPad・Watch・TV・Vision Pro・iMessageに対応しますMAX — AR/LiDARスキャン・Metalの3Dゲーム・Live Activities・HealthKit・Core MLなど、React Nativeでは届きにくい領域に踏み込めますSTACK — 通常のRorkはReact Native(Expo)でiOSとAndroidを同時に生成し、非エンジニアでも実機アプリを作れますPRICE — 料金は無料から用意され、有料プランは月$25から、Rork Maxは月$200ですMARKET — Gartnerは2026年末までに新規アプリの75%が低コード/ノーコード製になると予測していますGROWTH — Rorkは月間74.3万訪問・成長率85%と、利用が伸び続けていますMAX — Rork MaxはネイティブSwiftアプリを生成し、iPhone・iPad・Watch・TV・Vision Pro・iMessageに対応しますMAX — AR/LiDARスキャン・Metalの3Dゲーム・Live Activities・HealthKit・Core MLなど、React Nativeでは届きにくい領域に踏み込めますSTACK — 通常のRorkはReact Native(Expo)でiOSとAndroidを同時に生成し、非エンジニアでも実機アプリを作れますPRICE — 料金は無料から用意され、有料プランは月$25から、Rork Maxは月$200ですMARKET — Gartnerは2026年末までに新規アプリの75%が低コード/ノーコード製になると予測しています
記事一覧/開発ツール
開発ツール/2026-07-04上級

Rork Max のアプリで、選んだ写真が次回起動時に消えている — PHPicker の限定アクセスで URL を握り続けようとする罠

Rork Max が生成したネイティブ Swift アプリで、PHPickerViewController で選んだ写真が再起動後に読めなくなる——限定アクセスの時代に URL や PHAsset を握り続けようとするのが原因でした。選んだ瞬間に自前ストレージへ実体をコピーする設計をまとめます。

Rork Max213PHPickerViewControllerPhotoKit2限定アクセスSwift40iOS101

プレミアム記事

ユーザーが選んだ数枚の写真をコラージュに使うだけの機能を、Rork Max が生成したアプリへ足したときのことです。PHPickerViewController で選んだ写真はその場では問題なく表示され、加工も効きます。ところがアプリを再起動すると、直前に選んだはずの写真が真っ黒、あるいは読み込みに失敗する。選び直せば直りますが、保存したはずの状態が壊れている、という報告に近い挙動でした。エラーらしいエラーは出ません。

原因は、PHPicker が返す一時的な参照を「あとで読めばいい」と握り続けていたことです。限定アクセスが当たり前になった今の iOS では、選択時点で有効だった URL や PHAsset の識別子が、次回には読めなくなる前提で設計する必要があります。個人開発で写真アプリを長く触ってきましたが、この「選んだ瞬間だけ有効」という限定アクセスの性質を取りこぼすと、本番で静かにデータが欠けます。ここでは Rork Max のネイティブアプリに写真取り込みを最小差分で足す前提で、握り続ける罠の避け方と、そのまま使える取り込みレイヤーを順に見ていきます。

PHPicker が「権限を求めない」代わりに渡すものの正体

PHPickerViewController の大きな利点は、フォトライブラリの権限ダイアログを出さずに使えることです。ユーザーがシステム UI の中で選んだ写真だけがアプリに渡るので、アプリはライブラリ全体へのアクセス権を持ちません。ここが従来の UIImagePickerController との決定的な違いです。

渡ってくるのは実体ではなく「取り出し口」

PHPicker が返す NSItemProvider は、写真の実体そのものではなく「今なら取り出せる口」です。loadFileRepresentationloadDataRepresentation でその場で読み出すことはできますが、返ってくる一時ファイルの URL はコールバックを抜けた後には失効します。ここを「URL を保存しておいて後で開けばいい」と考えると、次回起動時に読めない状態に落ちます。

PHAsset の識別子も永続の保証ではない

PHPickerConfigurationpreferredAssetRepresentationMode を調整すれば PHAsset に到達する構成も取れますが、限定アクセス下では、そのときアプリに見えていた asset が次回も見えるとは限りません。ユーザーが後で選択範囲を変えれば、識別子は残っていても実体へ辿れなくなります。私は写真アプリの運用で、asset 識別子の永続化に頼る設計は避けるようになりました。

解決の芯は「選んだ瞬間に実体をコピーする」こと

握り続ける発想をやめて、選択のコールバックの中で実体を自分の永続領域(Application Support など)へコピーしきってしまう。以後はそのローカルファイルだけを参照する。これが限定アクセス時代の素直な設計です。取り込み時に一度だけコストを払い、あとは自前のファイルとして扱うことで、URL 失効も選択範囲変更も無関係になります。

import PhotosUI
import UniformTypeIdentifiers
import os
 
let picLog = Logger(subsystem: "net.rorklab.sample", category: "photo")
 
enum PhotoImportError: Error { case noImageRep, copyFailed }
 
final class PhotoImporter {
    private let dir: URL = {
        let base = FileManager.default.urls(for: .applicationSupportDirectory,
                                            in: .userDomainMask)[0]
        let d = base.appendingPathComponent("imported", isDirectory: true)
        try? FileManager.default.createDirectory(at: d, withIntermediateDirectories: true)
        return d
    }()
 
    // PHPicker の結果を受け取り、永続パスの配列を返す
    func importItems(_ results: [PHPickerResult]) async -> [URL] {
        var saved: [URL] = []
        for result in results {
            do {
                let url = try await copyToLocal(result.itemProvider)
                saved.append(url)
                picLog.info("imported: \(url.lastPathComponent)")
            } catch {
                picLog.error("import failed: \(error.localizedDescription)")
            }
        }
        return saved
    }
}

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

この記事の続きを読む

この先には、実装コードやベンチマーク結果など、実務でお役に立てる内容をご用意しています。このサイトは広告を掲載しておらず、サーバーや開発にかかる費用はメンバーの皆様のご支援で成り立っています。もしお役に立てていましたら、ご支援いただけますと大変ありがたいです。

この記事で得られること
PHPicker で選んだ写真が再起動後に消える症状を、限定アクセス・一時 URL の失効・PHAsset の非永続性のどこが原因か切り分けられるようになります
選択直後に実体を App の永続領域へコピーし、以後はローカルのファイルだけを参照する取り込みレイヤーを、そのまま Rork Max の生成コードに足せる形で手に入ります
限定アクセスでフォトライブラリ権限を求めない PHPicker の強みを保ちつつ、書き出し・向き補正・容量まわりを本番で破綻させない勘所がわかります
Stripe による安全な決済 · いつでもキャンセル可能

この記事を購入する

この先の内容をすべてお読みいただけます。一度のご購入で、いつでも何度でもアクセスできます。このサイトは広告を掲載しておらず、皆さまのご支援がサーバー費用などの運営を支えています。

または
メンバーシップなら全記事が読み放題 →
シェア

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

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

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

関連記事

開発ツール2026-07-04
Rork Max のアプリでアプリを起動せずに Live Activity を出す — push-to-start トークンが無言で取れない時の設計
Rork Max が生成したネイティブ Swift アプリで、アプリを一度も開かずにサーバから Live Activity を開始したい。ところが push-to-start トークンが観測できず無言で失敗する——その原因と、トークンを確実に取り切る観測レイヤーの実装をまとめます。
開発ツール2026-07-04
Rork Max の健康アプリで、朝になると歩数が更新されない — HKObserverQuery が無言で止まる background delivery の設計
Rork Max が生成したネイティブ Swift の健康アプリで、アプリを閉じている間の歩数や心拍が反映されない——その多くは HKObserverQuery の background delivery が無言で止まっていることが原因でした。切り分けと、そのまま組み込める観測レイヤーの実装をまとめます。
開発ツール2026-07-04
Rork Max 生成アプリで端末同士を直接つなぐ — MultipeerConnectivity が無言で失敗する Local Network 権限の罠
Rork Max が生成したネイティブ Swift アプリに、サーバを介さず近くの端末同士をつなぐ共有機能を組み込む方法です。シミュレータでは動くのに実機でピアが見つからない——その多くは Local Network 権限まわりの無言の失敗が原因でした。
📚RECOMMENDED BOOKS
大規模言語モデル入門
山田育矢
LLM開発
生成AIプロンプトエンジニアリング入門
我妻幸長
プロンプト
Claude CodeによるAI駆動開発入門
平川知秀
AI駆動開発
※ アフィリエイトリンクを含みます
もっと見る →