Swiftui 在父视图中使用@FetchRequest结果

Swiftui 在父视图中使用@FetchRequest结果,swiftui,fetchrequest,Swiftui,Fetchrequest,我试图把一些组件抽象成更小的部分。为此,我创建了以下列表: struct ArticleList:视图{ var fetchRequest:fetchRequest var结果:FetchedResults{fetchRequest.wrappedValue} init(){ fetchRequest=fetchRequest( 实体:Article.entity(), sortDescriptors:[] ) } var body:一些观点{ ForEach(results){article

我试图把一些组件抽象成更小的部分。为此,我创建了以下列表:

struct ArticleList:视图{
var fetchRequest:fetchRequest
var结果:FetchedResults{fetchRequest.wrappedValue}
init(){
fetchRequest=fetchRequest(
实体:Article.entity(),
sortDescriptors:[]
)
}
var body:一些观点{
ForEach(results){article in
文本(article.name???)
}
}
}
现在我有了一个容器,它将显示列表组件,如果子组件内的条件满足,还将显示一些附加内容:

struct容器:视图{
var body:一些观点{
让articleList=ArticleList2()
返回组{
如果articleList.results.isEmpty{
文本(“添加”)
}
标记名
}
}
}
现在我的问题是代码崩溃,出现以下异常:

Thread 1: EXC_BAD_INSTRUCTION (code=EXC_I386_INVOP, subcode=0x0)
通过控制台进一步调试,我得到了以下反馈:

(lldb) po self.results
error: warning: couldn't get required object pointer (substituting NULL): Couldn't load 'self' because its value couldn't be evaluated
调试
po self.fetchRequest
有效,它包含
fetchRequest
实例的一个实例
po self.fetchRequest.wrappedValue提供与上述
self.results
相同的错误

有人知道为什么这段代码会崩溃,以及可能的解决方法是什么吗


谢谢。

您的获取请求不起作用,因为在您创建和使用
ArticleList
视图时,还没有可用的托管对象上下文

无论如何。。。找到下面修改过的(我尽量减少更改)您的代码。使用Xcode 11.4/iOS 13.3进行测试

struct ArticleList: View {
    // always keep View memebers private to safe yourself from misconcept
    private var fetchRequest: FetchRequest<Article>
    private var results: FetchedResults<Article> { fetchRequest.wrappedValue }
    private var reportEmpty: () -> ()

    init(_ onEmpty: @escaping () -> ()) {
        reportEmpty = onEmpty

        // FetchRequest needs @Environment(\.managedObjectContext) which is not available (!) yet
        fetchRequest = FetchRequest<Article>(
            entity: Article.entity(),
            sortDescriptors: []
        )
    }

    var body: some View {
        // here (!) results are valid, because before call body SwiftUI executed FetchRequest
        if self.results.isEmpty { 
            self.reportEmpty()
        }
        return Group {
            ForEach(results, id: \.self) { article in
                Text(article.name ?? "")
            }
        }
    }
}

struct Container: View {
    @State private var isEmpty = false

    var body: some View {
        return Group {
            if self.isEmpty { // use view state for any view's conditions
                Text("Add")
            }

            ArticleList { // View must live only in view hierarchy !!
                DispatchQueue.main.async {
                    self.isEmpty = true
                }
            }
        }
    }
}
struct ArticleList:视图{
//始终保持查看成员的隐私,以避免误解
私有变量fetchRequest:fetchRequest
私有变量结果:FetchedResults{fetchRequest.wrappedValue}
私有变量reportEmpty:()->()
init(onEmpty:@escaping()->()){
reportEmpty=onEmpty
//FetchRequest需要@Environment(\.managedObjectContext),但它还不可用(!)
fetchRequest=fetchRequest(
实体:Article.entity(),
sortDescriptors:[]
)
}
var body:一些观点{
//这里(!)的结果是有效的,因为在调用体之前SwiftUI执行了FetchRequest
如果self.results.isEmpty{
self.reportEmpty()
}
返回组{
ForEach(results,id:\.self){article in
文本(article.name???)
}
}
}
}
结构容器:视图{
@State private var isEmpty=false
var body:一些观点{
返回组{
如果self.isEmpty{//则对任何视图的条件使用视图状态
文本(“添加”)
}
ArticleList{//视图必须仅存在于视图层次结构中!!
DispatchQueue.main.async{
self.isEmpty=true
}
}
}
}
}

虽然@Asperi的解决方案有效,但我现在确实以不同的方式实现了它

我将一个闭包传递给
文章列表
,如果按下
按钮
,则执行该回调。只有当
文章列表
为空时,
按钮
才可用,但现在
文章列表
负责显示按钮(这使它对我来说更易于重用:

struct ArticleList:视图{
var fetchRequest:fetchRequest
var结果:FetchedResults{fetchRequest.wrappedValue}
让onCreate:(()->Void)
初始化(onCreate:@escaping(()->Void)){
fetchRequest=fetchRequest(
实体:Article.entity(),
sortDescriptors:[]
)
self.onCreate=onCreate
}
var body:一些观点{
团体{
如果结果是空的{
按钮(操作:onCreate){
文本(“添加”)
}
}
ForEach(results){article in
文本(article.name???)
}
}
}
}
结构容器:视图{
var body:一些观点{
文章列表(onCreate:onCreate)
}
func onCreate(){
//在容器内创建项目
}
}

虽然我可以确认这是可行的,但在视图构建内部状态发生更改时感觉不对劲。这意味着,如果状态发生更改,视图层次结构将构建两次。这也将触发多个提取请求以执行。@TobiasTom,延迟刷新是常规做法,有些操作甚至在没有uch方法(实际上相当于
setNeedsLayout
setNeedsDisplay
。SwiftUI处理此类情况,不会重新呈现未更新的视图。但关于获取数据,这是您的责任,不应依赖或影响显示视图。其他一切都由您决定。