Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

iOS Realm Filter objects in a list of a relationship

Tags:

ios

swift

realm

I have three objects nested via lists like this:

class Canteen: Object {

        dynamic var name: String?
        let lines = List<Line>()
}

class Line: Object {

        dynamic var name: String?
        let meals = List<Meal>()
}

class Meal: Object {

        dynamic var name: String?
        dynamic var vegan: Bool = false
}

Getting all canteens with all the lines and meals is no problem. What im doing right now is this:

let predicate = NSPredicate(format: "name == %@", selectedCanteenType.rawValue)
canteens =  realm.objects(Canteen).filter(predicate)

But now i only need the meals which are vegan. So im looking to get the selected canteen with all the lines, but only with meals which are vegan. Is this possible in realm, to filter lists in retrieved objects?

like image 678
Daniel Storch Avatar asked May 12 '16 17:05

Daniel Storch


2 Answers

Realm doesn't have any sort of concept of a deep-filtered view, so you can't have a Results<Canteen> which restricts the Lists contained in related objects to vegan meals.

There are several similar things which you can do. You could add inverse relationship properties, and then query Meal objects instead:

class Canteen: Object {
    dynamic var name: String?
    let lines = List<Line>()
}

class Line: Object {
    dynamic var name: String?
    let meals = List<Meal>()
    let canteens = LinkingObjects(fromType: Canteen.self, property: "lines")
}

class Meal: Object {
    dynamic var name: String?
    dynamic var vegan: Bool = false
    let lines = LinkingObjects(fromType: Line.self, property: "meals")
}

let meals = realm.objects(Meal).filter("vegan = true AND ANY lines.canteens.name = %@", selectedCanteenType.rawValue)

(Or rather, you will be able to once Realm 0.102.1 is out; currently this crashes).

If you just need to iterate over the meals but need to do so from the Canteen down, you could do:

let canteens = realm.objects(Canteen).filter("name = %@ AND ANY lines.meals.vegan = true", selectedCanteenType.rawValue)
for canteen in canteens {
    for line in canteen.lines.filter("ANY meals.vegan = true") {
        for meal in line.meals.filter("vegan = true") {
            // do something with your vegan meal
        }
    }
}

This unfortunately has some duplication due to needing to repeat the filter for each level of the references.

like image 137
Thomas Goyne Avatar answered Oct 19 '22 19:10

Thomas Goyne


Try this:

let predicate = NSPredicate(format: "name == %@", "")
var canteens: [Canteen] = realm.objects(Canteen).filter(predicate).map { can in
    // Iterate through all the Canteens
    let lines: [Line] = can.lines.map { (line: Line) in
        // Iterate through all the lines in each canteene
        // Filter all the Meals that are NOT vegan
        let meals = line.meals.filter { $0.vegan == true }
        line.meals = List<Meal>(meals)
        return line
    }
    can.lines = List<Line>(lines)
    return can
}
like image 1
Dejan Skledar Avatar answered Oct 19 '22 18:10

Dejan Skledar