iOS 27のSwiftUIで何が新しくなったか
SwiftUIのリリースは、Appleが何を作り直すと決めたかによって、フレームワークの圧力点がどこにあったのかを教えてくれます。iOS 27の答えは異例なほど広範です。リストには第一級の並べ替えが備わり、ドキュメントには新しいreader/writerのプロトコル群が登場し、ツールバーには明示的な優先度を持つオーバーフローモデルが加わり、エラー表示にはついにErrorを直接渡せるバインディングが用意されました。このリリースは4つの領域を一度に動かしており、ほとんどのアプリは少なくともそのうち2つに触れることになります。1
これほど広範なリリースで陥りがちなのは、すべてを採用しようとすることです。より良い判断は、どの追加があなたの作り方そのものを変えるのか、そしてどれが既に使っているパターンに対する便利なオーバーロードにすぎないのかを見極めることです。ドラッグによる並べ替えとドキュメントプロトコルは前者です。これらは手書きしていたコードを置き換えます。アイテムベースのアラートとAsyncImage(request:)は後者で、回避策を取り除くものです。本稿では、iOS 27のSwiftUIの領域をこれらの筋立てに整理し、実際の宣言と、それぞれがどんなときにコードに居場所を得るのかの理由を示します。
セッション269でAppleは、このリリースを単一の目玉機能ではなく、洗練された見た目と操作感、強力な新しいドキュメントAPI、新しいインタラクションの方法、そしてパフォーマンスの作業という、ともに動く4つの広い筋立てとして位置づけています。35
TL;DR / 要点
- リストとカスタムコンテナに宣言的な並べ替えが備わります。
reorderContainer(for:isEnabled:move:)がコンテナをマークし、DynamicViewContentに付けるreorderable()が行を参加させ、手書きのインデックス計算の代わりにReorderDifferenceを受け取ります。23 - 遅延ドラッグコンテナは
dragContainer(for:itemID:in:_:)とdraggable(containerItemID:containerNamespace:)によって登場します。これは識別子だけを運ぶため、ドラッグ開始時にフレームワークがペイロードを遅延的に取得します。45 - 新しいドキュメントモデルが
ReadableDocumentとWritableDocumentとして登場します(ディスク上の処理はDocumentReader/DocumentWriterが担い、単純なケースにはFileWrapperDocumentReader/FileWrapperDocumentWriterが用意され、URLDocumentConfigurationが裏で支えます)。6789101112 - ツールバーには
ToolbarOverflowMenu、topBarPinnedTrailingの配置、そしてvisibilityPriority(_:)が加わり、バーの場所が足りなくなったときにどのコントロールを残すかをあなたが決められます。131415 - エラー表示には
alert(error:actions:message:)とアイテムベースのalert(_:item:actions:)/confirmationDialogが、さらに完全なURLRequest制御のためのAsyncImage(request:)とセッションを共有するasyncImageURLSession(_:)が加わります。1617181920 - 領域を締めくくるのは、
swipeActions(...onPresentationChanged:)、swipeActionsContainer()、NavigationTransition.crossFade、TabRole.prominent、UIHostingSceneDelegate、そしてGestureInputKindsです。212223242526
ドラッグ並べ替えが宣言的になる
SwiftUIでリストを並べ替えるには、これまでonMoveと、インデックスセットと、自分のモデル変更へ翻訳する移動先オフセットが必要でした。iOS 27はそれを2つの部分からなる宣言で置き換えます。コンテナを並べ替え可能としてマークし、その内容を参加させるとマークするのです。するとコンテナは構造化された差分をあなたに渡します。その差分が変更するモデル自体も、iOS 27ではより良く監視されます。SwiftDataは同じリリースで第一級の監視と永続履歴のAPIを獲得したので、あるビューで並べ替えたストアは、読み込まれるどこでも同期を保ちます。
単一コレクションのケースが一般的なものです。コンテナにreorderContainer(for:isEnabled:move:)を宣言し、その内部のDynamicViewContentにreorderable()を付けます。23
struct LandmarkList: View {
@State private var landmarks: [Landmark]
var body: some View {
List {
ForEach(landmarks) { landmark in
LandmarkRow(landmark: landmark)
}
.reorderable()
}
.reorderContainer(for: Landmark.self) { difference in
// Apply the reorder to your model.
landmarks.apply(difference)
}
}
}
シグネチャが契約を物語っています。コンテナ修飾子はItem : Identifiableに対してジェネリックで、ReorderDifference<Item.ID, ReorderableSingleCollectionIdentifier>を渡します。2
nonisolated func reorderContainer<Item>(
for item: Item.Type,
isEnabled: Bool = true,
move: @escaping (ReorderDifference<Item.ID, ReorderableSingleCollectionIdentifier>) -> ()
) -> some View where Item : Identifiable, Item.ID : Sendable
ここで2つの設計判断が重要になります。第一に、フレームワークがインタラクションを駆動します。Appleのドキュメントが説明するとおり、並べ替え可能なアイテムはドラッグジェスチャで持ち上げられ、プレースホルダービューがその位置を占めてアイテムが着地する場所を示し、プレースホルダーはコンテナ全体を通じてドラッグを追跡します。2もはやそのアフォーダンスを作る必要はありません。コレクションを記述し、結果に反応するだけです。第二に、isEnabledは別個の修飾子ではなくパラメータなので、編集モードの切り替えは条件付きのビュー構築ではなく1つのブール値で済みます。
コンテナが2つ以上のコレクションを保持する場合は、コレクション識別子の型を取るオーバーロードreorderContainer(for:in:isEnabled:move:)を使います。27
nonisolated func reorderContainer<Item, CollectionID>(
for item: Item.Type,
in collectionID: CollectionID.Type,
isEnabled: Bool = true,
move: @escaping (ReorderDifference<Item.ID, CollectionID>) -> ()
) -> some View where Item : Identifiable, CollectionID : Hashable, CollectionID : Sendable, Item.ID : Sendable
ReorderDifferenceはアイテムIDとコレクションIDの両方でキー付けされるようになったので、あるセクションから別のセクションへまたがる移動も表現できます。Appleの指針は明快です。コンテナが複数のコレクションを持つときは複数コレクションのオーバーロードを使い、1つだけのときは単一コレクションの便利なものを使います。27reorderable()修飾子は、曖昧さを解消する必要があるときに対応するコレクション識別子を受け取ります。3
ForEachにreorderable()を、その親にreorderContainerを加えて並べ替えを有効にし、クロージャ内で差分を処理します。
セッション271でAppleは、同じ並べ替えコードがListからLazyVGridへ変更なしで持ち越せる様子を示しています。修飾子がコンテナではなくコレクションを記述しているため、並べ替えはドラッグ&ドロップをサポートするどのコンテナでも機能するのです。36
遅延ドラッグコンテナ
iOS 27のドラッグの物語のもう半分はコストに関するものです。従来のdraggableは、ペイロードを前もって生成することを求めます。つまりドラッグが始まる前に、フレームワークがアイテムを実体化し(ときにはレンダリングまで)しなければならない場合があるのです。何千もの行を持つ遅延リストにとって、これは無駄な作業です。
dragContainer(for:itemID:in:_:)はドラッグ可能なビューのコンテナを定義し、ペイロードはドラッグされた識別子に対するクロージャとして一度だけ求めます。4
nonisolated func dragContainer<ItemID, Item, Data>(
for itemType: Item.Type = Item.self,
itemID: KeyPath<Item, ItemID>,
in namespace: Namespace.ID? = nil,
_ payload: @escaping (Array<ItemID>) -> Data
) -> some View where ItemID : Hashable, ItemID : Sendable, Item : Transferable, Item == Data.Element, Data : Collection
そのコンテナの内部では、ドラッグ可能な各子ビューがdraggable(containerItemID:containerNamespace:)を使い、これはアイテムの識別子だけを運びます。5
nonisolated func draggable<ItemID>(
containerItemID: ItemID,
containerNamespace: Namespace.ID? = nil
) -> some View where ItemID : Hashable, ItemID : Sendable
これが大きなコレクションにとってより良いデフォルトである理由は、Apple自身の説明にあります。この修飾子はペイロードではなく識別子だけを供給するため遅延的に機能し、フレームワークは実際にドラッグされたアイテムをドラッグ開始時にのみ要求し、ペイロードにアクセスするためにビューをレンダリングする必要がありません。5名前で自身を識別する(そしてIdentifiableには決して準拠しない)Fruit値も、複数アイテムのドラッグの発生源になれます。コンテナはIdentifiable準拠ではなく、あなたが提供するKeyPathをキーにするからです。4
クロスプラットフォームのコードで注意すべき点として、dragContainerとdraggable(containerItemID:containerNamespace:)はmacOS 26.0で利用可能です。このリリースの残りのほとんどがmacOS 27.0であるのに対し、遅延ドラッグAPIはすでにMacで採用できていたものなのです。45
新しいドキュメントモデル
DocumentGroupとFileDocumentは何年もの間SwiftUIのドキュメントアプリを支えてきましたが、読み込み側と書き込み側は単一の準拠の中で絡み合っていました。iOS 27はそれらを分割します。読み込みと書き込みは別々のプロトコルになり、ディスク上のロジックは独自の層となり、読み書き可能な型は両方を組み合わせます。
2つのトップレベルのプロトコルはReadableDocumentとWritableDocumentです。67
protocol ReadableDocument : AnyObject
protocol WritableDocument : AnyObject
読み込み専用の型はReadableDocumentだけに準拠します。読み書き可能な型は両方に準拠し、Appleはこの2つをまとめたDocument型エイリアスを提供するので、それぞれを名指しせずにペアを採用できます。67どちらもクラス束縛(: AnyObject)であり、これはこのモデルでドキュメントが参照型であることを示す目に見える合図です。
実際のディスクI/Oは、reader/writerの抽象であるDocumentReaderとDocumentWriterへ移ります。それぞれ、ドキュメントがシリアライズするスナップショット型でパラメータ化されています。89
protocol DocumentReader<Snapshot>
protocol DocumentWriter<Snapshot>
ほとんどのアプリはこれらを手で実装することはありません。カスタムロジックを必要としない小〜中規模のドキュメントには、SwiftUIがFileWrapperDocumentReaderとFileWrapperDocumentWriterを提供します。それぞれファイルラッパーに支えられ、Appleは単純なケースに効率的な選択肢だと説明しています。1011
struct FileWrapperDocumentReader<Snapshot>
struct FileWrapperDocumentWriter<Snapshot>
開いているドキュメントを結びつけるのがURLDocumentConfigurationで、開いているドキュメントの設定とプロパティを保持するメインアクターのクラスです。12
@MainActor final class URLDocumentConfiguration
エクスポートは、writerがURLを対象とするWritableDocumentを取る、更新されたfileExporterを経由します。28
nonisolated func fileExporter<D>(
isPresented: Binding<Bool>,
document: D?,
contentType: UTType? = nil,
defaultFilename: String? = nil,
onCompletion: @escaping (Result<URL, any Error>) -> Void,
onCancellation: (() -> Void)? = nil
) -> some View where D : WritableDocument, D.Writer.Destination == URL
制約D.Writer.Destination == URLが肝心な部分です。エクスポーターは、writerがURLへ書き込む書き込み可能なドキュメントだけを受け入れます。これはまさにシステムダイアログが扱うディスク上のファイルのケースです。Appleはライフサイクルを正確にドキュメント化しています。ダイアログはdocumentが非nilのときにのみ現れ、onCompletionが実行される前にisPresentedはfalseに設定され、ユーザーによるキャンセルはisPresentedをfalseに設定してonCancellationを呼び出します。28読み込み可能と書き込み可能の分割こそが、ビューア専用の機能が書き込み側に一切準拠することなくドキュメントをインポートできるようにするものです。
何を残すかを決めるツールバー
ツールバーは場所が足りなくなります。コンパクト幅のiPhone、リサイズされたMacのウィンドウ、アクティブな検索フィールド、いずれもコントロールの数より少ないスロットしか残さないことがあります。iOS 27より前は、フレームワークがあなたに代わって追い出しの判断をしていました。これからはあなたが決めます。
ToolbarOverflowMenuは明示的なオーバーフローの場です。Appleはこれを、ツールバーモード、プラットフォーム、カスタマイズ可能性にかかわらず常にツールバーのオーバーフローメニューに配置されるアクションとして説明し、iOSとvisionOSではその内容はナビゲーションバーのオーバーフローメニューに着地します。13
nonisolated struct ToolbarOverflowMenu<Content> where Content : View
オーバーフローに抵抗するべきコントロールには、新しいtopBarPinnedTrailingの配置がアイテムをツールバーの末尾端にピン留めします。14
static let topBarPinnedTrailing: ToolbarItemPlacement
Appleがドキュメント化している微妙な点は、ピン留めされたアイテムは検索がアクティブで十分な場所がないときにのみオーバーフローメニューへ移動すること、そしてiOSとvisionOSでは上部バーがナビゲーションバーであることです。14したがってtopBarPinnedTrailingは、検索が迫らない限り決して埋もれさせたくない1つか2つのコントロールのためのものです。
選択が絶対的ではなく相対的なときは、ToolbarContentに対するvisibilityPriority(_:)がアイテムをランク付けし、フレームワークに追い出しの順序を知らせます。15
@MainActor @preconcurrency
func visibilityPriority(_ priority: ToolbarItemVisibilityPriority) -> some ToolbarContent
Appleのルールは、ツールバーの場所が限られているとき、優先度の低いアイテムが優先度の高いアイテムより先にオーバーフローメニューへ移動するというものです。15重要なコントロールは末尾端に置きながらも、ウィンドウが縮んでも表示され続けられます。topBarPinnedTrailingとToolbarOverflowMenuと組み合わせれば、優雅な劣化のための完全な語彙が手に入ります。必須のものはピン留めし、残りには優先度を付け、常に二次的なものはオーバーフローへ送るのです。
関連する1つの修飾子は、ツールバーをスクロール挙動と、iOS 26が導入したLiquid Glassのクロームに結びつけます。toolbarMinimizeBehavior(_:for:)はスクロールに応じたツールバーの最小化を有効にし、Appleはナビゲーションバーが最小化するとき、統合された上部タブバーもそれとともに最小化すると述べています。29
nonisolated func toolbarMinimizeBehavior(
_ behavior: ToolbarMinimizeBehavior,
for bars: ToolbarPlacement...
) -> some View
サポートされる配置はナビゲーションバーで、デフォルトではバーが最小化するにつれてセーフエリアが調整されます。29Liquid Glassのツールバーを採用していて、ユーザーが読み進めるにつれて引っ込んでほしいと思っていたなら、それを実現するのがこの修飾子です。
アイテムベースのアラートとエラー表示
SwiftUIのアラートAPIには長らくブール値の形式(alert(_:isPresented:))があり、アラートのデータを表示フラグとは別の@Stateにしまうことを強いてきました。iOS 27は、シートやポップオーバーのAPIがすでに持っていたアイテムベースの形式を加えるので、データそのものがトリガーになります。
アイテムベースのアラートは、バインディングが非nilになるたびに表示され、ラップ解除された値をアクションビルダーに渡します。17
nonisolated func alert<A, T>(
_ title: Text,
item data: Binding<T?>,
@ContentBuilder actions: (T) -> A
) -> some View where A : View
メッセージビルダーを加える対応するオーバーロードalert(_:item:actions:message:)があり、confirmationDialogにも同等のペアがあるので、同じアイテム駆動のパターンがアラートとダイアログをまたいで持ち越されます。183031Appleの契約はそれぞれ同じです。表示が現れるにはデータが非nilでなければならず、表示が発生した後にデータへ加えた変更は無視されます。18
エラー表示のオーバーロードは、真に新しい能力です。エラーをカスタム構造体へマッピングする代わりに、Errorを直接バインドします。16
nonisolated func alert<E, A, M>(
error: Binding<E?>,
@ContentBuilder actions: (E) -> A,
@ContentBuilder message: (E) -> M
) -> some View where E : Error, A : View, M : View
採用する価値を生むのはその挙動です。エラー値がnilでないとき、システムはアラートを表示し、エラーがLocalizedErrorであればタイトルはエラーのerrorDescriptionから推測され、そうでなければタイトルはローカライズされた説明にフォールバックします。16すでに定義したLocalizedErrorが、追加の配線なしで自身のアラートタイトルを駆動するようになります。より単純なオーバーロードalert(error:actions:)は、OKアクションだけで十分なときにメッセージビルダーを省きます。19
nonisolated func alert<E, A>(
error: Binding<E?>,
@ContentBuilder actions: () -> A
) -> some View where E : Error, A : View
struct EditorView: View {
@State private var saveError: SaveError?
var body: some View {
Form { /* ... */ }
.alert(error: $saveError) { error in
Button("Retry") { retry() }
Button("Cancel", role: .cancel) { }
} message: { error in
Text(error.recoverySuggestion ?? "")
}
}
}
消えるパターンはこうです。手作りのAlertError構造体、identifiableなラッパー、そしてあなたの本物のエラー型とアラートのデータソースの間のマッピングコード。あなたはコードがすでに生成しているError?をバインドします。
AsyncImageがURLRequestで大人になる
AsyncImageはURLイニシャライザとともに登場しましたが、ヘッダー、キャッシュポリシー、タイムアウトを設定する手段はありませんでした。iOS 27の追加はURLRequestを取り、これがその3つすべてを運ぶオブジェクトです。
最も単純な形式は、リクエストから画像を読み込んで表示します。20
nonisolated init(request: URLRequest, scale: CGFloat = 1) where Content == Image
段階的な形式はコンテンツクロージャを駆動するAsyncImagePhaseを与え、Appleはリクエストを介してキャッシュポリシーとタイムアウト間隔を指定できると述べています。32
nonisolated init(
request: URLRequest?,
scale: CGFloat = 1,
transaction: Transaction = Transaction(),
@ContentBuilder content: @escaping (AsyncImagePhase) -> Content
)
「読み込むまでこれを表示し、成功したらそれを表示する」という一般的な分担のためのcontent/placeholder形式もあります。333つすべてに共通する挙動は、ドキュメント化されたAsyncImageの契約です。SwiftUIは読み込みが完了するまでプレースホルダーを表示し、成功時に画像を差し込み、失敗時はプレースホルダーを保ちます。20
付随する修飾子はasyncImageURLSession(_:)で、ビュー内のAsyncImageインスタンスに取得用のURLSessionを渡します。34
nonisolated func asyncImageURLSession(_ urlSession: URLSession) -> some View
var body: some View {
List(avatars) { avatar in
AsyncImage(request: URLRequest(url: avatar.url))
.frame(width: 44, height: 44)
}
.asyncImageURLSession(authenticatedSession)
}
この組み合わせが、認証付き画像読み込みへの答えです。リクエストはAuthorizationヘッダーやカスタムキャッシュポリシーを付けられるようにし、セッション修飾子はサブツリー全体が1つの設定済みURLSession(カスタムヘッダー、ディスクキャッシュ、プロキシ)を共有できるようにします。各AsyncImageが共有セッションにフォールバックする代わりにです。トークンの背後でアバターを読み込むアプリにとって、これは動くか動かないかの違いになります。
こちらも登場
いくつかのより小さな追加は、それぞれ特定の摩擦を取り除くので、1行ずつ触れる価値があります。
swipeActionsはonPresentationChanged:クロージャを持つオーバーロードを獲得します。これは行のスワイプアクションが表示されるとtrue、消えるとfalseで発火するので、アクションが表示されている間に行を暗くしたり周囲のクロームを更新したりできます。21ListではなくScrollViewやLazyVStackの上に構築されたカスタムの行レイアウトには、swipeActionsContainer()が、Listがすでに自動で行っているように、行をまたいだ消去と相互排他を調整します(Listに適用しても何も起こりません)。22
NavigationTransition.crossFadeは、現れるビューと消えるビューの間でクロスフェードするトランジションです。シートに指定すると、コンテンツを覆うために上へ移動するのではなく、コンテンツの上にシートをフェードインさせます。23TabRole.prominentは、サポートされるタブバーで1つのタブに目立つ視覚的扱いを与えます。Appleは、明示的な.prominentタブがない場合、.searchロールのタブがデフォルトで目立つ扱いを受けることがあると述べています。24
UIHostingSceneDelegateはUISceneDelegateを拡張してSwiftUIのシーンを橋渡しし、準拠するクラスの静的なrootSceneプロパティで宣言されたSwiftUIのシーンをUIKitがアクティブ化できるようにします。25(これはここで唯一、ほとんどのプラットフォームでiOS 26.0までさかのぼり、27.0ベータでtvOSに到達する項目です。25)そのシーンの配管は見た目以上に重要です。なぜならiOS 27はUIKitのシーンベースのライフサイクルを必須要件にもしているからです。最新のSDKで構築されたアプリでそれを採用していないものは、まったく起動できなくなります。そしてGestureInputKindsは、ジェスチャが認識すべき入力種別を指定するオプションセットで、たとえばタッチとポインタを区別するジェスチャの基盤です。26
ContentBuilderがリザルトビルダーを統一する
上記の追加はAPIの表面です。2027サイクルの1つの変更は配管であり、それはあなたが採用するどれか1つではなく、コンパイルするすべてのビューに触れます。セッション269はそれを、ほとんどのSwiftUI開発者がぶつかったことのあるエラーを通して位置づけます。「コンパイラはこの式を妥当な時間内に型チェックできません」。37
原因はオーバーロード解決です。コンテンツをSection、Group、ForEachでラップするビューは、コンパイラに決定木を歩かせます。Appleが説明するとおり、「まずコンパイラはSectionのどのオーバーロードを使うかを選ばなければなりません。SectionはViewまたはTableRowContentのいずれかを生成するビルダーで初期化できます。どちらを使うか知るために、コンパイラは両方の選択肢を試さなければなりません」。その分岐は入れ子になります。「ネストしたForEachについては、コンパイラはそれぞれを試さなければなりません。そしてForEachのビルダーには独自の選択肢一式があり、それらもチェックされる必要があります」。各層がパスを乗算し、「これらのパスをそれぞれ試すことで、型チェックはますます高コストになります」。37
この修正はその木を畳みます。「最も一般的なビルダー一式が今や単一のイニシャライザを共有し、まっすぐな1つのパスだけが残ります。これが可能なのは、複数の異なるビルダー型が単一のビルダー、ContentBuilderの下に統一されたからです」。Appleはこれをより長い弧の始まりとして位置づけます。「これはSwiftUIのすべてのAPIにわたって統一されたビルダーを可能にするための一歩です」。37
2つの特性が、ContentBuilderを後ではなく今から頼れるものにします。デプロイメントターゲットのコストがかかりません。「ContentBuilderはどんな最小デプロイメントターゲットでも使えます。なぜなら内部的には、既存のViewBuilderの進化形だからです」。37そして、何を出荷するかにかかわらず、その恩恵はビルド時に得られます。「ContentBuilderはXcode 27でビルドする際、SwiftUIの型チェックパフォーマンスに大幅な改善をもたらします。2027リリースをターゲットにしていても、それ以前のリリースをターゲットにしていても、です」。37Appleのドキュメントは宣言内でその後方互換性を裏付けています。ContentBuilderは型エイリアスで、その利用可能性はiOS 13.0およびmacOS 10.15までずっとさかのぼって記載され、「クロージャからビューやその他のコンテンツ型を構築するカスタムパラメータ属性」と説明されています。38
SwiftのエンジニアリングマネージャーであるHolly Borla氏は、WWDC26のクロージングインタビューでコンパイラ側を裏付けました。このエラーは「コンパイラの型チェッカーにおけるフォールバックです」と彼女は説明し、チームはそれが現れる場所を絞り込みました。「今年は、ネストしたクロージャやSwiftUIのビューボディでそのエラーを軽減することに大いに注力しました。それは非常によく見かける場所だからです」。40さらに彼女はこの作業がオープンに続いていると付け加えました。「まだやるべき作業があり、Open Source Swiftプロジェクトを通じてそれを追えます」。40
SwiftUIチームはグループラボでこの変更にもう1つの側面を与えました。WWDC 2026 SwiftUI Group Labのローカルに書き起こした録音からの言い換えですが、チームは古い型ごとのビルダーオーバーロードが自分たちのAPI表面を頭打ちにしていたと説明しました。ビルダーを加えたい場所のたびに型チェックが悪化したので、彼らは控えてしまい、ForEachなどを本来望んだより少ない位置でしか使えないままにしていたのです。39統一はその天井を持ち上げます。チームはまた、統一されたビルダーがビューの外でも使えるようになったので、ビューだけでなく自分の構成要素からSwiftUIのようなカスタムDSLを組み立てられるとも述べました。39コンパイラの恩恵が見出しですが、設計上の余地はより静かな帰結です。
採用の優先順位
これほど広範なリリースはトリアージに報います。まずはこれらに手を伸ばしましょう。
- 手書きの並べ替えを置き換える。
onMoveのインデックス計算を保守しているなら、reorderContainer(for:isEnabled:move:)とreorderable()はコードの正味の削除であり、より良いインタラクションです(プレースホルダーのアフォーダンスはあなたのものではなくシステムのものです)。23リスト中心のアプリにとって、並べ替えAPIはこのリリースで最も重みを持ちます。 - エラーバインディングのアラートを採用する。
alert(error:actions:message:)は、失敗を表に出すすべての画面からカスタムのエラーラッパー構造体を取り除き、すでに持っているLocalizedErrorが自身のアラートにタイトルを付けます。16低い労力で、即座の可読性が得られます。 - 大きなドラッグ元を遅延コンテナに切り替える。 数百を超えるドラッグ可能な行を持つどのリストも、
dragContainer(for:itemID:in:_:)とdraggable(containerItemID:containerNamespace:)から恩恵を受けます。フレームワークが、決して使わないかもしれないペイロードの実体化をやめるからです。45 - ツールバーに優先順位の筋立てを与える。 ツールバーがコンパクト幅でオーバーフローすることがあるなら、
visibilityPriority(_:)、topBarPinnedTrailing、ToolbarOverflowMenuによって、フレームワークのデフォルトを受け入れる代わりに何を残すかを決められます。131415 - ドキュメントアプリは反射的にではなく慎重に移行する。
ReadableDocument/WritableDocumentの分割は正しいモデルですが、他のものより大きな変更です。すでにドキュメント層に触れているときに採用し、小〜中規模のケースにはreader/writerプロトコルを手で実装するのではなくFileWrapperDocumentReader/FileWrapperDocumentWriterに頼りましょう。671011
一貫した筋立てはこうです。保守しているコードを削除してくれる追加は採用し、すでに動いているコードを再構築するものは後回しにする。
FAQ
iOS 27でSwiftUIのリストを並べ替え可能にするには?
コンテナにreorderContainer(for:isEnabled:move:)を宣言し、その内部のDynamicViewContent(通常はForEach)にreorderable()を適用します。コンテナのmoveクロージャは、あなたがモデルに適用するReorderDifferenceを受け取ります。フレームワークがドラッグジェスチャ、持ち上げ、そしてドロップ位置を示すプレースホルダーを処理します。231つのコンテナが複数のコレクションを保持するときは、コレクション識別子の型を取るreorderContainer(for:in:isEnabled:move:)のオーバーロードを使います。27
draggable(containerItemID:)と従来のdraggableの違いは?
draggable(containerItemID:containerNamespace:)はペイロードではなくアイテムの識別子だけを運ぶので、dragContainer(for:itemID:in:_:)の内部で遅延的に機能します。フレームワークは実際にドラッグされたアイテムをドラッグ開始時にのみ要求し、ペイロードを読むためにビューをレンダリングする必要がありません。45そのため、すべてのペイロードを前もって生成するのが無駄になるような、大きな、あるいは遅延読み込みのコレクションに対する正しい選択肢になります。
新しいSwiftUIのドキュメントモデルはFileDocumentとどう違うのか?
iOS 27は読み込みと書き込みを別々のプロトコルReadableDocumentとWritableDocumentに分割し、ディスク上の処理はDocumentReader/DocumentWriterが担い、読み書き両方が可能な型のためのDocument型エイリアスを用意します。67カスタムロジックを必要としない小〜中規模のドキュメントには、FileWrapperDocumentReaderとFileWrapperDocumentWriterが実装を提供し、URLDocumentConfigurationが開いているドキュメントを記述します。101112この分割により、ビューア専用の機能が読み込み側だけに準拠できます。
iOS 27ではSwiftUIでErrorから直接アラートを表示できるか?
はい。alert(error:actions:message:)とalert(error:actions:)は、E : ErrorであるBinding<E?>を取ります。バインドされたエラーが非nilのとき、システムはアラートを表示し、エラーがLocalizedErrorに準拠していればタイトルはそのerrorDescriptionから推測され、そうでなければローカライズされた説明を使います。1619もはやエラーをカスタムのidentifiableな構造体でラップする必要はありません。
場所が足りなくなったとき、どのツールバーアイテムが消えるかを制御するには?
ToolbarContentに対してvisibilityPriority(_:)を使ってアイテムをランク付けします。場所が縮むにつれて、優先度の低いアイテムが高いものより先にオーバーフローメニューへ移動します。15topBarPinnedTrailingを使ってコントロールを末尾端にピン留めすれば、検索がアクティブで場所がないときにのみオーバーフローへ移動するようになり、ToolbarOverflowMenuを使えば常にオーバーフローメニューに居るアクションを宣言できます。1314
iOS 27ではAsyncImageはカスタムヘッダーを送ったりキャッシュポリシーを設定したりできるか?
はい。AsyncImage(request:scale:)の系列はURLRequestを取り、これがヘッダー、キャッシュポリシー、タイムアウト間隔を運びます。Appleはリクエストを介してキャッシュポリシーとタイムアウトを指定できると述べています。2032サブツリー内のAsyncImageインスタンス間で設定済みのURLSession(認証やカスタムキャッシュのため)を共有するには、asyncImageURLSession(_:)を適用します。34
Apple Ecosystemクラスタの全体像はこうです。SwiftUIの基盤(リザルトビルダー、不透明型、値型のビューツリー)、iOS 27のツールバー最小化挙動が関わるLiquid Glassのパターン、本稿のすべてのビューの下で状態層を駆動する@Observableの内部、そしてバックグラウンド実行、同期、Spotlightのための並行するiOS 27のApp Intentsの領域です。ハブはApple Ecosystemシリーズにあります。AIエージェントとともに使うiOSのより広い文脈については、iOSエージェント開発ガイドをご覧ください。
参考文献
-
Apple Developer Documentation: SwiftUI. ビュー、リスト、ドキュメント、ツールバー、そしてここで説明したiOS 27の追加を網羅するフレームワークリファレンス。 ↩
-
Apple Developer Documentation:
reorderContainer(for:isEnabled:move:)(iOS 27.0 beta). アイテムの並べ替えを許可するコンテナを定義する。moveクロージャにReorderDifferenceを渡す単一コレクションの便利版。 ↩↩↩↩↩↩ -
Apple Developer Documentation:
reorderable()(iOS 27.0 beta). 並べ替えコンテナのスコープ内で使われたとき、DynamicViewContentのビューを並べ替え可能にする。 ↩↩↩↩↩ -
Apple Developer Documentation:
dragContainer(for:itemID:in:_:)(iOS 27.0 beta; macOS 26.0). ドラッグ可能なビューのコンテナ。各アイテムの識別子へのKeyPathと、ドラッグされた識別子に対するペイロードクロージャを取る。 ↩↩↩↩↩↩ -
Apple Developer Documentation:
draggable(containerItemID:containerNamespace:)(iOS 27.0 beta; macOS 26.0). ドラッグコンテナ内でビューをドラッグ元として有効化し、識別子だけを供給することでコンテナが遅延的に機能するようにする。 ↩↩↩↩↩↩ -
Apple Developer Documentation:
ReadableDocument(iOS 27.0 beta). 「ファイルからドキュメントを読み込むために使う型」。protocol ReadableDocument : AnyObjectとして宣言。読み書き可能にするにはWritableDocumentにも準拠するかDocument型エイリアスを使う。 ↩↩↩↩↩ -
Apple Developer Documentation:
WritableDocument(iOS 27.0 beta). 「ファイルへドキュメントを書き込むために使う型」。protocol WritableDocument : AnyObjectとして宣言。保存をサポートするにはReadableDocumentとともに準拠する。 ↩↩↩↩↩ -
Apple Developer Documentation:
DocumentReader(iOS 27.0 beta). 「ディスクからドキュメントを読み込むロジックを実装する」。protocol DocumentReader<Snapshot>として宣言。 ↩↩ -
Apple Developer Documentation:
DocumentWriter(iOS 27.0 beta). 「ディスクへドキュメントを書き込むロジックを実装する」。protocol DocumentWriter<Snapshot>として宣言。 ↩↩ -
Apple Developer Documentation:
FileWrapperDocumentReader(iOS 27.0 beta). ファイルラッパーに支えられたドキュメントreader。カスタムの読み込みロジックを必要としない小〜中規模のドキュメントに効率的。 ↩↩↩↩ -
Apple Developer Documentation:
FileWrapperDocumentWriter(iOS 27.0 beta). ファイルラッパーに支えられたドキュメントwriter。カスタムの書き込みロジックを必要としない小〜中規模のドキュメントに効率的。 ↩↩↩↩ -
Apple Developer Documentation:
URLDocumentConfiguration(iOS 27.0 beta). 「開いているドキュメントの設定とプロパティの一式」。@MainActor final class URLDocumentConfigurationとして宣言。 ↩↩↩ -
Apple Developer Documentation:
ToolbarOverflowMenu(iOS 27.0 beta). 「ツールバーのオーバーフローメニュー」。nonisolated struct ToolbarOverflowMenu<Content> where Content : Viewとして宣言。iOSとvisionOSでは内容はナビゲーションバーのオーバーフローメニューに配置される。 ↩↩↩↩ -
Apple Developer Documentation:
topBarPinnedTrailing(iOS 27.0 beta). 「アイテムをツールバーの末尾端にピン留めする配置」。ピン留めされたアイテムは検索がアクティブで十分な場所がないときにのみオーバーフローメニューへ移動する。 ↩↩↩↩↩ -
Apple Developer Documentation:
visibilityPriority(_:)(iOS 27.0 beta). 「ツールバーアイテムの表示優先度を定義する」。ツールバーの場所が限られているとき、優先度の低いアイテムが高いものより先にオーバーフローメニューへ移動する。 ↩↩↩↩↩ -
Apple Developer Documentation:
alert(error:actions:message:)(iOS 27.0 beta). 「エラーが存在するときにメッセージ付きのアラートを表示する」。エラーがLocalizedErrorであればタイトルはそのerrorDescriptionから推測され、そうでなければローカライズされた説明から推測される。 ↩↩↩↩↩ -
Apple Developer Documentation:
alert(_:item:actions:)(iOS 27.0 beta). 「指定したデータを使ってアラートの内容を生成し、テキストビューをタイトルとして使ってアラートを表示する」。アラートが現れるにはdataがnilでないことが必要。 ↩↩ -
Apple Developer Documentation:
alert(_:item:actions:message:)(iOS 27.0 beta). メッセージビルダーを持つアイテムベースのアラートオーバーロード。データは非nilでなければならず、表示後の変更は無視される。 ↩↩↩ -
Apple Developer Documentation:
alert(error:actions:)(iOS 27.0 beta). 「エラーが存在するときにアラートを表示する」。メッセージビルダーを持たないエラーバインディングのオーバーロード。 ↩↩↩ -
Apple Developer Documentation:
init(request:scale:)(iOS 27.0 beta). 「指定したURL読み込みリクエストから画像を読み込んで表示する」。init(request: URLRequest, scale: CGFloat = 1) where Content == Imageとして宣言。読み込みが完了するまでプレースホルダーを表示する。 ↩↩↩↩ -
Apple Developer Documentation:
swipeActions(edge:allowsFullSwipe:content:onPresentationChanged:)(iOS 27.0 beta). 行のスワイプアクションが表示されるとtrue、消えるとfalseでクロージャが呼ばれる。 ↩↩ -
Apple Developer Documentation:
swipeActionsContainer()(iOS 27.0 beta).ScrollViewや類似のコンテナで、行をまたいだスワイプアクションの消去と相互排他を調整する。Listに適用しても何も起こらない。 ↩↩ -
Apple Developer Documentation:
crossFade(iOS 27.0 beta). 「現れるビューと消えるビューの間でクロスフェードするナビゲーショントランジション」。シートに指定すると、覆うために上へ移動するのではなくコンテンツの上にフェードインする。 ↩↩ -
Apple Developer Documentation:
prominent(iOS 27.0 beta). 「目立つロール」。サポートされるタブバーで1つのタブに目立つ視覚的扱いを与える。明示的な.prominentタブがない場合、.searchロールのタブがデフォルトでそれを受けることがある。 ↩↩ -
Apple Developer Documentation:
UIHostingSceneDelegate(iOS 26.0; tvOS 27.0 beta). 「UISceneDelegateを拡張してSwiftUIのシーンを橋渡しする」。UIKitからアクティブ化するSwiftUIのシーンを、準拠するクラスの静的なrootSceneプロパティで宣言する。 ↩↩↩ -
Apple Developer Documentation:
GestureInputKinds(iOS 27.0 beta). 「ジェスチャが認識すべき入力種別を指定するオプションセット」。 ↩↩ -
Apple Developer Documentation:
reorderContainer(for:in:isEnabled:move:)(iOS 27.0 beta). 「アイテムの並べ替えを許可するコンテナを定義する」。コレクション識別子の型でキー付けされた複数コレクションのオーバーロード。コンテナが2つ以上のコレクションを保持するときに使う。 ↩↩↩ -
Apple Developer Documentation:
fileExporter(isPresented:document:contentType:defaultFilename:onCompletion:onCancellation:)(iOS 27.0 beta). writerの宛先がURLであるWritableDocumentをエクスポートするシステムダイアログを表示する。ダイアログはdocumentが非nilのときにのみ現れる。 ↩↩ -
Apple Developer Documentation:
toolbarMinimizeBehavior(_:for:)(iOS 27.0 beta). 「指定したバーの最小化挙動を設定する」。スクロールに応じたツールバーの最小化を有効にする。サポートされる配置はナビゲーションバーで、統合された上部タブバーもそれとともに最小化する。 ↩↩ -
Apple Developer Documentation:
confirmationDialog(_:item:titleVisibility:actions:message:)(iOS 27.0 beta). データを使ってダイアログの内容を生成し、メッセージ用のテキストビューを伴って、メッセージ付きの確認ダイアログを表示する。 ↩ -
Apple Developer Documentation:
confirmationDialog(_:item:titleVisibility:actions:)(iOS 27.0 beta). メッセージビルダーを持たないアイテムベースの確認ダイアログ。 ↩ -
Apple Developer Documentation:
init(request:scale:transaction:content:)(iOS 27.0 beta). 「指定したURL読み込みリクエストから変更可能な画像を段階的に読み込んで表示する」。リクエストを介してキャッシュポリシーとタイムアウト間隔を指定できる。 ↩↩ -
Apple Developer Documentation:
init(request:scale:content:placeholder:)(iOS 27.0 beta). 「画像が読み込まれるまでカスタムのプレースホルダーを使い、指定したURL読み込みリクエストから変更可能な画像を読み込んで表示する」。 ↩ -
Apple Developer Documentation:
asyncImageURLSession(_:)(iOS 27.0 beta). 「ビューに含まれる非同期画像が画像データを取得する際に使うURLセッションを追加する修飾子」。 ↩↩ -
Apple, WWDC26 session 269, “What’s new in SwiftUI.” developer.apple.com/videos/play/wwdc2026/269. このセッションは、リリースを洗練された見た目と操作感、新しいドキュメントAPI、新しいインタラクションの方法、そしてパフォーマンスの改善を中心に位置づけている。 ↩
-
Apple, WWDC26 session 271, “Code-along: Build powerful drag and drop in SwiftUI.” developer.apple.com/videos/play/wwdc2026/271. 並べ替えAPIがドラッグ&ドロップをサポートするどのコンテナでも機能するため、並べ替えコードが
ListとLazyVGridの間で変更なしに移る様子が示されている。 ↩ -
Apple, WWDC26 session 269, “What’s new in SwiftUI.” developer.apple.com/videos/play/wwdc2026/269. 「妥当な時間内にこの式を型チェックできない」エラー、
Section/Group/ForEachのオーバーロード決定木、一般的なビルダーのContentBuilderの下への統一、SwiftUI全体にわたる統一されたビルダーへの一歩、ViewBuilderの進化形としてのどんな最小デプロイメントターゲットでもの対応、そして2027リリースとそれ以前のリリースをまたぐXcode 27の型チェック改善の出典。 ↩↩↩↩↩ -
Apple Developer Documentation:
ContentBuilder. 「クロージャからビューやその他のコンテンツ型を構築するカスタムパラメータ属性」。型エイリアスとして宣言され、利用可能性はiOS 13.0およびmacOS 10.15までさかのぼって記載されている。 ↩ -
Apple, WWDC26 session 8006, “SwiftUI Group Lab.” developer.apple.com/videos/play/wwdc2026/8006. WWDC 2026 SwiftUI Group Labのローカルに書き起こした録音からの言い換え。Appleはラボの公式キャプションを公開していない。型ごとのビルダーオーバーロードがチーム自身のAPI表面を頭打ちにしていたこと(追加のたびに型チェックが悪化した)、そして統一されたビルダーがビューの外でも使えてSwiftUIのようなカスタムDSLを可能にすることの出典。 ↩↩
-
Apple, WWDC26 session 400, “Dub Dub Daily: Day 5”, official transcript. SwiftのエンジニアリングマネージャーHolly Borla氏が、Jeffとのクロージングインタビューにて。「コンパイラの型チェッカーにおけるフォールバック」という特徴づけ、ネストしたクロージャとSwiftUIのビューボディへの注力、そして継続中のOpen Source Swiftの作業の出典。 ↩↩