SwiftUI 在 Xcode 预览修改视图 FetchedResults 对象的属性时为什么会崩溃?

解决Xcode预览中SwiftUI视图崩溃问题

在这里插入图片描述

概览

从 SwiftUI 诞生那天起,让秃头码农们又爱又恨的 Xcode 预览就在界面调试中扮演了及其重要的角色。不过,就是这位撸码中的绝对主角却尝尝在关键时刻“掉链子”。

在这里插入图片描述

比如当修改 SwiftUI 视图中 FetchedResults 对象的属性时,Xcode 预览可能会毫不留情的发生崩溃。这是怎么回事,又该如何解决呢?

在本篇博文中,您将学到如下内容:

本篇博文代码测试环境:macOS 15.2 + Xcode 16.1

相信学完本课后,小伙伴们对于 Xcode 预览中 SwiftUI 视图 FetchedResults 对象的领悟又会更加精进一层!

那还等什么呢?Let‘s go!!!😉


1. 莫名其妙的崩溃场景

在 SwiftUI 与 CoreData 共同协作的视图中,我们常常需要使用 @FetchRequest 属性包装器来获取所有相关的托管对象:

struct MainTest: View {    
    @FetchRequest(sortDescriptors: [.init(keyPath: \WorryObject.name, ascending: false)], animation: .bouncy) var allWorryObjects: FetchedResults<WorryObject>
    
    @State var ascending = false
    
    var body: some View {
        NavigationStack {
            List {
                ForEach(allWorryObjects) { wObj in
                    VStack(alignment: .leading) {
                        Label(wObj.name ?? "", systemImage: "brain.filled.head.profile")
                            .font(.title2)
                        Text(wObj.desc ?? "")
                            .foregroundStyle(.gray)
                    }
                }
            }
            .toolbar {
                ToolbarItem(placement: .topBarLeading) {
                    Button(ascending ? "升序" : "降序") {
                        ascending.toggle()
                    }
                }
            }
            .onChange(of: ascending) {_,new in
                allWorryObjects.nsSortDescriptors = [.init(keyPath: \WorryObject.name, ascending: ascending)]
            }
        }
    }
}

#Preview {
    let context = Common.moc_auto

    MainTest()
        .task {
            try! WorryObject.createDefaults(context)
        }
}

我们在上面的代码中主要做了这样几件事:

  • 使用 @FetchRequest 属性包装器产生结果为 FetchedResults<WorryObject> 的托管对象集合;
  • 遍历每个 WorryObject 对象并在视图中显示它们;
  • 让用户通过 ascending 状态切换 allWorryObjects 集合的排序方式,这是利用动态修改 FetchedResults<WorryObject> 的 nsSortDescriptors 属性来实现的;

不过,上面这段代码在 Xcode 预览中会有两个问题:

  1. 我们的 WorryObject.createDefaults() 方法明明产生了若干默认 WorryObject 对象,但在预览视图中却依旧是一片“空白”;
  2. 当我们切换 allWorryObjects 集合的排序方式时,预览会立即崩溃;

在这里插入图片描述

从预览崩溃栈中我们可以发现,崩溃的原因是重新设置 FetchedResults<WorryObject> 对象的 nsSortDescriptors 属性的操作所导致的:

在这里插入图片描述

然而奇怪的是,完全相同的代码在模拟器和真机上却毫无任何问题:

在这里插入图片描述

为什么单单 Xcode 预览会如此蛋疼呢?

2. 崩溃的根本原因

其实 Xcode Preview 榱崩栋折的原因很简单,我们也在之前很多篇博文详细讨论过了。

简单来说,在 Xcode 预览环境中 SwiftUI 视图若包含 @FetchRequest 属性包装器,则必须向视图传递正确的 managedObjectContext 环境变量。而且该环境变量必须特别针对预览做出修正。


关于在 Xcode 预览中向 SwiftUI 视图传递 managedObjectContext 环境变量导致的其它问题,请小伙伴们移步如下链接观赏进一步精彩的内容:


按照这种思路,我们可以很快写出修复方案:

struct MainTest: View {
    
    @Environment(\.managedObjectContext) var context
    @FetchRequest(sortDescriptors: [.init(keyPath: \WorryObject.name, ascending: false)], animation: .bouncy) var allWorryObjects: FetchedResults<WorryObject>
}

#Preview {
    let context = try! Common.moc_auto.cook4Preview(WorryObject.self)
    
    MainTest()
        .environment(\.managedObjectContext, context)
        .task {
            // 创建默认 WorryObject 对象集合
            try! WorryObject.createDefaults(context)
        }
}

在上面的代码中,我们向 MainTest 视图传递了修复后的托管对象上下文,这使得它在 Xcode 预览环境中表现的出类拔萃!棒棒哒!💯

在这里插入图片描述

至此,我们已经彻底了解了博文开头那个问题,并一发入魂给出完美的解决方案!么么哒!


想要进一步系统地学习 Swift 开发的小伙伴们,可以来我的《Swift 语言开发精讲》专栏逛一逛哦:

在这里插入图片描述


2024 博客之星的评选正如火如荼的进行中,如果小伙伴们觉得我的博客还不错就请投我神圣的一票吧,爱你们哦❤️:

在这里插入图片描述


总结

在本篇博文中,我们讨论了为何包含 FetchedResults 对象的 SwiftUI 视图属性被修改时,在 Xcode 预览中会导致崩溃。并在最后给出完美解决之道。

感谢观赏,再会啦!😎

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

大熊猫侯佩

赏点钱让我买杯可乐好吗 ;)

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值