iOS Development

ios – SwiftData Predicate altering at person enter?

Spread the love


I have been preventing with SwiftData for the previous few days with out reaching an understanding. I’ve to say I am only a newbie so I could have made errors some other place too, however nonetheless I do not perceive.

So, what I am making an attempt to do is to have an inventory of Phrases (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 essential.

import Basis
import SwiftData

@Mannequin
class Class: Codable, Equatable {
    enum CodingKeys: CodingKey {
        case identify, primaryColor, secondaryColor
    }
    
    @Attribute(.distinctive) let identify: String
    let primaryColor: ColorComponents
    let secondaryColor: ColorComponents
    
    init(identify: String, primaryColor: ColorComponents, secondaryColor: ColorComponents) {
        self.identify = identify
        self.primaryColor = primaryColor
        self.secondaryColor = secondaryColor
    }
    
    required init(from decoder: Decoder) throws {
        let container = attempt decoder.container(keyedBy: CodingKeys.self)
        self.identify = attempt container.decode(String.self, forKey: .identify)
        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.identify, forKey: .identify)
        attempt container.encode(self.primaryColor, forKey: .primaryColor)
        attempt container.encode(self.secondaryColor, forKey: .secondaryColor)
    }
    
    static func ==(lhs: Class, rhs: Class) -> Bool {
        lhs.identify == rhs.identify
    }
    
    static let instance = Class(identify: "Basic", primaryColor: ColorComponents(coloration: .mint), secondaryColor: ColorComponents(coloration: .blue))
}

Phrase mannequin

Then, the Phrase mannequin. Now, this comprises a static methodology to return a predicate. Apple’s pattern code suggests this and is probably the one strategy 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?.identify ?? "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 primary 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. In line with Apple, provided that the class is handed to the initializer itself, this manner of doing issues ensures that the question is up to date at each class change (that ideally I might like for the person to have the ability to choose at any time).

import SwiftData
import SwiftUI

struct WordsCardsListView: View {
    let class: Class?
    @Question non-public 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 {
        Listing {
            // different views
            
            ForEach(phrases) { phrase in
                WordCardView(phrase: phrase)
                    .listRowSeparator(.hidden)
            }
        }
        .listStyle(.plain)
    }
}

The errors I get

I did attempt each mixture doable, I consider, however I at all times get a SwiftData.SwiftDataError._Error.unsupportedPredicate error at runtime (or generally the predicate will not even compile). From what I can collect the predicate doesn’t help evaluating objects (maybe, it fails each time I attempt to examine a Class or perhaps a Phrase) and it additionally fails when making an attempt to entry phrase.class?.identify, both with optionally available chaining or drive unwrapping (provided that the class’s identify is exclusive I’d have been happy with that too). I do know that predicates are considerably restricted in what they’ll settle for as expressions, however I do not perceive why Apple implementation works and mine doesn’t, since I consider there will not be vital variations.

I do know that the best answer can be to simply question for all phrases after which filter them afterwards (and it is most likely what I’ll find yourself doing), but it surely puzzles me that such a easy concept (a filter that updates dwell) just isn’t really easy to acquire with SwiftData.

Anyway, I thank anybody that learn up up to now and that may take the time to reply.

Leave a Reply

Your email address will not be published. Required fields are marked *