Home > Net >  How can I load state from en EnvironmentObject in SwiftUI?
How can I load state from en EnvironmentObject in SwiftUI?

Time:02-24

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?

CodePudding user response:

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.

CodePudding user response:

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)
    }
}
  • Related