Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Can I create a generic ObservableObject class which can be used by multiple ContentViews?

Hi I was just wondering is it possible to create a generic class confirming to ObservableObject protocol which can be used by more then one ContentViews.

If i can do that then I will be able make my ContentView and Model class completely generic and reusable.

An example of what i would like to achieve:

protocol ContentViewModelType: ObservableObject {
    var propertyToInitialiseView: [String] { get }
}

struct ContentView: View {
    @ObservedObject var viewModel: some ViewModel

    var body: some View {
        Text("Hello World")
    }
}

If I can do that any class can implement ContentViewModelType and become a model for ContentView which makes it generic and reusable. For example

class ViewModel: ObservableObject {
    var objectWillChange = PassthroughSubject<ViewModel, Never>()
}

But when i try to initialise ContentView that xcode gives me a type error.

enter image description here

I thought the whole point of introducing some keyword was so that we can use protocol as type for those protocols that have associated type as a requirement and hence this should work. But it gives an error.

If anyone has any references or knowledge about this problem that they could share or possibly a solution for this it would be great.

Thanks in advance.

like image 383
nishith Singh Avatar asked Jan 25 '23 19:01

nishith Singh


1 Answers

Trying to understand your question, and I am not quite sure I understand the intent... but to create a view which takes in generic view model (based on the protocol you had before) you will need the following code:

protocol ViewModelWithProperties: ObservableObject {
    var properties: [String] { get }
}

struct SomeView<T>: View where T: ViewModelWithProperties {
// this can also be written as 
// struct SomeView<T: ViewModelWithProperties>: View {
    @ObservedObject var item: T

    var body: some View {
        VStack {
            ForEach(item.properties, id: \.self) {
                Text($0)
            }
        }
    }
}

To consume this instance you will need to:

struct ContentView: View {
    var body: some View {
        SomeView(item: MyViewModel())
    }
}

As stated in one of the other answers, some is used for opaque types, it doesn't make your code generic.

like image 180
rodrigoelp Avatar answered Jan 28 '23 07:01

rodrigoelp