swift - 如何在 ForEach SwiftUI 中使用核心数据关系

标签 swift core-data swiftui

由于关系是集合,我在 SwiftUI 中显示我的核心数据时遇到了问题。在 SwiftUI ForEach 循环中显示多个关系集的最佳方法是什么?

例如,我在核心日期中有两个实体:Entry & Position。条目可以包含多个位置,每个位置只能属于一个条目。

我在应该显示条目位置的 View 上将条目作为@binding var。理想情况下,我希望直接从此条目变量访问位置,但由于位置是一个集合,我收到以下错误:

错误“ForEach”要求“Set”符合“RandomAccessCollection”

@Binding private var entry: Entry?

ForEach(entry?.positions) { position in
    Text("position here")
}

解决方案一:

或者,我可以对所有职位进行获取请求,然后过滤掉所有不属于实体的职位,我不喜欢这个想法,因为我可能会获取数千个职位而只获得几个职位。 (或者我想错了吗?我是核心数据的新手,因为 swiftui 而来自领域)

虽然如果我只能在@binding entry var上进行提取,并将其所有位置作为排序的提取结果提取,这可能会起作用,但我不确定是否有办法做到这一点。

也许像这样,或者如果可能有数千个条目,每个条目都有 10-20 多个位置,这会是一个性能问题吗? objectID 可以这样使用吗,如果条目被移到另一个期刊,它仍然是唯一的吗?:
@Binding private var entry: Entry?

@FetchRequest(
    entity: Position.entity(),
    sortDescriptors: [],
    predicate: NSPredicate(formate: "entry.objectID == %@", self.entry.objectID)
) var positions: FetchedResults<Position>

解决方案二:

我想为诸如“日期”之类的位置添加一个属性,这样可以比较和排序位置吗?但不确定如何使用 SwiftUI 进行更新,因为它只会在 init() 中完成一次。
let list = entry.wrappedValue?.positions?.sorted()

核心数据模型:
public class Entry: NSManagedObject, Identifiable {

    // MARK: - ATTRIBUTES
    @NSManaged public var date: Date

    // MARK: - RELATIONSHIPS
    @NSManaged public var journal: Journal?
    @NSManaged public var positions: Set<Position>?
}

public class Position: NSManagedObject, Identifiable {

    // MARK: - RELATIONSHIPS
    @NSManaged public var entry: Entry?
}

你将如何解决这个问题?请记住,在列出位置的 View 上,该 View 可以添加、删除和修改位置,因此解决方案应该允许 SwiftUI 在进行更改时重新加载 View 并更新位置。

最佳答案

@JoakimDanielson 评论就像一个魅力,但需要一些调整。

在数组初始值设定项中包装集合的工作方式如下,但需要解开可选集合。我惊讶地发现即使设置为零,强制展开也不会导致崩溃?也许有人可以解释一下?

ForEach(Array(entry.positions!)) { position in
   Text("Position")
}

下一个问题是,每次由于集合无序而导致位置集合发生变化时,数组都会被随机化。因此,通过使 Position 符合 Comparable Protocol 解决了这个问题。我认为按日期对位置进行排序最有意义,所以我更新了模型,如下所示:
public class Position: NSManagedObject, Identifiable {

    // MARK: - ATTRIBUTES
    @NSManaged public var date: Date

    // MARK: - RELATIONSHIPS
    @NSManaged public var entry: Entry?
}

extension Position: Comparable {

    static func < (lhs: Position, rhs: Position) -> Bool {
        lhs.date < rhs.date
    }
}

然后可以对 ForEach 进行排序,如下所示:
ForEach(Array(entry.positions!).sorted()) { position in
   Text("\(position.date)")
}

我发现了一些其他解决方案,但由于原始帖子中提到的原因并不理想,但它们确实有效,是使用在 View 初始化中自定义的获取请求,如下所示:
@FetchRequest var positions: FetchedResults<Position>

init(entry: Entry) {

    var predicate: NSPredicate?

    // Do some kind of control flow for customizing the predicate here.
    predicate = NSPredicate(formate: "entry == %@", entry)

    self._positions = FetchRequest(
        entity: Position.entity(),
        sortDescriptors: [],
        predicate: predicate
    )
}

或者创建一个绑定(bind)到@ObservedObject 的“中间人” View 模型,它将核心数据托管对象转换为可用的 View 数据。对我来说,这最没有意义,因为它基本上包含一堆已经在核心数据托管对象中找到的冗余信息,而且我喜欢核心数据托管对象也是单一事实来源的想法。

关于swift - 如何在 ForEach SwiftUI 中使用核心数据关系,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/58739719/

相关文章:

swift - 缓存的图像闪烁

swift - Apple 传输安全

arrays - 如何转发带有可变参数的函数?

iphone - 存储关系时核心数据_NSFaultingMutableSet 中的存储问题

ios - widgetURL 在 Foreach 内被覆盖?

ios - Swift 中的泛型和 EXC_BAD_ACCESS

ios - 在哪里放置数据数组

objective-c - 重命名 NSManagedObject 类中的核心数据属性

swift - 在 iOS16.4 中 TextField 内的电子邮件提示呈现蓝色

ios - SF-Symbols 位于哪里?