Slide 1

Slide 1 text

THE SHAPE OF NOTHING EmptyView は本当に”空”なのか? Akihiko Sato iOSDC Japan 2026

Slide 2

Slide 2 text

EmptyView()

Slide 3

Slide 3 text

📃 Documentation Structure EmptyView A view that doesn t contain any content Overview ’ EmptyView represents the absence of a view.

Slide 4

Slide 4 text

🔍 事件現場 iOSDC Track A の参加者をチェックするアプリ +, − 参加者の増減 ^ ATTENDEES パネルの表示・非表示 閉じた状態は EmptyView で表現 パネルを開閉すると、 State まで初期化されてしまう 🚨

Slide 5

Slide 5 text

🔍 現場検証 struct ContentView: View { @State private var isExpanded = true var body: some View { VStack(alignment: .leading, spacing: 24) { // ヘッダーセクション Button { isExpanded.toggle() } label: { Image(systemName: "chevron.up") } if isExpanded { AttendeeCounterView() } else { EmptyView() } } } } struct AttendeeCounterView: View { @State private var count = 0 var body: some View { // カウンターUI } }

Slide 6

Slide 6 text

🔍 EmptyView の Public Interface @frozen public struct EmptyView : View { @inlinable public init() public typealias Body = Never } 1. 保存プロパティを持たない 2. Body == Never 3. 表示する子 View も持たない EmptyView の中身は、本当に空だった…

Slide 7

Slide 7 text

🔍 EmptyView 周辺を再検証 SwiftUI から見えている構造 var body: some View { if isSessionExpanded { some View = _ConditionalContent< AttendeeCounterView() AttendeeCounterView, True EmptyView False } else { EmptyView() } } >

Slide 8

Slide 8 text

🔍 View Identity SwiftUI が View の同一性を判断するための仕組み • Explicit Identity (明示的な識別) • Structural Identity(構造による識別)

Slide 9

Slide 9 text

🔍 Structural Identity View の型と、階層構造上の位置から決まる Identity var body: some View { // ・・・ if isExpanded { AttendeeCounterView() } else { EmptyView() } } “True” View IDENTITY: A “False” View IDENTITY: B

Slide 10

Slide 10 text

🔍 Identity と Lifetime の関係 View Identity の Lifetime → State の Lifetime AttendeeCounterView Lifetime IDENTITY: A 開始 count = 0 onAppear count = 1 count = 2 count = 3 IDENTITY: A 終了 onDisappear

Slide 11

Slide 11 text

🔍 Explicit Identity 開発者やデータが明示的に与える Identity ScrollViewReader { proxy in HeaderView() .id(headerId) ContentView() Button("Scroll to Top") { withAnimation { proxy.scrollTo(headerId) } } }

Slide 12

Slide 12 text

✅ State を維持する設計 struct ContentView: View { @State private var isExpanded = true @State private var count = 0 var body: some View { VStack(alignment: .leading, spacing: 24) { // ・・・ } } } if isExpanded { AttendeeCounterView(count: $count) } else { EmptyView() } struct AttendeeCounterView: View { @Binding var count: Int var body: some View { // カウンターUI } }

Slide 13

Slide 13 text

🔍 まとめ • EmptyView の中身は、本当に空だった • State 消失の鍵は、View Identity と Lifetime • 真相は、自分のメンタルモデル 「EmptyView = 存在しない」

Slide 14

Slide 14 text

📄 資料 • EmptyView - Apple Developer Documentation • OpenSwiftUI: EmptyView.swift • Demystify SwiftUI - WWDC21

Slide 15

Slide 15 text

About Me Akihiko Sato Software Engineer @akkiee76 @akkie76 著書『コードレビューの教科書』

Slide 16

Slide 16 text

EmptyView() https://speakerdeck.com/akkie76