You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

SwiftUI中如何为通过.task修饰符或模型调用API的视图配置示例数据预览?

SwiftUI中如何为通过.task修饰符或模型调用API的视图配置示例数据预览?

这个问题我太有共鸣了——SwiftUI预览默认会执行真实的API调用逻辑,不仅慢,还容易暴露测试环境的密钥,完全没必要让预览依赖真实服务器。下面针对你提到的两种场景,分别给你实用的解决方案:

场景1:使用.task修饰符的视图

核心思路是把数据获取逻辑和视图解耦,通过依赖注入的方式让视图接受一个数据源,这样真实运行时用真实数据源,预览时用返回示例数据的模拟数据源。

步骤1:定义数据源协议

先抽象出数据获取的行为,避免视图直接依赖具体的HTTP客户端:

// 定义数据源协议,统一数据获取的接口
protocol BookDataSource {
    func fetchBooks() async -> [Book]
}

// 真实环境的数据源,调用真实API
struct RealBookDataSource: BookDataSource {
    func fetchBooks() async -> [Book] {
        await HTTPClient.fetchBooks()
    }
}

// 预览专用的模拟数据源,直接返回示例数据
struct MockBookDataSource: BookDataSource {
    func fetchBooks() async -> [Book] {
        return [
            Book(title: "《SwiftUI实战指南》", id: "1"),
            Book(title: "《iOS开发进阶》", id: "2")
        ]
    }
}

步骤2:修改视图接受数据源

ListView通过初始化器接受数据源,默认使用真实数据源,不影响正常使用:

struct ListView: View {
    @State private var books: [Book] = []
    private let dataSource: BookDataSource

    // 正常运行时用默认的真实数据源
    init(dataSource: BookDataSource = RealBookDataSource()) {
        self.dataSource = dataSource
    }

    var body: some View {
        List(books) { book in
            Text(book.title)
        }
        .task {
            // 现在调用的是数据源的方法,而非直接调用HTTPClient
            books = await dataSource.fetchBooks()
        }
    }
}

步骤3:配置预览

预览时传入模拟数据源,就能看到示例数据了:

struct ListView_Previews: PreviewProvider {
    static var previews: some View {
        ListView(dataSource: MockBookDataSource())
    }
}

场景2:使用自定义模型(如BookModel)的视图

这种场景下,我们可以给模型添加预览专用的初始化逻辑,让预览时直接加载示例数据,跳过真实API调用。

步骤1:给模型添加预览支持

假设你的BookModelObservableObject,可以给它加一个预览专用的初始化器或者静态预览属性:

class BookModel: ObservableObject {
    @Published var books: [Book] = []

    // 真实环境的数据获取方法
    func fetchBooks() async {
        books = await HTTPClient.fetchBooks()
    }

    // 正常使用的初始化器
    init() {}

    // 预览专用初始化器,直接传入示例数据
    init(previewBooks: [Book]) {
        self.books = previewBooks
    }

    // 更便捷的预览属性(可选)
    static var preview: BookModel {
        BookModel(previewBooks: [
            Book(title: "预览书籍1", id: "1"),
            Book(title: "预览书籍2", id: "2")
        ])
    }
}

步骤2:修改视图接受模型实例

ListView通过初始化器接受模型,默认使用正常初始化的模型:

struct ListView: View {
    @StateObject private var model: BookModel

    // 正常运行时用默认的模型
    init(model: BookModel = BookModel()) {
        self._model = StateObject(wrappedValue: model)
    }

    var body: some View {
        List(model.books) { book in
            Text(book.title)
        }
        .task {
            await model.fetchBooks()
        }
    }
}

步骤3:配置预览

预览时直接传入带示例数据的模型即可:

struct ListView_Previews: PreviewProvider {
    static var previews: some View {
        // 两种方式任选其一
        ListView(model: BookModel.preview)
        // 或者 ListView(model: BookModel(previewBooks: [/* 自定义示例数据 */]))
    }
}

额外小技巧

如果不想大改现有代码,也可以在预览中临时禁用.task的执行,但这种方式不够优雅,因为预览状态和真实运行状态可能不一致,只适合临时调试:

struct ListView_Previews: PreviewProvider {
    static var previews: some View {
        ListView()
            .onAppear {
                // 直接给books赋值示例数据,跳过task里的API调用
                // 注意:这里需要把books改成@Published或者用其他方式修改,适合临时场景
            }
    }
}

备注:内容来源于stack exchange,提问作者Pieter

火山引擎 最新活动