【问题标题】:SwiftUI: ForEach of FetchedResults gives the error - "Value of type 'NSManagedObject' has no member..."SwiftUI:FetchedResults 的 ForEach 给出错误 - “'NSManagedObject' 类型的值没有成员......”
【发布时间】:2019-11-27 00:17:03
【问题描述】:

我是 SwiftUI 的新手,部分是 Swift,所以希望有人能对我公然遗漏的东西有所了解。原谅乱码!我还没有机会重构。

我正在使用@FetchRequest 属性包装器从CoreData 模型中提取所有Memories 的列表,其中定义了NSFetchRequest<Memory> 方法,从NSManagedObject 扩展。

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

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")
    }
}

【问题讨论】:

  • MemoryRow 是什么?
  • 另外,您是否检查过memoryItemMemory 对象,而不是NSManagedObject
  • 它是另一个 SwiftUI 类,我将其拆分为一个组件来呈现行。我已经编辑了我的答案!
  • 我将如何检查或强制对象类型? Xcode 报告memoryItem 在与它交互时是NSManagedObject 的一个实例。

标签: swift swiftui


【解决方案1】:

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

@NSManaged public var name: String?

当然,要解决这个问题,可以从上面的行中删除?,或者在使用ClassName.name ?? "Unknown"等属性时使用?? nil 合并运算符

根据给定的答案和 cmets,我去检查我在课堂上的属性名称是否有任何拼写错误,然后我意识到了这个问题。现在必须处理SwiftUI 的这种行为,它不会显示实际错误或将显示在任何其他行上。?

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

【讨论】:

  • bruv 我遇到了同样的问题!在我的情况下,我在 xcode 中取消了可选复选框,但由于某种原因,生成的类仍然有一个可选的。不确定我是否有什么问题。谢谢!
【解决方案2】:

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

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

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

【讨论】:

  • 这确实有效!太奇怪了。适用于具有类似应用设计的其他项目。
【解决方案3】:

我实际上有点困惑是什么为我解决了这个问题。以下是我在它之前做过的几件事'刚刚工作'

我不知道是哪一个造成的,我刚刚重新打开了 Xcode,之前失败的构建现在又重新构建了出乎意料

所以,我匆忙思考我做了什么来修复它并使用 Git 差异作为某种形式的参考,这是我按以下顺序重新跟踪我的步骤:

  1. contentView 中检查了SceneDelegate 中的视图附件,其中View 暴露了NSManagedObjectContext 上下文。这对我来说是错误的View,它附加到MemoryDetail() 而不是MemoryList()

  2. 删除了违规行:

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

并用填充符 Text("test") 替换它,成功保存并构建项目。

  1. 更改 sortDescriptor 声明:
let sortDescriptor = NSSortDescriptor(key: "createdAt", ascending: true)

request.sortDescriptors = [sortDescriptor]

request.sortDescriptors = [
    NSSortDescriptor(key: "createdAt", ascending: true)
]
  1. 重新启动 Xcode(尽管多次这样做都无济于事)。

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

  3. 删除填充符 Text("test") 并手动输入回行:

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

其中代码提示仍然不起作用,但该行现在构建良好,因此您必须手动执行。不,展开力 ! 不是错误的原因。


编辑:

在使用 Git diffs 并粘贴到我的原始代码之后,我想我知道错误是什么:

MemoryRow(title: memoryItem.title!, createdAt: memoryItem.createdAt)
//                                                        ^^^^^^^^^
// This is wrong ----------------------------------------//////////

这无济于事,因为编译器给出的错误与实际问题没有直接关系。错误应该是memoryItem.createdAt 没有返回MemoryRow 期望的正确类型。

MemoryRow(title: memoryItem.title!, createdAt: "")
// Compiles fine

// And now the corrected version:
MemoryRow(title: memoryItem.title!, createdAt: "\(memoryItem.createdAt!)")

我认为问题在于 Xcode 有时会出现一些偶然的错误。

我已经修改了 MemoryRow 现在接受 Date? 类型而不是 String? 以更好地描述对象正在接受的内容。以及在该类中展开、格式化和呈现变量。

【讨论】:

  • 我会小心强制展开属性。更好的是做类似if let title = memoryItem.title, let createdAt = memoryItem.createdAt { MemoryRow(title, createdAt) }
  • 谢谢!是的,这只是一个原型阶段。无论如何,我正在更改该逻辑并在 SwiftUI 组件类中进行解包,只是为了保持上下文关系。
  • 是的,这很烦人——当你输入错误的字段时,它只会说“错误”!
【解决方案4】:

我也遇到过类似的问题:

var D: [ACoreDataEntity]

然后

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

名称正确地是 ACoreDataEntity 的字段

但它就是不会解析或编译。

添加大括号修复它(!)

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

去看看。

【讨论】:

    【解决方案5】:

    您必须指定要迭代的数组元素的类型(memoryItem: Memory),例如:.ForEach(self.allMemories) { (memoryItem: Memory) in `:

    在此处指定内存类型:

    `
    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

    【讨论】:

      【解决方案6】:

      试试这个构造获取请求

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

      【讨论】:

      • 好,那么回到关于MemoryRow接口的问题。可以展示一下吗?
      • 很抱歉刚刚参加了一个 sprint 会议。用它编辑了我原来的问题,谢谢!
      • 可能原因是类型不匹配,MemoryRow有String,但是data对象有String?和日期?所以尝试对齐类型。
      • 试过了,没有运气。对齐模型并且仍然抛出相同的错误:Value of type 'NSManagedObject' has no member 'title' or 'createdAt'
      • 您对@Asperi 的类型是正确的,虽然不是错误的,但它实际上是问题的根源。修复发送给MemoryRow 或由MemoryRow 接受的类型修复了问题!
      【解决方案7】:

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

      改变

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

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

      【讨论】:

        【解决方案8】:

        我遇到了类似的错误。

        在我的例子中,我有一个 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
        
        ...
        
        

        【讨论】:

          【解决方案9】:

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

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2018-03-14
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多