5

Is it possible to use a @EnvironmentObject inside an ObservableObject?

It is giving me an error:

Fatal error: No ObservableObject of type EntryViewModel found. A View.environmentObject(_:) for EntryViewModel may be missing as an ancestor of this view.: file SwiftUI, line 0

I know the @EnvironmentObject is good because I use it other places.

If not, how can I get the information?

class ResultsViewModel: ObservableObject {
    @EnvironmentObject var entry: EntryViewModel
    ...
}

Thank you.

pawello2222
  • 46,897
  • 22
  • 145
  • 209
diogenes
  • 1,865
  • 3
  • 24
  • 51

2 Answers2

6

Is it possible to use a @EnvironmentObject inside an ObservableObject?

No, it is not. @EnvironmentObject can be used in View only.

If not, how can I get the information?

A possible solution is to pass it in init:

class ResultsViewModel: ObservableObject {
    var entry: EntryViewModel

    init(entry: EntryViewModel) {
        self.entry = entry
    }
}

Another solution may be to use a singleton or (maybe the best) dependency injection.

pawello2222
  • 46,897
  • 22
  • 145
  • 209
  • I tried the init() and it was a no go. caused other issues. thanks. – diogenes Aug 27 '20 at 23:39
  • @diogenes EnvironmentObject is kind of a singleton for views. If you want to have it always available you may need to use a singleton or [dependency injection](https://theswiftdev.com/swift-dependency-injection-design-pattern/) – pawello2222 Aug 27 '20 at 23:46
  • this is sort of over my pay grade. researching now... thanks. – diogenes Aug 28 '20 at 00:01
  • 1
    Interestingly you can use Environment in ObservableObject. Even though documentation describes Environment as "a property wrapper that reads a value from a view’s environment". – SerhiiK Mar 03 '21 at 07:35
  • @SerhiiK Is that so? Please confirm :) Am running to go test. – Geob Mar 04 '21 at 06:57
  • @Geob of course, otherwise why would I write it? In my project I'm using Environment(\.keyPath) to retrieve the object at any place. It can be inside ObservableObject class or even inside class that doesn't have any protocols implemented. But I still don't know if it's a good practice to do so. One consideration: I don't change that Environment object anywhere in view hierarchy. It just serves me like access to the same service object across the App. – SerhiiK Mar 04 '21 at 09:10
  • Bye here is article about using Environment outside View https://blog.human-friendly.com/how-does-the-swiftui-environment-work-and-can-it-be-used-outside-swiftui-for-dependency-injection – SerhiiK Mar 05 '21 at 17:09
  • I'm also using `@Environment` in my observable objects and even UIKit class and all over my SwiftUI beyond views. It seems very robust to read from but not to write to because of the timing you'r expecting the write to happen in relation to everything else. But I'm using it as a static DI so I never have to write to it, but can read from anywhere.. very useful. However, bummer I can't add EnvironmentObject in ObservableObjects because it makes sense to have nested state. – TruMan1 Oct 02 '22 at 00:53
4

You need to go via DynamicProperty making use of its update func, e.g.

class MyObject: ObservableObject {
    var context: NSManagedObjectContext? {
        didSet {
            if oldValue != context {
                // do something
            }
        }
    }
}

struct MyProperty: DynamicProperty {
    @Environment(\.managedObjectContext) private var viewContext
    @StateObject var object = MyObject()

    // update is called before body in the View containing this property
    func update() {
        // environment vars now are valid
        object.context = viewContext    
    }
}

struct MyView {
    var myProperty = MyProperty()

    var body: some View {
    }
}
malhal
  • 26,330
  • 7
  • 115
  • 133