SwiftUI:ObservableObject与fetchRequest init不兼容

3
在我的中,我声明了一个ObservableObject对象,代码如下:
import SwiftUI

    class SelectedDate: ObservableObject {
        @Published var selectedMonth: Date = Date()
        
        var startDateOfMonth: Date {
            let components = Calendar.current.dateComponents([.year, .month], from: self.selectedMonth)
            let startOfMonth = Calendar.current.date(from: components)!
            return startOfMonth
        }
    
        var endDateOfMonth: Date {
            var components = Calendar.current.dateComponents([.year, .month], from: self.selectedMonth)
            components.month = (components.month ?? 0) + 1
            let endOfMonth = Calendar.current.date(from: components)!
            return endOfMonth
        }
    }

struct DateView: View {
// other code
}

我可以在其他视图中像这样访问startDateOfMonth和endDateOfMonth:
Text("\(self.selectedDate.startDateOfMonth)")

但是当我试图在我的 TransactionsListView 中的fetchRequest初始化器中使用ObservableObject中的那些变量时,遇到了一个问题:

    import SwiftUI
    
    struct TransactionsListView: View {
    
    @Environment(\.managedObjectContext) var managedObjectContext
        var fetchRequest: FetchRequest<NPTransaction>
        var transactions: FetchedResults<NPTransaction> { fetchRequest.wrappedValue }
    @EnvironmentObject var selectedDate: SelectedDate

    // other code
            init() {
                    fetchRequest = FetchRequest<NPTransaction>(entity: NPTransaction.entity(), sortDescriptors: [
                        NSSortDescriptor(keyPath: \NPTransaction.date, ascending: false)
                    ], predicate: NSPredicate(format: "date >= %@ AND date < %@", self.selectedDate.startDateOfMonth as NSDate, self.selectedDate.endDateOfMonth as NSDate))
            }
    
    var body: some View {
    // other code
    }
    }

我遇到了一个错误:

变量“self.fetchRequest”在初始化之前被使用

我做错了什么?我已经尝试在init中使用这些变量而不使用self。还是出现了同样的错误。唯一可行的方法是将startDateOfMonth和endDateOfMonth不作为ObservableObject,而是作为祖先视图中的vars传递给我的视图,并在fetchRequest init中使用它们作为参数。但是我有几个像这样的视图,并且希望使用ObservableObject,而不是一直将相同的参数传递给子视图。


你能展示一下在初始化之前如何在视图中声明 selectedDate 吗? - Asperi
selectedDate 在另一个视图(DateView)中的 class SelectedDate: ObservableObject {...} 中声明。我尝试在 TransactionsListView 中使用它,其中我有 fetchRequest init。我不能在 init 中使用它,但是我可以在 body 中使用它,例如 Text("\(self.selectedDate.startDateOfMonth)"),没有问题。 我已经更新了我的问题,并提供了更多细节。 - mallow
3个回答

1
问题在于您无法在init()中使用@EnvironmentObject。可能的选项是将EnvironmentObject作为参数传递给视图。然后在init内部使用该参数。以下是视图的构造函数,然后您可以使用本地变量selectedDate访问start和endTime。
init(selectedDate : SelectedDate) 
{
   //now you can access selectedDate here and use it in FetchRequest
   fetchRequest = FetchRequest<NPTransaction>(entity: NPTransaction.entity(), sortDescriptors: [NSSortDescriptor(keyPath: \NPTransaction.date, ascending: false)], predicate: NSPredicate(format: "date >= %@ AND date < %@", selectedDate.startDateOfMonth as NSDate, selectedDate.endDateOfMonth as NSDate))
}

在调用该视图时,将EnvironmentObject作为参数传递。

                                    //Declared as Environment Object
TransactionsListView(selectedDate : self.selectedDate)

1
非常感谢。我发现可以像这样传递参数:TransactionsListView(startDateOfMonth: self.selectedDate.startDateOfMonth, endDateOfMonth: self.selectedDate.endDateOfMonth),但是你的版本更好! :) - mallow

0

Swift保证了所有属性在初始化后都已初始化。

当您访问对象属性时,假定对象已经初始化,但是在init的中间访问它并非如此。

发生的情况是,当您初始化fetchRequest时,您访问self.selectedDate(无论您是否添加self都相同),这表明self已经初始化,这意味着作为self属性之一的fetchRequest已准备好使用。 因此,编译器错误:

变量“self.fetchRequest”在初始化之前使用

fetchRequest设置为计算或惰性属性应该可以解决问题。(对@FetchRequest不太熟悉)

希望能对您有所帮助。


谢谢,但是怎么做呢?你的意思是我不应该把fetchrequest放在init里面,而是将其作为var或lazy var?我使用init是因为我在一些教程中发现它可以工作。我仍在学习Swift和SwiftUI,init是我仍然不太理解的东西。 - mallow
是的,在计算属性或懒加载变量的范围内,您可以访问self。Swift有非常严格的两阶段初始化过程。通常在init之后做事情比在init期间更容易。您应该能够在网上找到很多关于计算、惰性、两阶段初始化的示例。 - Jim lai

0

你可以将它作为参数传递给视图:

import SwiftUI

struct TransactionsListView: View {

    @Environment(\.managedObjectContext) var managedObjectContext
    @FetchRequest var transactions: FetchedResults<NPTransaction>

    init(selectedDate: SelectedDate) {
        _transactions = FetchRequest(sortDescriptors: [NSSortDescriptor(keyPath: \NPTransaction.date, ascending: false)], predicate: NSPredicate(format: "date >= %@ AND date < %@", selectedDate.startDateOfMonth as NSDate, selectedDate.endDateOfMonth as NSDate))
    }

    var body: some View {
    // other code
    }
}

selectedDate作为@State存储在父视图中,并使用当前值重新创建TransactionsListView


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接