I have been combating with SwiftData for the previous few days with out reaching an understanding. I’ve to say I am only a newbie so I’ll have made errors some place else too, however nonetheless I do not perceive.
So, what I am attempting to do is to have an inventory of Phrase
s (a category of mine), that are saved in SwiftData, filtered relying on the class the person chooses. It seems SwiftData has different concepts although.
For organizing the code I took inspiration from Apple’s pattern code.
Class
mannequin
Let’s begin with the Class
mannequin (which represents the class a phrase could belong to). ColorComponents
is a quite simple Codable
struct I wrote to retailer a coloration, not necessary.
import Basis
import SwiftData
@Mannequin
class Class: Codable, Equatable {
enum CodingKeys: CodingKey {
case title, primaryColor, secondaryColor
}
@Attribute(.distinctive) let title: String
let primaryColor: ColorComponents
let secondaryColor: ColorComponents
init(title: String, primaryColor: ColorComponents, secondaryColor: ColorComponents) {
self.title = title
self.primaryColor = primaryColor
self.secondaryColor = secondaryColor
}
required init(from decoder: Decoder) throws {
let container = attempt decoder.container(keyedBy: CodingKeys.self)
self.title = attempt container.decode(String.self, forKey: .title)
self.primaryColor = attempt container.decode(ColorComponents.self, forKey: .primaryColor)
self.secondaryColor = attempt container.decode(ColorComponents.self, forKey: .secondaryColor)
}
func encode(to encoder: Encoder) throws {
var container = encoder.container(keyedBy: CodingKeys.self)
attempt container.encode(self.title, forKey: .title)
attempt container.encode(self.primaryColor, forKey: .primaryColor)
attempt container.encode(self.secondaryColor, forKey: .secondaryColor)
}
static func ==(lhs: Class, rhs: Class) -> Bool {
lhs.title == rhs.title
}
static let instance = Class(title: "Normal", primaryColor: ColorComponents(coloration: .mint), secondaryColor: ColorComponents(coloration: .blue))
}
Phrase
mannequin
Then, the Phrase
mannequin. Now, this incorporates a static methodology to return a predicate. Apple’s pattern code suggests this and is maybe the one approach to have a predicate altering along with its enter knowledge.
import Basis
import SwiftData
@Mannequin
class Phrase {
let time period: String
let learntOn: Date
var notes: String
@Relationship var class: Class?
var categoryName: String {
class?.title ?? "No class"
}
init(time period: String, learntOn: Date, notes: String = "", class: Class? = nil) {
self.time period = time period
self.learntOn = learntOn
self.notes = notes
self.class = class
}
static func predicate(class: Class?) -> Predicate<Phrase> {
return #Predicate<Phrase>
}
static let instance = Phrase(time period: "Swift", learntOn: .now, notes: "A swift testing phrase.")
}
These are the 2 fashions I’ve. In the principle view I create the mannequin container utilizing .modelContainer(for: Phrase.self)
.
SwiftUI View
I then have the view the place the question is being made. Based on Apple, on condition that the class is handed to the initializer itself, this fashion of doing issues ensures that the question is up to date at each class change (that ideally I would like for the person to have the ability to choose at any time).
import SwiftData
import SwiftUI
struct WordsCardsListView: View {
let class: Class?
@Question personal var phrases: [Word]
init(class: Class? = .instance) {
self.class = class
let predicate = Phrase.predicate(class: class!) // drive unwrapping only for testing, after all
let sortDescriptors = [
SortDescriptor(Word.learntOn, order: .reverse)
]
_words = Question(filter: predicate, type: sortDescriptors)
}
var physique: some View {
Checklist {
// different views
ForEach(phrases) { phrase in
WordCardView(phrase: phrase)
.listRowSeparator(.hidden)
}
}
.listStyle(.plain)
}
}
The errors I get
I did attempt each mixture attainable, I imagine, however I all the time get a SwiftData.SwiftDataError._Error.unsupportedPredicate
error at runtime (or typically the predicate will not even compile). From what I can collect the predicate doesn’t assist evaluating objects (maybe, it fails each time I attempt to examine a Class
or perhaps a Phrase
) and it additionally fails when attempting to entry phrase.class?.title
, both with non-compulsory chaining or drive unwrapping (on condition that the class’s title is exclusive I might have been pleased with that too). I do know that predicates are considerably restricted in what they will settle for as expressions, however I do not perceive why Apple implementation works and mine doesn’t, since I imagine there aren’t vital variations.
I do know that the best resolution can be to simply question for all phrases after which filter them afterwards (and it is most likely what I’ll find yourself doing), nevertheless it puzzles me that such a easy concept (a filter that updates reside) will not be really easy to acquire with SwiftData.
Anyway, I thank anybody that learn up thus far and that can take the time to reply.