swift 使用 predicate 过滤核心数据,以仅显示今天的条目(过滤器停止工作)

ttcibm8c  于 2023-02-28  发布在  Swift
关注(0)|答案(1)|浏览(99)

预期行为:

  • 用户每天至少输入一个条目。
  • 用户看到"请输入"按钮
  • 除非他们已经有了,否则按钮会显示"保存",如果他们点击它,它会转到历史视图。(按钮切换到导航视觉上表明发生了一些事情)。

我的想法是在日期上应用一个 predicate ,以便按一天的开始进行过滤,并使用if语句交换所看到的内容。
当我应用下面的代码时,它在模拟器和应用程序中运行良好,但 * 几天后 * predicate 停止工作。我在一天开始时打开应用程序,它显示"已保存"。大约2 - 3个晚上后,我不知道是什么坏了。不确定这是否是实现结果的最佳解决方案。
还有,如果我杀死应用程序并重新加载,一切又都好了。
我尝试了一个版本的this答案,但我仍然是一个新的编码,可能没有做正确的事情,因为它根本不工作(显示核心数据中的所有结果)。

import SwiftUI
import CoreData

struct HomeScreenView: View {
    @EnvironmentObject var backgroundSet: Background
    @Environment(\.managedObjectContext) private var viewContext
    @FetchRequest(entity: Entry.entity(),
        sortDescriptors: [NSSortDescriptor(keyPath: \Entry.date, ascending: true)], predicate: nil, animation: .default)

private var entries: FetchedResults<Entry>

        init(){
            var calendar = Calendar.current
            calendar.timeZone = NSTimeZone.local
            let dateFrom = calendar.startOfDay(for: Date()) // eg. 2016-10-10 00:00:00
            let dateTo = calendar.date(byAdding: .day, value: 1, to: dateFrom)
            let predicate = NSPredicate(format : "date <= %@ AND  date >= %@", dateTo! as CVarArg, dateFrom as CVarArg)
            self._entries = FetchRequest(
                entity: Entry.entity(),
                sortDescriptors: [NSSortDescriptor(keyPath: \Entry.date, ascending: false)],
                predicate: predicate)
        }

 @State var presentSheet = false
    @State private var today : Date = Date()

 var body: some View {

        NavigationStack {
            ZStack {

if entries.count == 0 {
                        Button("Please make an entry?") {
                            presentSheet = true
                            //refresh.toggle()
                        }
                        .frame(minWidth: 250, minHeight: 50, alignment: .center).padding()
                        .cornerRadius(40)
                        .padding()
                    } else {
                        NavigationLink(destination: HistoryView()) {
                            VStack {
                                Text("Saved for today.")
                                Text("Click here to view history.")
                            }
                        }
                        .frame(minWidth: 250, minHeight: 50, alignment: .center).padding()
                        .cornerRadius(40)
                        .padding()
                    }

            }
            .sheet(isPresented: $presentSheet) {
                AddEntryView()
                    .presentationDetents([.medium])   
            }

下面是持久性的副本:

import CoreData

struct PersistenceController {
    static let shared = PersistenceController()

    static var preview: PersistenceController = {
        let result = PersistenceController(inMemory: true)
        let viewContext = result.container.viewContext
        for _ in 0..<10 {
            let newEntry = Entry(context: viewContext)
            newEntry.timestamp = Date()
        }
        do {
            try viewContext.save()
        } catch {
            // Replace this implementation with code to handle the error appropriately.
            // fatalError() causes the application to generate a crash log and terminate. You should not use this function in a shipping application, although it may be useful during development.
            let nsError = error as NSError
            fatalError("Unresolved error \(nsError), \(nsError.userInfo)")
        }
        return result
    }()

    let container: NSPersistentContainer

    init(inMemory: Bool = false) {
        container = NSPersistentContainer(name: "Express_Gratitude")
        if inMemory {
            container.persistentStoreDescriptions.first!.url = URL(fileURLWithPath: "/dev/null")
        }
        container.loadPersistentStores(completionHandler: { (storeDescription, error) in
            if let error = error as NSError? {              
                fatalError("Unresolved error \(error), \(error.userInfo)")
            }
        })
        container.viewContext.automaticallyMergesChangesFromParent = true
    }
}

以下是@main中的内容

var body: some Scene {
        WindowGroup {
            ContentView()
                .environment(\.managedObjectContext, persistenceController.container.viewContext)
}
}

从上面的链接,我尝试了一个版本,它没有工作。我可能是实施这完全错误的...开放的教育和建议,以实现的结果。

import SwiftUI
import CoreData

struct HomeScreenView: View {
    @EnvironmentObject var backgroundSet: Background
    @Environment(\.managedObjectContext) private var viewContext
    @FetchRequest(entity: Entry.entity(),
        sortDescriptors: [NSSortDescriptor(keyPath: \Entry.date, ascending: true)], predicate: nil, animation: .default)
    
    private var entries: FetchedResults<Entry>
    
    init(){
        // Get the current calendar with local time zone
        var calendar = Calendar.current
        calendar.timeZone = NSTimeZone.local
        // Get today's beginning & end
        let dateFrom = calendar.startOfDay(for: Date()) // eg. 2016-10-10 00:00:00
        let dateTo = calendar.date(byAdding: .day, value: 1, to: dateFrom)
        // Note: Times are printed in UTC. Depending on where you live it won't print 00:00:00 but it will work with UTC times which can be converted to local time
        // Set predicate as date being today's date
        let fromPredicate = NSPredicate(format: "%@ >= %K", dateFrom as NSDate, #keyPath(Entry.date))
        let toPredicate = NSPredicate(format: "%K < %@", #keyPath(Entry.date), dateTo! as NSDate)
        let datePredicate = NSCompoundPredicate(andPredicateWithSubpredicates: [fromPredicate, toPredicate])
        self._entries = FetchRequest(
            entity: Entry.entity(),
            sortDescriptors: [NSSortDescriptor(keyPath: \Entry.date, ascending: false)],
            predicate: datePredicate)
    }

    @State var presentSheet = false
    @State private var today : Date = Date()
svdrlsy4

svdrlsy41#

我认为你需要在日期变化时更新 predicate ,例如。

var timerForNextMidnight: Timer {
    ....
}

.onReceive(timerForNextMidnight) { timer in
    entries.predicate = ...
}

请注意,您还需要使用此日历,因此,如果用户在应用运行时在“设置”中更改其日历,则会重新创建计时器。

@Environment(\.calendar) var calendar

相关问题