简短的回答,Environment 需要@,它是一个包装器。你试图做的不是Environment的记录使用
https://developer.apple.com/documentation/swiftui/environment
长答案,
您还没有提供最小可复制产品,但这是我看到的
let viewContextValue = Environment(\.managedObjectContext).wrappedValue
它不起作用,因为您知道 @Environment 此时不可用,或者您只需使用 viewContext。
let controller = ContentViewController(managedObjectContext: viewContextValue)
我知道你在这里尝试做什么,但如上所述 @Environment 只是在 init 期间不可用
self._controller = StateObject(wrappedValue: controller)
虽然它在表面上“有效”,但它有点违背StateObject的优点
SwiftUI 可能随时创建或重新创建视图,因此这很重要
用给定的输入集初始化视图总是会导致
相同的看法。结果,创建观察对象是不安全的
在一个视图里面。相反,SwiftUI 为
这个目的。您可以在此视图中安全地创建 Book 实例
大大地:
@StateObject var book = Book()
https://developer.apple.com/documentation/swiftui/managing-model-data-in-your-app
根据我的经验,SwiftUI 中的自定义 inits 不能提供可靠的体验。我尽量远离他们。如果您必须在 init 上进行自定义工作,请在 class 作为 ViewModel/ViewController 进行自定义工作,这也是 ObservableObject View 不应该做任何工作。
如果您想要替代您想要执行的操作,请参阅this SO question
你只需要
let persistenceController = PersistenceController.shared
在您的ContentViewController 中并像这样初始化您的StateObject。
@StateObject private var controller: ContentViewController = ContentViewController()
这是一个示例,我使用了 FetchedResultsController 它有部分
import SwiftUI
import CoreData
class TaskListViewModel: ObservableObject {
let persistenceController = PersistenceController.previewAware()
@Published var fetchedResultsController: NSFetchedResultsController<Task>?
init() {
setupController()
}
func setupController() {
do{
fetchedResultsController = try retrieveFetchedController(sortDescriptors: nil, predicate: nil, sectionNameKeyPath: #keyPath(Task.isComplete))
}catch{
print(error)
}
}
func deleteObject(object: Task) {
persistenceController.container.viewContext.delete(object)
save()
}
func save() {
do {
if persistenceController.container.viewContext.hasChanges{
try persistenceController.container.viewContext.save()
objectWillChange.send()
}else{
}
} catch {
print(error)
}
}
}
//MARK: FetchedResultsController setup
extension TaskListViewModel{
func retrieveFetchedController(sortDescriptors: [NSSortDescriptor]?, predicate: NSPredicate?, sectionNameKeyPath: String) throws -> NSFetchedResultsController<Task> {
return try initFetchedResultsController(sortDescriptors: sortDescriptors, predicate: predicate, sectionNameKeyPath: sectionNameKeyPath)
}
private func initFetchedResultsController(sortDescriptors: [NSSortDescriptor]?, predicate: NSPredicate?, sectionNameKeyPath: String) throws -> NSFetchedResultsController<Task> {
fetchedResultsController = getFetchedResultsController(sortDescriptors: sortDescriptors, predicate: predicate, sectionNameKeyPath: sectionNameKeyPath)
//fetchedResultsController!.delegate = self
do {
try fetchedResultsController!.performFetch()
return fetchedResultsController!
} catch {
print( error)
throw error
}
}
func getFetchedResultsController(sortDescriptors: [NSSortDescriptor]?, predicate: NSPredicate?, sectionNameKeyPath: String) -> NSFetchedResultsController<Task> {
return NSFetchedResultsController(fetchRequest: getEntityFetchRequest(sortDescriptors: sortDescriptors, predicate: predicate), managedObjectContext: persistenceController.container.viewContext, sectionNameKeyPath: sectionNameKeyPath, cacheName: nil)
}
private func getEntityFetchRequest(sortDescriptors: [NSSortDescriptor]?, predicate: NSPredicate?) -> NSFetchRequest<Task>
{
let fetchRequest: NSFetchRequest<Task> = Task.fetchRequest()
fetchRequest.includesPendingChanges = false
fetchRequest.fetchBatchSize = 20
if sortDescriptors != nil{
fetchRequest.sortDescriptors = sortDescriptors
}else{
fetchRequest.sortDescriptors = [NSSortDescriptor(key: #keyPath(Task.dateAdded), ascending: false)]
}
if predicate != nil{
fetchRequest.predicate = predicate
}
return fetchRequest
}
}
struct TaskListView: View {
@StateObject var vm: TaskListViewModel = TaskListViewModel()
@State var taskToEdit: Task?
var body: some View {
if vm.fetchedResultsController?.sections != nil{
List{
ForEach(0..<vm.fetchedResultsController!.sections!.count){idx in
let section = vm.fetchedResultsController!.sections![idx]
TaskListSectionView(objects: section.objects as? [Task] ?? [], taskToEdit: $taskToEdit, sectionName: section.name).environmentObject(vm)
}
}.sheet(item: $taskToEdit, onDismiss: {
vm.save()
}){editingTask in
TaskEditView(task: editingTask)
}
}else{
Image(systemName: "empty")
}
}
}
struct TaskEditView: View {
@ObservedObject var task: Task
var body: some View {
TextField("name", text: $task.name.bound)
}
}
struct TaskListSectionView: View {
@EnvironmentObject var vm: TaskListViewModel
let objects: [Task]
@State var deleteAlert: Alert = Alert(title: Text("test"))
@State var presentAlert: Bool = false
@Binding var taskToEdit: Task?
@State var isExpanded: Bool = true
var sectionName: String
var body: some View {
Section(header: Text(sectionName) , content: {
ForEach(objects, id: \.self){obj in
let task = obj as Task
Button(action: {
taskToEdit = task
}, label: {
Text(task.name ?? "no name")
})
.listRowBackground(Color(UIColor.systemBackground))
}.onDelete(perform: deleteItems)
})
}
private func deleteItems(offsets: IndexSet) {
withAnimation {
deleteAlert = Alert(title: Text("Sure you want to delete?"), primaryButton: Alert.Button.destructive(Text("yes"), action: {
let objs = offsets.map { objects[$0] }
for obj in objs{
vm.deleteObject(object: obj)
}
//Because the objects in the sections aren't being directly observed
vm.objectWillChange.send()
}), secondaryButton: Alert.Button.cancel())
self.presentAlert = true
}
}
}
struct TaskListView_Previews: PreviewProvider {
static var previews: some View {
TaskListView()
}
}
previewAware() 只是一个方法,它决定是通过内置的preview 还是shared
static func previewAware() -> PersistenceController{
if ProcessInfo.processInfo.environment["XCODE_RUNNING_FOR_PREVIEWS"] == "1" {
return PersistenceController.preview
}else{
return PersistenceController.shared
}
}