0
votes

In my app, I want to store an object in Environment to perform network queries from various views. However, during development I'd like to use a mock instead, and retrieve the payload (in JSON format) from the app's bundle. These are the steps I've taken:

  1. Specify the protocol
    typealias Token = String

    protocol MyAPIConforming {
        func signIn(username: String, password: String, completion: @escaping (Token?, Error?) -> Void)
    }
  1. Implement the two classes I need
    class MockMyAPIManager: MyAPIConforming, ObservableObject {
        func signIn(username: String, password: String, completion: @escaping (Token?, Error?) -> Void) {
            // Read the JSON document from the app's bundle
        }
    }

    class MyAPIManager: MyAPIConforming, ObservableObject {
        func signIn(username: String, password: String, completion: @escaping (Token?, Error?) -> Void) {
            // Perform the network request
        }
    }
  1. Inject the mock object in the Environment
    let myManager = MockMyAPIManager()

    // Create the SwiftUI view that provides the window contents.
    let contentView = LoginView().environmentObject(myManager)
  1. Declare the @Environment object in the view
    struct LoginView: View {
    @EnvironmentObject var myManager: MyAPIConforming
    ...

I did declare myManager to be MyAPIConforming because I could be passing an object of type MockMyAPIManager or MyAPIManager.

However, in step #4 I get the following error:

Property type 'MyAPIConforming' does not match that of the 'wrappedValue' property of its wrapper type 'EnvironmentObject'

I'm not sure if the error means that there is no guarantee that the object adopting to MyAPIConforming will be also adopting the ObservableObject protocol.

So what do I need to do to store either MockMyAPIManager or MyAPIManager in the environment? Is this even possible?

1

1 Answers

1
votes

Here is a possible solution

struct LoginView<T: ObservableObject & MyAPIConforming>: View {
    @EnvironmentObject var myManager: T

        // other code here
}

Update: usage

for content view

LoginView<MyAPIManager>().environmentObject(MyAPIManager())

and for testing/preview

LoginView<MockMyAPIManager>().environmentObject(MockMyAPIManager())