Oleksandr Matrosov
Oleksandr Matrosov

Reputation: 27133

SwiftUI Fatal error: No ObservableObject of type found

I want a single point in my app where I can store some data and quick access it. Before SwiftUI I just had ApplicationDelegate for this purposes. Before I just needed to call kind of appDelegate.userService and it provides to me a single point in app which was initialised once. No more singletons at all just appDelegate.

Now I found this tutorial how to use @EnvironmentObject

I have added this code here to init my userService:

class SceneDelegate: UIResponder, UIWindowSceneDelegate {

    var window: UIWindow?
    var userService = UserService() 

...

Also I have this code in my view:

struct LoginView: View {

    @EnvironmentObject var userService: UserService

    var body: some View {

...

In the above LoginView I have a function to fetch a user from the server by email:

func fetchUser(with email: String) {

        userService.getUser(with: email) { (result) in
            switch result {
            case .success(let user):
                self.userService.user = user // update user in user service after fetch
                self.showWelcomeView = true
                break
            case .failure:
                break
            }

            self.showActivityIndicator = false
        }
    }

But when I try to call getUser function I get this error:

Fatal error: No ObservableObject of type UserService found.
A View.environmentObject(_:) for UserService may be missing as an ancestor of this view.: file /BuildRoot/Library/Caches/com.apple.xbs/Sources/Monoceros_Sim/Monoceros-39.4.3/Core/EnvironmentObject.swift, line 55
2020-04-18 23:52:41.774988+0300 BusinessTool[7426:730172] Fatal error: No ObservableObject of type UserService found.
A View.environmentObject(_:) for UserService may be missing as an ancestor of this view.: file /BuildRoot/Library/Caches/com.apple.xbs/Sources/Monoceros_Sim/Monoceros-39.4.3/Core/EnvironmentObject.swift, line 55

Here is my user service:

class UserService: ObservableObject {

    enum UserFetchResult {
        case success(data: User)
        case failure
    }

    typealias UserServiceResponse = (User?, NSError?) -> Void

    @Published var user = User(username: "", email: "")
...

The main question is how to resolve the issue above?

I also have side questions:

  1. Do I need to setup this environment singleton object (@EnvironmentObject) at all or it's better to use AppDelegate as before appDelegate.userService and store all needed data there, I don't see any advantages of this thing.
  2. In case there's some advantages do I need to make a user service as ObservableObject as to me user service is more about execution some functionality (fetch user data, update user data on the server, in the local storage and etc) rather than data model layer which we need to observe. Confused at all here. In the link I provided above Paul uses this ObservableObject for app settings, not sure this is right.
  3. In my example I try to fetch user from the server and update user in the user service after fetch, is this correct or clean designed code or I overthink it?

Upvotes: 1

Views: 1703

Answers (1)

Nicolas Mandica
Nicolas Mandica

Reputation: 861

In your SceneDelegate you should call .environmentObject(userService) on your root view (LoginView).

Upvotes: 2

Related Questions