Home > Mobile >  Accessing an enum in ObservableObject
Accessing an enum in ObservableObject

Time:06-01

I'm trying to refactor some code I have and thought I'd be able to move some of the code from globally accessible enum into the ViewModel of the app.

However, when I try to access it from the View it doesn't seem to be accessible or even an option in the autocomplete.

I was wondering if there was a reason why this was the case and if there was something I was missing..

// view
struct MyView: View {
 @StateObject var vm = MyViewModel()
 var body: some View {
  ForEach(vm.MyEnum.allCases, id: \.self) { item in
   ...
  }
 }
}

// view model
final class MyViewModel: ObservableObject {
 enum MyEnum: String, CaseIterable {
  case a, b, c
 }
}

I seem to be able to access everything else in a View Model but when it comes to enums they are not. I tried reading about enums to see why, but most things I come across are mainly "how-to" but nothing deep diving why.

CodePudding user response:

Your problem isn't really about enums, it's more about properties vs types. When you do this:

final class MyViewModel: ObservableObject {
    enum MyEnum: String, CaseIterable {
        case a, b, c
    }
}

... you're defining 2 types.

  1. MyViewModel, which is a class
  2. MyEnum, which is an enum

It doesn't matter that your MyEnum is nested inside MyViewModel — it's still just a type, not a property.

Properties are declared with let or var. For example:

final class MyViewModel: ObservableObject {
    let myEnumArray = [MyEnum.a, MyEnum.b, MyEnum.c] /// create an instance property

    enum MyEnum: String, CaseIterable {
        case a, b, c
    }
}

struct MyView: View {
    @StateObject var vm = MyViewModel()
    var body: some View {

                /// access the instance property here!
        ForEach(vm.myEnumArray, id: \.self) { item in
            /// ...
        }
    }
}

In the above code I've created a new property, myEnumArray, which you can access with vm.myEnumArray. This will work.

Now what if you want to access MyEnum's allCases? Note that this property is static — the property belongs to the type itself and not an instance of the type. That's why it's not showing up in your autocomplete. Example:

MyViewModel.MyEnum.allCases /// Works! The type is `MyViewModel.MyEnum` and I'm accessing the static `allCases` property.

@StateObject var vm = MyViewModel()
vm.MyEnum.allCases /// NO! the `MyEnum` property doesn't exist inside `vm`.

So your code really has 2 problems.

  1. You think MyEnum is a property. No, it's a type!
  2. Because MyEnum is a type, you can't access it with vm.MyEnum (that's how you access an instance property). You need to do MyViewModel.MyEnum.

Here's the fixed code:

struct MyView: View {
    var body: some View {
        ForEach(MyViewModel.MyEnum.allCases, id: \.self) { item in
            /// ...
        }
    }
}
  • Related