title: How do I bind information received via an API to different views?
body: <p>I am working on a project of my first application. I have a REST API that provides information that should be used to update the state of various views.  But I have not yet managed to find a solution that satisfies me in all respects. First of all, I am not sure if I am choosing the right approach at all. I know that it would be very inefficient to fetch information from the server every time a view is updated and that the use of a cache is therefore essential. Since the topic is already very complex for me, I'll leave out everything that has to do with CACHE for now.</p>
  <p>Enough talk, I'll just walk you through my code.</p>
  <p>Here is the core of my API. The enum endpoint specifies the desired request. It can be assumed that each request has a different return value type. For example, in the cases of my stripped down API version: String, Object2(class), Object3(class).</p>
  <pre><code>struct GetInformationAPI {
  
  static var shared = GetInformationAPI()
  
  enum Endpoint {
      
      static let baseUrl = URL(string: &quot;https://localhost:8080/getInformation/&quot;)!
      
      case getUsername
      case getObject2
      case getObject3
      
      var request: URLRequest {
          
          var request = URLRequest(url: Endpoint.baseUrl)
          request.addValue(&quot;application/json&quot;, forHTTPHeaderField: &quot;Content-Type&quot;)
          request.addValue(ViewNavigatorModel.shared.token.value, forHTTPHeaderField: &quot;Authorization&quot;)
          
          switch self {
          case .getUsername:
              request.url = Endpoint.baseUrl.appendingPathComponent(&quot;username&quot;)
              return request
          case .getObject2:
              request.url = Endpoint.baseUrl.appendingPathComponent(&quot;Object2&quot;)
              return request
          case .getObject3:
              request.url = Endpoint.baseUrl.appendingPathComponent(&quot;Object3&quot;)
              return request
          }
      }
      
  }
  
  
  let sharedJSONDecoder = JSONDecoder()
  private let apiQueue = DispatchQueue(label: &quot;API&quot;, qos: .default, attributes: .concurrent) 
  
  }
  </code></pre>
  <p>To get an answer to the requested query, I would normally use a method like the following for each case:</p>
  <pre><code>    func getObject2() -&gt; AnyPublisher&lt;Object2, Never&gt; {
      URLSession.shared.dataTaskPublisher(for: Endpoint.getObject2.request)
          .receive(on: apiQueue)
          .map{ $0.0 }
          .decode(type: Object2.self, decoder: sharedJSONDecoder)
          .catch{_ in Empty() }
          .eraseToAnyPublisher()
  }
  </code></pre>
  <p>Then I would call it in the specific view on appearance and bind it to a @Propertywrapper.
  However, I am looking for a solution for any number of requests, each with any response value return type. So I tried this:</p>
  <pre><code>func getInformation_2&lt;Input: ObservableObjectConvertable, Output: ObservableObject&gt;(for using: Endpoint, input: Input) -&gt; AnyPublisher&lt;Output, Never&gt; {
      URLSession.shared.dataTaskPublisher(for: using.request)
          .receive(on: apiQueue)
          .map{ $0.0 }
          .decode(type: Input.self, decoder: sharedJSONDecoder)
          .catch{_ in Empty() }
          .map{$0.toObservableObject() }
          .eraseToAnyPublisher()
      
  }
  </code></pre>
  <p>As you can see, I made the function generic, with the input being convertible to ObservableObject. I thought this would make it easier to link to the views.
  Then when called in the different views, Enpoint needs to be specified, the return type, as well as an example of the Input needs to be set. ( I didn't know how else to declare the Input type).
  But from there I'm not really getting anywhere, I found a solution where I catch the ObservableObject that is spit out by this Publisher and in turn it is packed into another ObservableObject. I don't think that's quite the point.</p>
  <p>Here is my further, hopefully not too confusing code.</p>
  <pre><code>protocol ObservableObjectConvertable: Codable {
      func toObservableObject&lt;T: ObservableObject&gt;() -&gt; T
  }
  
  struct Object2: ObservableObjectConvertable {
      func toObservableObject&lt;T&gt;() -&gt; T where T : ObservableObject {
          return Object2OO(theValue: value) as! T
      }
      var value: Int
  }
  
  class Object2OO: ObservableObject {
      static var uninitialized = Object2OO()
      var uninitialized: Bool
      @Published var value: Int
      init(theValue: Int) { value = theValue; uninitialized = false}
      init() { uninitialized = true; value = -1}
  }
  
  struct View2: View {
  
  @StateObject var view2Model = View2Model()
  
  
  var body: some View {
      VStack{
          if(view2Model.object2oo.uninitialized) {
              Text(&quot;...&quot;)
          } else {
              View2Inside(object2oo: view2Model.object2oo)
          }
      }.onAppear(perform: getInformation)
  }
  
  
  func getInformation() {
      view2Model.getInformation()
  }
  
  
  }
  
  struct View2Inside: View {
  
  @ObservedObject var object2oo: Object2OO
  
  var body: some View {
      Text(&quot;object 2 has the value: \(object2oo.value)&quot;)
  }
  
  }
  
  class View2Model: ObservableObject {
  
  @Published var object2oo = Object2OO()
  
  var subscriptions = Set&lt;AnyCancellable&gt;()
  
  func getInformation() {
      let publisher: AnyPublisher&lt;Object2OO, Never&gt; = GetInformationAPI.shared.getInformation_2(for: .getObject2, input: Object2(value: 1) )
      publisher
          .sink(
                receiveValue: { print(&quot;value received: \($0.value)&quot;)
                    self.object2oo = $0
          })
          .store(in: &amp;subscriptions)
  }
  }
  </code></pre>
  <p>Maybe someone can shed some light for me in all this darkness... :)</p>