5

I'm building a UI with SwiftUI, and I have an array that I use to build a List element. Now I want to sort that list based on a @Published variable coming from an @EnvironmentObject.

Approach 1

I tried getting the array already sorted, passing in the environment object to the sorting method:

List(getArraySorted(environmentObject)) { item in
    //do stuff with item
}

This will compile, but the list will not update if environmentObject changes. I also tried passing in environmentObject.variableToBaseSortOn but to no avail.

Approach 2

I tried sorting the array inline in Swift UI:

List(array.sorted(by: { (lhs, rhs) -> Bool in
    // do sorting based on self.environmentObject
})) { item in
    // do stuff with item
}

This will compile, but crash:

Fatal error: No ObservableObject of type EnvironmentObjectClass found.
A View.environmentObject(_:) for EnvironmentObjectClass may be missing as an ancestor of this view.

The hint in the crash is incorrect, environmentObject is set and self.environmentObject is set to the correct object.

BlackWolf
  • 5,239
  • 5
  • 33
  • 60

1 Answers1

11

Your best approach is probably to sort the list within ObservableObject each time the data changes using a property observer, then manually informing the ObservableObject publisher of the change (rather than using @Published, so the unsorted array is not briefly published between changes.)

It's not a good idea to do the sorting directly in the body of the View because that block may be called many, many times as SwiftUI renders it (this is transparent to us as developers). I suspect that may be a reason that Approach 2 is not working.

For example:

import Combine

class SomeObject: ObservableObject {

    // or whatever data type you need...
    var array: [String] {
        didSet {
            array.sort(by: { (lhs, rhs) -> Bool in
                // do sorting based on self.environmentObject
            })
            objectWillChange.send()
        }
    }

}
Bradley Mackey
  • 6,777
  • 5
  • 31
  • 45
  • 1
    creating a property that's "ready-to-use" by SwiftUI seems to indeed be the best way. In my case, I was able to create a new array with the sorted contents, publish it and update that. – BlackWolf Jan 28 '20 at 14:01
  • Their ObservableObject is in the environment which likely means it is a model object, you wouldn’t want to be sorting that if a view isn’t onscreen that requires the data to be sorted. Sorting, formatting etc should be done in the View struct, or a @StateObject owned by the View if a reference type is needed for some other reason. – malhal Apr 06 '21 at 12:04