Post

Reading Apple Docs: PreviewModifier Step by Step

Reading Apple Docs: PreviewModifier Step by Step

讀 Apple 文件的方法:以 PreviewModifier 為例

Apple 在 What’s new in SwiftData – WWDC24 裡用一句話介紹了 PreviewModifier

To use preview traits, you can create a new struct that conforms to PreviewModifier which has two functions: one for setting up a shared context for the preview, and another to apply the shared context to a view. For SwiftData previews, you can vend a ModelContainer as the shared context, creating a ModelConfiguration that stores data in memory only.

看完這句話,你應該要能寫出一個完整的 PreviewModifier 實作。

但如果你跟我一樣,看完之後只覺得「每個字都認識,合在一起不知道在說什麼」——這篇就是給你的。

拆解方法:一段一段對應程式碼

Apple 的 WWDC 講稿風格是把很多前置知識壓縮成一句話。要讀懂它,需要三件事:

  1. 把句子切成片段
  2. 每個片段對應到一段程式碼
  3. 標出「Apple 沒說、但你需要自己去查的東西」

以下用這個方法拆解那句話。


片段 1

“create a new struct that conforms to PreviewModifier

對應:

1
2
3
struct SampleDataPreviewModifier: PreviewModifier {
    // ...
}

Apple 沒說的: PreviewModifier 是哪來的?它是 SwiftUI 的 protocol,iOS 18+ 才有。你需要 import SwiftUI

片段 2

“which has two functions”

對應:這個 protocol 要求你實作兩個 method。

Apple 沒說的: 這兩個 method 的確切簽名是什麼?你要去查 PreviewModifier 文件 才知道:

1
2
3
4
5
// 第一個
static func makeSharedContext() async throws -> Self.Context

// 第二個
func body(content: Self.Content, context: Self.Context) -> some View

Context 是一個 associated type,你自己決定它是什麼型別。

片段 3

“one for setting up a shared context for the preview”

對應:第一個 method makeSharedContext()

1
2
3
static func makeSharedContext() async throws -> ??? {
    // 在這裡準備 Preview 需要的東西
}

??? 要填什麼?看下一段。

片段 4

“For SwiftData previews, you can vend a ModelContainer as the shared context”

對應:Context = ModelContainer。所以回傳型別是 ModelContainer

1
2
3
static func makeSharedContext() async throws -> ModelContainer {
    // 建一個 ModelContainer
}

Apple 沒說的: 你需要 import SwiftData

片段 5

“creating a ModelConfiguration that stores data in memory only”

對應:在 makeSharedContext() 裡面用 isStoredInMemoryOnly: true

1
2
3
4
5
static func makeSharedContext() async throws -> ModelContainer {
    let config = ModelConfiguration(isStoredInMemoryOnly: true)
    let container = try ModelContainer(for: CareEvent.self, configurations: [config])
    return container
}

Apple 沒說的: ModelContainer(for:configurations:) 的第一個參數要填你的 Model 型別。而且如果你想 Preview 有資料可以看,要自己塞假資料(Apple 這句話完全沒提假資料的事)。

片段 6

“and another to apply the shared context to a view”

對應:第二個 method body(content:context:)

1
2
3
func body(content: Content, context: ModelContainer) -> some View {
    content.modelContainer(context)
}

content 是你的 Preview View,context 是剛才 makeSharedContext() 回傳的 ModelContainer。用 .modelContainer() 注入就好。


組裝起來

把六個片段組在一起:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
import SwiftData
import SwiftUI

struct SampleDataPreviewModifier: PreviewModifier {
    static func makeSharedContext() async throws -> ModelContainer {
        let config = ModelConfiguration(isStoredInMemoryOnly: true)
        let container = try ModelContainer(for: CareEvent.self, configurations: [config])
        // 塞假資料(Apple 沒提,但 Preview 需要)
        CareEventSeeder.seedForPreview(into: container.mainContext)
        return container
    }

    func body(content: Content, context: ModelContainer) -> some View {
        content.modelContainer(context)
    }
}

Apple 那句話完全沒提到的東西

寫完 struct 之後,你還需要:

1. 註冊成 PreviewTrait

#Preview(traits:) 認得你的 modifier:

1
2
3
extension PreviewTrait where T == Preview.ViewTraits {
    @MainActor static var sampleData: Self = .modifier(SampleDataPreviewModifier())
}

Apple 那句話完全沒提到這一步。你要看 WWDC session 的 demo 或查文件才知道。

2. 使用方式

1
2
3
#Preview(traits: .sampleData) {
    DailyTimelineView()
}

traits: 參數也沒有在那句話裡出現。

結論:Apple 文件的讀法

Apple 的文件和 WWDC 不是教學,是規格說明書。它假設你已經知道:

  • protocol 怎麼用
  • associated type 怎麼填
  • 去哪裡查 method 簽名

如果你覺得「看了 Apple 的說明還是不會寫」,不是你的問題。是 Apple 把「教學」和「規格」混在一起,卻用規格的寫法寫教學。

讀的時候,試試這個流程:

  1. 把句子切成片段
  2. 每個片段查一次文件(不是整篇看,是只查那個 API 的簽名)
  3. 標出「Apple 沒說的」(那些就是你需要額外查的)
  4. 先寫最小的 compilable 版本,再慢慢加功能

這比「看完整句話然後試著一次寫完」有效得多。

This post is licensed under CC BY 4.0 by the author.