1

如何将 @AppStorage 变量添加到 init() 以将其用作 @FetchRequest 谓词中的键?

这是一个工作示例,我使用带有固定关键字“A”的谓词过滤结果。

内容视图.swift

import SwiftUI
import CoreData

struct Settings {
    static let filter = "filter"
}

struct ContentView: View {
    @AppStorage(wrappedValue: "B", Settings.filter) var filter
    
    @Environment(\.managedObjectContext) private var viewContext
    
    @FetchRequest private var items: FetchedResults<Item>
    init() {
        let request: NSFetchRequest<Item> = Item.fetchRequest()
        
        // this works
        request.predicate = NSPredicate(format: "name == %@", "A")
        
        // this gives an error "Variable 'self.items' used before being initialized"
        //request.predicate = NSPredicate(format: "name == %@", filter)
        
        request.sortDescriptors = [NSSortDescriptor(keyPath: \Item.name, ascending: true)]
        _items = FetchRequest(fetchRequest: request)
    }
    
    var body: some View {
        NavigationView {
            List {
                ForEach(items) { item in
                    Text(item.name ?? "x")
                }
                .onDelete(perform: deleteItems)
            }
            .toolbar {
                ToolbarItem(placement: .navigationBarTrailing) {
                    EditButton()
                }
                ToolbarItem {
                    Button(action: addItem) {
                        Label("Add Item", systemImage: "plus")
                    }
                }
            }
            Text("Select an item")
            
        }
    }
    
    private func addItem() {
        withAnimation {
            let newItem1 = Item(context: viewContext)
            newItem1.name = "A"
            
            let newItem2 = Item(context: viewContext)
            newItem2.name = "B"
            
            do {
                try viewContext.save()
            } catch {
                let nsError = error as NSError
                fatalError("Unresolved error \(nsError), \(nsError.userInfo)")
            }
        }
    }
    
    private func deleteItems(offsets: IndexSet) {
        withAnimation {
            offsets.map { items[$0] }.forEach(viewContext.delete)
            
            do {
                try viewContext.save()
            } catch {
                let nsError = error as NSError
                fatalError("Unresolved error \(nsError), \(nsError.userInfo)")
            }
        }
    }
}

struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        ContentView().environment(\.managedObjectContext, PersistenceController.preview.container.viewContext)
    }
}

持久性.swift

import CoreData

struct PersistenceController {
    static let shared = PersistenceController()

    static var preview: PersistenceController = {
        let result = PersistenceController(inMemory: true)
        let viewContext = result.container.viewContext
        let newItem1 = Item(context: viewContext)
        newItem1.name = "A"
        let newItem2 = Item(context: viewContext)
        newItem2.name = "B"
        do {
            try viewContext.save()
        } catch {
            let nsError = error as NSError
            fatalError("Unresolved error \(nsError), \(nsError.userInfo)")
        }
        return result
    }()

    let container: NSPersistentContainer

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

我有一个实体:项目。使用“名称” - 字符串类型属性。当谓词是固定的“A”关键字时,一切正常。但是当我尝试使用过滤器变量(来自@AppStorage)时,我得到了错误:

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

当我使用这个时:

request.predicate = NSPredicate(format: "name == %@", filter)

而不是这个:

request.predicate = NSPredicate(format: "name == %@", "A")
4

1 回答 1

1

将所有内容移至分离视图并注入filter正文。这是一个架构:

struct ContentView: View {
    @AppStorage(wrappedValue: "B", Settings.filter) var filter

    // Variant to use UserDefaults in init
    init() {
        let storedFilter: String = UserDefaults.standard.string(forKey: Settings.filter) ?? "B"
           // do something here
    }

    var body: some View {
       // Move everything into `FilteredContentView` and then `filter`
       // will be available in `FilteredContentView.init` where you
       // inject it into `predicate`
       FilteredContentView(filter: filter)   // << here !!
    }
}
于 2021-12-26T11:47:00.480 回答