TEAM LICENSES: Save money and learn new skills through a Hacking with Swift+ team license >>

SOLVED: Assistance Needed with Predicate Usage in SwiftData Initializer

Forums > SwiftUI

I encountered some issues while working with predicates within an initializer and bought "SwiftData by Example" hoping to find a solution. Unfortunately, the book did not address my specific problem, so I'm reaching out for guidance.

I'm working with two models in my application: Project and Panel. Here's a brief overview of their structures:

// Project model
import Foundation
import SwiftData

@Model
public class Project {
    var name: String
    @Attribute(.unique) var number: String
    var customer: String
    var details: String
    @Relationship(deleteRule: .cascade) var panels = [Panel]()
    @Attribute(.externalStorage) var photo: Data?
    // Initializer omitted for brevity
}

// Panel model
import Foundation
import SwiftData

@Model
public class Panel {
    @Attribute(.unique) var number: String
    var name: String
    var details: String
    weak var project: Project?
    @Attribute(.externalStorage) var photo: Data?
    // Initializer omitted for brevity
}

The user experience begins with presenting a list of projects. Once a project is selected, the application navigates to a view displaying all panels associated with that project. Here’s the problematic initializer in my Panel view model:

init(searchString: String = "", sortOrder: [SortDescriptor<Panel>] = [], project: Project, path: Binding<NavigationPath>, isEditing: Binding<Bool>) {

        let projectNumber = project.number
        let predicate = #Predicate<Panel> { panel in
            if panel.project?.number == projectNumber {
                if searchString.isEmpty {
                    return true
                } else {
                    return panel.number.localizedStandardContains(searchString)
                    || panel.name.localizedStandardContains(searchString)
                }
            } else {
               return false
            }
        }

        _panels = Query(filter: predicate, sort: sortOrder)
        self.project = project
        _path = path
        _isEditing = isEditing
    }

I'm having trouble when attempting to filter panels by their associated project using a predicate. If I try to add a predicate to check the optional panel.project == project, I encounter an error.

Could you provide any insights or suggestions on what might be going wrong here?

Thank you for your time and help.

   

Fixed the problem:

Firstly the corect predicate was this all along:

let predicate = #Predicate<Panel> { panel in

            if let panelProjectNumber = panel.project?.number {
                if panelProjectNumber != projectNumber {
                    return false
                } else if searchString.isEmpty {
                    return true
                } else {
                    return panel.number.localizedStandardContains(searchString) ||
                    panel.name.localizedStandardContains(searchString)
                }
            } else {
                return false
            }
        }

        _panels = Query(filter: predicate, sort: sortOrder)

The issue was that while adding a new panel I forgot to also add it to the project.panels array.... This solved it:

func addPanel() {
        isEditing = true
        let newPanel = Panel(number: "", name: "", details: "", project: project)
        project.panels.append(newPanel)
        modelContext.insert(newPanel)
        path.append(newPanel)
    }

I only wasted two days on this... 🤦

   

Hacking with Swift is sponsored by String Catalog.

SPONSORED Get accurate app localizations in minutes using AI. Choose your languages & receive translations for 40+ markets!

Localize My App

Sponsor Hacking with Swift and reach the world's largest Swift community!

Reply to this topic…

You need to create an account or log in to reply.

All interactions here are governed by our code of conduct.

 
Unknown user

You are not logged in

Log in or create account
 

Link copied to your pasteboard.