-1

The ObservableObject class is being instantiated from both the ContentView() as well as another Swift class. When a function of the ObservableObject class is run by the Swift class, it does not update the @ObservedObject of the ContentView().

I am aware that this is due to me instantiating the ObservableObject class twice. What is the best practice to utilise @ObservedObject when the Observable Class is not/cannot be instantiated by the ContentView().

I haven't found a way to make @EnvironmentObject work with Swift classes.
I could use a global variable and run a Timer() to check for changes to it. However, this feels like an ugly way to do it?!?

Please see example code below. Please run on a device, to see the print statement.


import SwiftUI

struct ContentView: View {
    
    @ObservedObject var observedClass: ObservedClass = ObservedClass()
    
    // The callingObservedClass does not exist on the ContentView, but is called
    // somewhere in the app with no reference to the ContentView.
    // It is included here to better showcase the issue.
   
    let callingObservedClass: CallingObservedClass = CallingObservedClass()
    
    var body: some View {
        
        VStack {
   
            // This Text shall be updated, when
            // self.callingObservedClass.increaseObservedClassCount() has been executed.
   
            Text(String(observedClass.count))
            
            Button(action: {
    
                // This updates the count-variable, but as callingObservedClass creates
                // a new instance of ObservedClass, the Text(observedClass.count) is not updated.
   
                self.callingObservedClass.increaseObservedClassCount()
            }, label: {
                Text("Increase")
            })
        }
    }
}


class CallingObservedClass {
    
    let observedClass = ObservedClass()
    
    func increaseObservedClassCount() {
   
        // Returning an Int here to better showcase that count is increased.
        // But not in the ObservedClass instance of the ContentView, as the
        // Text(observedClass.count) remains at 0.
    
        let printCount = observedClass.increaseCount()
        print(printCount)
    }
}


class ObservedClass: ObservableObject {
    @Published var count: Int = 0
    
    func increaseCount() -> Int {
        count = count + 1
        return count
    }
}


struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        ContentView()
    }
}

Edit: I suppose my question is how do I get data from a Swift class and update a SwiftUI view when the data changes when I am unable to instantiate the Swift class from the SwiftUI view.

Peanutsmasher
  • 220
  • 3
  • 13

1 Answers1

0

A possible solution to this is to chain the ObservableObject classes. Unfortunately, as of iOS 13.6 this does not work out of the box.

I found the answer via: How to tell SwiftUI views to bind to nested ObservableObjects

Adjusted & functioning example:

// Add Combine

import Combine
import SwiftUI

struct ContentView: View {

    @ObservedObject var callingObservedClass: CallingObservedClass = CallingObservedClass()
    
    var body: some View {
        
        VStack {

            // Calling the chained ObservableObject
            
            Text(String(callingObservedClass.observedClass.count))
            
            Button(action: {

                self.callingObservedClass.increaseObservedClassCount()
            }, label: {
                Text("Increase")
            })
        }
    }
}

class CallingObservedClass: ObservableObject {
    
    // Chaining Observable Objects
    
    @Published var observedClass = ObservedClass()
    
    // ObservableObject-chaining does not work out of the box.
    // The anyCancellable variable with the below init() will do the trick.
    // Thanks to https://stackoverflow.com/questions/58406287/how-to-tell-swiftui-views-to-bind-to-nested-observableobjects
    
    var anyCancellable: AnyCancellable? = nil
    
    init() {
        anyCancellable = observedClass.objectWillChange.sink { (_) in
            self.objectWillChange.send()
        }
    }
    
    func increaseObservedClassCount() {
        let printCount = observedClass.increaseCount()
        print(printCount)
    }
}

class ObservedClass: ObservableObject {
    @Published var count: Int = 0
    
    func increaseCount() -> Int {
        count = count + 1
        return count
    }
}


struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        ContentView()
    }
}

I am interested on how to access Swift Class data and update an SwiftUI view if ObservableObject-Chaining is not an option.

Please answer below if you have a solution to this.

Peanutsmasher
  • 220
  • 3
  • 13