Reputation: 14060
My end goal is to create a SwiftUI List
with children. I have sections (parent) that contain projects (children), and the sections will expand and collapse their list of projects.
I have this built with an NSOutlineView
using Realm, but now I'm trying to build it with SwiftUI and Core Data. *GULP*
: )
I'm not very experienced with Core Data, but I think I have my data set up right. I have a Section
entity and a Project
entity, and the Section
has a projects
attribute that has a to-many relationship.
So, I presume (incorrectly as shown below) section.projects
is a collection of Project
objects.
For simplicity, I'm just trying to nest to the two inside a List
(I'll worry about the DisclosureGroup
stuff later):
List{
ForEach(sections, id: \.recordName){ section in
Text(section.name)
ForEach(section.projects, id: \.recordName){ project in //<-- ERROR
Text(project.name)
}
}
}
The line marked above has two errors that seems to suggest I don't have an array of objects to work with in section.projects
:
Referencing initializer 'init(_:id:content:)' on 'ForEach' requires that 'NSObject' conform to 'RandomAccessCollection'
Value of optional type 'NSOrderedSet?' must be unwrapped to a value of type 'NSOrderedSet'
If section.projects
is an NSOrderedSet
, why can't I iterate over it?
Upvotes: 1
Views: 1822
Reputation: 14060
I figured this out with the help of this article: https://www.hackingwithswift.com/books/ios-swiftui/one-to-many-relationships-with-core-data-swiftui-and-fetchrequest
I had to change my Core Data entities to have their Codegen setting be Manual/None
(in the .xcdatamodeld
editor in Xcode). I then select my entities and go to Editor > Create NSManagedObject Subclass... which created a bunch of files that reflect my Core Data models.
Then inside the Section+CoreDataProperties.swift file, I added a computed property to give me access to my projects (sorted by name):
public var projectArray: [Project] {
let set = projects as? Set<Project> ?? []
return set.sorted {
$0.wrappedName < $1.wrappedName
}
}
And then, for convenience, inside Project+CoreDataProperties.swift I added this:
public var wrappedName: String{
name ?? "Unknown Name"
}
...which allows my computed property to sort my projects by name (since Core Data treats strings as optionals by default).
As a result, I was able to iterate over my nested data like this:
ForEach(sections, id: \.self) { section in
Text(section.wrappedName)
ForEach(section.projectArray, id: \.self) { project in
Text(project.wrappedName)
}
}
Upvotes: 3