【问题标题】:SwiftUI MVVM @Environment BreaksSwiftUI MVVM @Environment Breaks
【发布时间】:2020-04-21 15:28:08
【问题描述】:

我正在尝试使用 MVVM 做一个相当简单的 View 以成为一个优秀的 ViewModel 公民。然而, 访问 ViewModel 中的 @Enviromnent 核心数据时代码中断。我创建 ViewModel 中的两个函数。一个通过@Environment 和一个访问核心数据 使用旧样式访问核心数据 - 获取对 AppDelegate 的引用并自己做 事物。 OldSchool 方法有效。下面评论2。 @Environment 不会 - 它会破坏 在下面指示的行中出现对我没有帮助的错误。评论 1。 (线程 1:EXC_BAD_INSTRUCTION(代码=EXC_I386_INVOP,子代码=0x0))

现在,如果我采用相同的 @Environment 代码并将其直接放入视图中,它就可以工作。 如果我在视图中的文本中调用破坏 MVVM 的同一行,我会得到 正确的反应。评论2

这是视图:

struct UserUtilities: View {

@Environment(\.managedObjectContext) var managedObjectContext
@FetchRequest(fetchRequest: DermPhoto.getAllDermPhotos()) var dermPhotos: FetchedResults<DermPhoto>

@State private var reminderInterval = 1
@State private var enableReminders = true
@State private var daysSincePhoto: Int = 0

@ObservedObject var userUtilitiesVM = UserUtilitiesViewModel()

var body: some View {
    NavigationView {
        VStack {
            Group { //group 1
            Toggle(isOn: $enableReminders) {
                Text("Enable Reminders")
            }
            .padding(EdgeInsets(top: 50, leading: 50, bottom: 0, trailing: 50))
            Text("Reminders are" + (enableReminders == true ? " On" : " Off"))
            Spacer()

            //Comment 3 - this always works   
            Text("String interpolation of self.dermPhotos.count")
                Text("\(self.dermPhotos.count)")
            }  //group 1

            Group { //group 2
            Text("It has been " + "\(self.daysSincePhoto) " + (daysSincePhoto == 1 ? "day" : "days") + " since a photo was added.")
            .padding(EdgeInsets(top: 0, leading: 50, bottom: 0, trailing: 50))
                //options for the sentence above
                //\(self.userUtilitiesVM.getTheDateInterval())
                //\(self.userUtilitiesVM.getFromEnvironment())
                //\(self.userUtilitiesVM.dateInterval)
            Spacer()
            Stepper("Reminder Interval", value: $reminderInterval, in: 1 ... 30)
            .padding(EdgeInsets(top: 0, leading: 50, bottom: 0, trailing: 50))
            Text("Reminder Interval is: \(reminderInterval)" + (reminderInterval == 1 ? " day" : " days"))
            Spacer()

        }//group 2
        }
        .navigationBarTitle("Reminder Days", displayMode: .inline)
        .navigationBarItems(trailing: Button(action: {
            print("getting the date interval from the nav button")
            //self.daysSincePhoto = self.userUtilitiesVM.getOldSchool()
            self.daysSincePhoto = self.userUtilitiesVM.getFromEnvironment()
            //self.daysSincePhoto = self.getFromEnvironment()
        } , label: { Text("Fetch")
        }))
    }
}

//this always works
func getFromEnvironment() -> Int {

    let numberOfRecords = self.dermPhotos.count

    if numberOfRecords > 0 {
        let now = Date()
        let lastDate = self.dermPhotos.last?.addDate
        //ok to bang - addDate is always added to core data
        let dateInterval = DateInterval(start: lastDate!, end: now)
        let days = Int(dateInterval.duration) / (24 * 3600)
        self.daysSincePhoto = days
        return days
    }
    return 0
}
}

这是 ViewModel:

class UserUtilitiesViewModel: ObservableObject {
    @Environment(\.managedObjectContext) var managedObjectContext
    @FetchRequest(fetchRequest: DermPhoto.getAllDermPhotos()) var dermPhotos: FetchedResults<DermPhoto>
    @Published var dateInterval: Int = 50

    //Comment 2 this always works
    func getOldSchool() -> Int { 
        let kAppDelegate = UIApplication.shared.delegate as! AppDelegate
        let context = kAppDelegate.persistentContainer.viewContext
        var resultsDermPhotos : [DermPhoto] = []

        let fetchRequest: NSFetchRequest<DermPhoto> = DermPhoto.fetchRequest() as! NSFetchRequest<DermPhoto>
        let sortDescriptor = NSSortDescriptor(key: "addDate", ascending: true)
        fetchRequest.sortDescriptors = [sortDescriptor]

        do {
            resultsDermPhotos = try context.fetch(fetchRequest)
        } catch {
            print("the fetchRequest error is \(error.localizedDescription)")
        }

        let numberOfRecords = resultsDermPhotos.count
        if numberOfRecords > 0 {
            let now = Date()
            let lastDate = resultsDermPhotos.last?.addDate
            //ok to bang this since addDate is always added to core data
            let di = DateInterval(start: lastDate!, end: now)
            let days = Int(di.duration) / (24 * 3600)
            dateInterval = days
            return days
        }
        return 0
    }

    //this never works
    func getFromEnvironment() -> Int {

        //Comment 1 - this breaks
        let numberOfRecords = self.dermPhotos.count

        if numberOfRecords > 0 {
            let now = Date()
            let lastDate = self.dermPhotos.last?.addDate
            //ok to bang this since addDate is always added to core data
            let di = DateInterval(start: lastDate!, end: now)
            let days = Int(di.duration) / (24 * 3600)
            dateInterval = days

            return days
        }

        return 0
    }
}

显然我可以使用老派或放弃 ViewModel 的想法,但我想知道如何解决这个问题。任何指导将不胜感激。 Xcode 11.3 (11C29)

【问题讨论】:

    标签: xcode core-data swiftui combine


    【解决方案1】:

    我认为您不能从 View 层次结构外部访问 @Environment。

    您的视图模型不符合视图。但是一旦符合它就不是视图模型了。

    => 你应该放弃视图模型的想法

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-08-08
      • 2022-07-06
      • 1970-01-01
      • 2021-10-11
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多