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

Syb*_*Syn 9 core-data swift swiftui

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

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

我在假设显示条目的位置的视图上将条目作为 @binding var。理想情况下,我想直接从这个入口变量访问位置,但由于位置是一个集合,我收到以下错误:

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

@Binding private var entry: Entry?

ForEach(entry?.positions) { position in
    Text("position here")
}
Run Code Online (Sandbox Code Playgroud)

解决方案一:

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

尽管如果我只能对 @binding 条目 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>
Run Code Online (Sandbox Code Playgroud)

解决方案二:

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

let list = entry.wrappedValue?.positions?.sorted()
Run Code Online (Sandbox Code Playgroud)

核心数据模型:

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?
}
Run Code Online (Sandbox Code Playgroud)

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

Syb*_*Syn 1

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

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

ForEach(Array(entry.positions!)) { position in
   Text("Position")
}
Run Code Online (Sandbox Code Playgroud)

下一个问题是,每次由于集合无序而导致位置集发生变化时,数组都会被随机化。因此,通过使立场符合可比较协议解决了这个问题。我认为按日期对职位进行排序最有意义,因此我更新了模型,如下所示:

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
    }
}
Run Code Online (Sandbox Code Playgroud)

然后 ForEach 可以排序,如下所示:

ForEach(Array(entry.positions!).sorted()) { position in
   Text("\(position.date)")
}
Run Code Online (Sandbox Code Playgroud)

我发现了一些其他解决方案,但由于原始帖子中提到的原因并不理想,但它们确实有效,就是使用在视图初始化内部自定义的获取请求,如下所示:

@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
    )
}
Run Code Online (Sandbox Code Playgroud)

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