skip to Main Content

So I am working on a view where I want to load state from an EnvironmentObject which acts something like a database.

I would like to achieve something like this:

class MyDB: ObservableObject {
    func getName(_ id: RecordID) -> String { ... }
    func getChildren(_ id: RecordID) -> [RecordID] { ... }
    var didUpdate: PassthroughSubject...

}

struct TreeView: View {
    let id: RecordID
    @EnvironmentObject var db: DB
    @State var name: String
    @State var children: [RecordID]

    func loadState() {
        self.name = db.getName(id)
        self.children = db. getChildren(id)
    }

    var body: some View {
        Text(self.name)
        List(self.children) { child in
            TreeView(id: child)
        }
        .onReceive(self.db.didUpdate) { _ in
            self.loadState()
        }
    }

}

So basically I would like to just pass the id of the node in the tree view to the child view, and then load the state from this environment object with the loadState function before the view is displayed.

Is there any way to achieve this? For instance, is there some kind of lifecycle function I could implement which will be called after the environment is bound?

Or for example can I implement loadState inside a custom init?

What would be the idiomatic way to handle this?

2

Answers


  1. I have provided an explanation here if you want to check it out.

    You will need to pass your MyDB instance using .environmentObject(myDBInstance) on a parent view, so all children views can read from the environment through @EnvironmentObject.

    Login or Signup to reply.
  2. Try using a different approach, such as the following code,
    where children and name are published var of MyDB, and
    the functions just load the data into those.

    // for testing
    struct RecordID: Identifiable {
        let id = UUID().uuidString
        var thing: String = ""
    }
    
    class MyDB: ObservableObject {
        @Published var didUpdate: Bool = false
        @Published var children: [RecordID] = []
        @Published var name: String = ""
        
        func getName(_ id: RecordID) {
            // ...
            name = "xxx"  // whatever
        }
        
        func getChildren(_ id: RecordID) {
            // ...
            children = [RecordID(), RecordID()] // whatever
        }
    }
    
    struct TreeView: View {
        @EnvironmentObject var db: MyDB
        @State var id: RecordID
        
        var body: some View {
            Text(db.name)
            List(db.children) { child in
              //  TreeView(id: child)  // <-- here recursive call, use OutlineGroup instead
              Text("(child.id)")
            }
            .onReceive(db.$didUpdate) { _ in
                loadState()
            }
        }
    
        func loadState() {
            db.getName(id)
            db.getChildren(id)
        }
        
    }
    
    struct ContentView: View {
        @StateObject var myDB = MyDB()
        let recId = RecordID()
        
        var body: some View {
            TreeView(id: recId).environmentObject(myDB)
        }
    }
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search