SwiftUI:FetchedResults的ForEach给出错误-“类型'NSManagedObject'的值没有成员...”

时间:2019-11-27 00:17:03

标签: swift swiftui

我是SwiftUI的新手,部分是Swift的新手,所以希望有人可以对我公然缺少的东西有所了解。麻烦代码了!我还没有机会重构。

我正在使用@FetchRequest属性包装器,从Memories模型中拉回所有CoreData的列表,并在其上定义了NSFetchRequest<Memory>方法,从NSManagedObject

当我通过FetchRequest<Memory>遍历SwiftUI中ForEach的结果时,它将NSManagedObject的一个实例返回到循环变量中,而不是Memory,并且因此,对于以下SwiftUI类,显示错误:Value of type 'NSManagedObject' has no member 'createdAt'

import SwiftUI

struct MemoryView: View {

    @Environment(
        \.managedObjectContext
    ) var managedObjectContext

    @FetchRequest(
        fetchRequest: Memory.allMemories()
    ) var allMemories: FetchedResults<Memory>

    @State private var newMemory = ""

    var body: some View {
        NavigationView {
            List {
                Section(header: Text("What's on your mind?")) {
                    HStack {
                        TextField("New memory", text: self.$newMemory)

                        Button(action: {
                            let memory = Memory(context: self.managedObjectContext)

                            memory.title = self.newMemory
                            memory.createdAt = Date()

                            do {
                                try self.managedObjectContext.save()
                            } catch {
                                print(error)
                            }

                            self.newMemory = ""
                        }) {
                            Image(systemName: "plus.circle.fill")
                                .foregroundColor(.green)
                                .imageScale(.large)
                        }
                    }
                }
                .font(.headline)

                Section(header: Text("My Memories")) {
                    ForEach(self.allMemories) { memoryItem in
                        MemoryRow(title: memoryItem.title, createdAt: memoryItem.createdAt)
                        //               /\ /\ /\ /\ /\ /\            /\ /\ /\ /\ /\ /\ /\
                        // >>> Error:
                        // >>> Value of type 'NSManagedObject' has no member 'createdAt'
                    }
                }
            }
            .navigationBarTitle(Text("Memories"))
            .navigationBarItems(trailing: EditButton())
        }
    }
}

struct MemoryView_Previews: PreviewProvider {
    static var previews: some View {
        MemoryView()
    }
}

作为参考,有问题的Memory模型如下:

import Foundation
import CoreData
import CoreLocation

// Define the CoreData Object Model.
// Ensuring that this class name matches
// the entity model name.
public class Memory: NSManagedObject, Identifiable {
    @NSManaged public var title: String?
    @NSManaged public var body: String?

    @NSManaged public var latitude: Double
    @NSManaged public var longitude: Double

    @NSManaged public var createdAt: Date?
    @NSManaged public var updatedAt: Date?

//    @NSManaged public var type: Type?
//    @NSManaged public var tags: [Tags]?
}

// Create an extension in which to place things out of the context
// of the initial class declaration within.
extension Memory {
    // Define a helper method for accessing a generated
    // CLLocationCoordinate2D instance from the objects
    // latitude and longitude values.
    var locationCoordinate: CLLocationCoordinate2D {
        CLLocationCoordinate2D(
            latitude: latitude,
            longitude: longitude
        )
    }

    // Define a helper method that grabs all instances of a Memory
    // sorted by their created date in ascending order.
    static func allMemories() -> NSFetchRequest<Memory> {
        let request: NSFetchRequest<Memory> = Memory.fetchRequest() as! NSFetchRequest<Memory>

        let sortDescriptor = NSSortDescriptor(key: "createdAt", ascending: true)

        request.sortDescriptors = [sortDescriptor]

        return request
    }
}

任何帮助我的想法都将不胜感激。我认为,明天的问题会崭露头角!

编辑:这是MemoryRow类:

import SwiftUI

struct MemoryRow: View {
    var title: String = ""
    var createdAt: String = ""

    var body: some View {
        HStack {
            VStack(alignment: .leading) {
                Text(title)
                    .font(.headline)

                Text(createdAt)
                    .font(.caption)
            }
        }
    }
}

struct MemoryRow_Previews: PreviewProvider {
    static var previews: some View {
        MemoryRow(title: "Test Title", createdAt: "Test Date")
    }
}

9 个答案:

答案 0 :(得分:12)

在我的情况下,这是因为optional数据类型是在我们向数据模型中添加属性时默认选择的,因此在NSManagedObjectClass中看起来像这样

@NSManaged public var name: String?

当然,要解决此问题,可以在上一行中删除?或在使用??之类的属性时使用ClassName.name ?? "Unknown" nil合并运算符

从给出的答案和评论中,我去检查了类中的属性名称是否有错字,然后我意识到了这个问题。现在必须处理SwiftUI的这种行为,即它不会显示实际错误或在任何其他行上显示。?

希望这可以帮助像我这样的人。

答案 1 :(得分:2)

在闭包参数中设置NSManagedObject类型。

ForEach(books, id: \.self) { book in 
     //
}

ForEach(books, id: \.self) { (book: Book) in
     //
}

答案 2 :(得分:2)

我有类似的问题,

var D: [ACoreDataEntity]

然后

filtered = D.filter{$0.name == 'john'}

名称正确是ACoreDataEntity

的字段

但是它不会解析或编译。

添加括号将其固定(!)

filtered = L.filter{($0).name == 'john'}

去吧。

答案 3 :(得分:1)

关于什么为我解决了这个问题,我实际上有点困惑。这是我之前做过的一些事情,“行之有效”

我不知道是哪个原因引起的,我只是重新打开了Xcode,以前失败的构建现在又在意外地构建。

因此,为了快速思考如何解决它,并使用Gi​​t diff作为某种参考形式,这是我按照以下顺序重新跟踪自己的步骤:

  1. 检查了SceneDelegate上下文所面向的contentViewView中的视图附件。这对我来说是错误的NSManagedObjectContext,它附加在View而不是MemoryDetail()上。

  2. 删除了违规行:

MemoryList()

并仅使用填充符MemoryRow(title: memoryItem.title!, createdAt: memoryItem.description) 替换它,即可成功保存并构建项目。

  1. 从以下位置更改Text("test")声明:
sortDescriptor

let sortDescriptor = NSSortDescriptor(key: "createdAt", ascending: true)

request.sortDescriptors = [sortDescriptor]
  1. 重新启动Xcode(尽管这样做多次,无论如何都无济于事)。

  2. 暂存而不是提交我跟踪的代码更改。

  3. 删除填充符request.sortDescriptors = [ NSSortDescriptor(key: "createdAt", ascending: true) ] 并手动键入该行:

Text("test")

其中的代码提示仍然不起作用,但是该行现在可以正常运行,因此您必须手动进行操作。不,展开力MemoryRow(title: memoryItem.title!, createdAt: memoryItem.description) 并不是错误的原因。


编辑:

使用Git差异并粘贴我的原始代码后,我想我知道错误是什么

!

这没有帮助,因为编译器给出的错误与实际问题没有直接关系。错误应该是MemoryRow(title: memoryItem.title!, createdAt: memoryItem.createdAt) // ^^^^^^^^^ // This is wrong ----------------------------------------////////// 未返回memoryItem.createdAt期望的正确类型。

MemoryRow

我认为问题有时出在Xcode上,有些偶然危险。

此后,我对MemoryRow(title: memoryItem.title!, createdAt: "") // Compiles fine // And now the corrected version: MemoryRow(title: memoryItem.title!, createdAt: "\(memoryItem.createdAt!)") 进行了修改,现在接受MemoryRow类型,而不是Date?,以更好地描述对象正在接受什么。以及解包,格式化和显示该类中的变量。

答案 4 :(得分:0)

尝试此构造以获取请求

@FetchRequest(
    entity: Memory.entity(),
    sortDescriptors: [
        NSSortDescriptor(keyPath: \Memory.createdAt, ascending: true)
    ]
) var allMemories: FetchedResults<Memory>

答案 5 :(得分:0)

我认为处理此问题的正确方法是这样做

更改

MemoryRow(title: memoryItem.title, createdAt: memoryItem.createdAt)

MemoryRow(title: (memoryItem.title ?? ""), createdAt: (memoryItem.createdAt ?? ""))

答案 6 :(得分:0)

您必须指定要迭代的数组元素的类型(memoryItem:内存),例如:`。

中的.ForEach(self.allMemories){(memoryItem:Memory)

在此处指定内存类型:

`
ForEach(self.allMemories) { (memoryItem: Memory) in
 MemoryRow(title: memoryItem.title, createdAt: memoryItem.createdAt)
     }
`

这是因为它可以是任何类型的NSManagedObject,但是为了使Swift确切地知道它是什么类型的托管对象,我们需要明确。结帐:https://www.hackingwithswift.com/books/ios-swiftui/dynamically-filtering-fetchrequest-with-swiftui

答案 7 :(得分:0)

我遇到了类似的错误。

就我而言,我有一个 CoreData 实体“Category”,它与“Contact”实体具有一对多关系。联系人具有 firstNamelastname 可选字符串属性。

我的确切错误是:

Value of type 'NSObject' has no member 'lastName' 在我的 SwiftUI 文本视图中尝试访问 contact.lastName 时。

解决方案

解决方案似乎比我一开始假设的要简单,因为当这一切都不必要时,我对 set vs Array 进行了过多的读取:

(注意:此代码位于 if category.contacts?.count != 0 { } 内,因为我只想在有要显示的内容时显示列表。)

if category.contacts?.count != 0 {
    List {
        Section (header: Text("Contacts")) {
            ForEach(contacts) { contact in
                HStack (spacing: 4) {
                    Image(systemName: "person.crop.circle.fill")
                    Text("\(contact.firstName ?? "")")
                    Text("\(contact.lastName ?? "")")
                    Spacer()
                }
            }
        }
    }
    .listStyle(InsetGroupedListStyle())
}

这个结构的开头像这样初始化contacts

struct CategoryForm: View {
    
    @Environment(\.managedObjectContext) private var moc
    
    @ObservedObject var category: Category
    
    @State private var isEditing = false
    
    @State private var errorAlertIsPresented = false
    @State private var errorAlertTitle = ""
    
    @FetchRequest var contacts: FetchedResults<Contact>
    
    init(category: Category) {
        self.category = category
        var predicate: NSPredicate?
        predicate = NSPredicate(format: "category = %@", category)
        self._contacts = FetchRequest<Contact>(
            sortDescriptors: [NSSortDescriptor( keyPath: \Contact.lastName, ascending: true),
                              NSSortDescriptor( keyPath: \Contact.firstName, ascending: true)],
            predicate: predicate
        )
        
    }
    
    var body: some View {
        
///etc...

我不正确但合乎逻辑的初学者代码

对我来说,这个错误来自于将 NSSet 更改为数组的太多尝试。但 FetchRequest 是另一回事。所以,对我来说更简单更好。

///This line of code generated the error!
///The Text("\(contact.firstName ?? "")") was not actually the problem
///although that line is where Xcode showed the error

ForEach(Array(category.contacts! as Set), id: \.self) { contact in

...

答案 8 :(得分:0)

我通过在 Xcode 数据模型中将 CodegenClass Definition 更改为 Category/Extension 来修复这种类型的错误。

enter image description here