Home > front end >  How to dynamically bind a @SceneStorage to a @FetchRequest in a custom initializer?
How to dynamically bind a @SceneStorage to a @FetchRequest in a custom initializer?

Time:12-19

Let's assume an app using Core Data with one Item entity having a name and a date.

How to dynamically set the sortDescriptors parameter of a @FetchRequest in a custom View initializer?

The code below triggers an error:

Accessing a SceneStorage value outside of being installed on a View. This will always return the default value.

enum ItemSort: String {
    case date
    case name
    
    static let `default`: Self = .date
    
    var descriptor: SortDescriptor<Item> {
        switch self {
        case .date:
            return .init(\.date, order: .forward)
        case .name:
            return .init(\.name, order: .forward)
        }
    }
}

struct ItemList: View {
    @SceneStorage private var sort: ItemSort
    @FetchRequest private var items: FetchedResults<Item>
    
    init() {
        _sort = .init(wrappedValue: .default, "itemSort")
        _items = .init(sortDescriptors: [_sort.wrappedValue.descriptor], animation: .default)  // Error: Accessing a SceneStorage value outside of being installed on a View. This will always return the default value.
    }
    
    var body: some View {
        List(items) { item in
            /* ... */
        }
    }
}

CodePudding user response:

You can try doing it at the top of body like this:

var body: some View {
    let _ = items.sortDescriptors = [sort.descriptor]
    List(items) { item in

It isn't a state mutation so theoretically it's fine. It might benefit from extracting it out to a func that checks if the current sortDescriptors already has the correct order and only set it if required.

  • Related