2

I have simple viewModel:

final class EmployeeListViewModel: ObservableObject {
 @Published var list = [Employee]()
 init() {
  // some request
  self.list = [Employee, Employee]
 }
}

And have a view:

struct EmployeeView: View {
 @ObservedObject var viewModel = EmployeeListViewModel()
 @State private var showContents: [Bool] = Array(repeating: false, count: viewModel.list.count)// <- error throws here
 var body: some View {
        GeometryReader { fullView in
            ScrollView {
                VStack(spacing: 40) {
                  ForEach(self.viewModel.list) { employee in
                     Text(employee.firstName).foregroundColor(.black)
                  }
                }
            }
        }
 }
}

Error text:

Cannot use instance member 'viewModel' within property initializer; property initializers run before 'self' is available

I tried change it with init:

struct EmployeeView: View {
 @ObservedObject var viewModel = EmployeeListViewModel()
 @State private var showContents: [Bool]

 init() {
        _showContents = State(initialValue: Array(repeating: false, count: viewModel.list.count)) // <- error
    }

 var body: some View {
        GeometryReader { fullView in
            ScrollView {
                VStack(spacing: 40) {
                  ForEach(self.viewModel.list) { employee in
                     Text(employee.firstName).foregroundColor(.black)
                  }
                }
            }
        }
 }
}

But it also throws error:

'self' used before all stored properties are initialized

this throws on I call viewModel on init()

How to solve it? @State i use for card view. There I simplified views for easy understand.

aturan23
  • 4,798
  • 4
  • 28
  • 52
  • Use an `init` or declare the variable as `lazy`. – Joakim Danielson Jul 09 '20 at 05:41
  • Does this answer your question? [How to initialize properties that depend on each other](https://stackoverflow.com/questions/25854300/how-to-initialize-properties-that-depend-on-each-other). Or this, https://stackoverflow.com/questions/43550813/property-initializers-run-before-self-is-available – Joakim Danielson Jul 09 '20 at 05:43
  • @JoakimDanielson I updated question. `init` doesnt resolve. Or I used it with mistake – aturan23 Jul 09 '20 at 05:50
  • @JoakimDanielson tried move code on `init` to `onAppear` it throws `Cannot assign to property: 'self' is immutable` – aturan23 Jul 09 '20 at 06:03

2 Answers2

1

First initialise the state variable to an empty array

@State private var showContents: [Bool] = []

then set it in the init

init() {
    showContents = Array(repeating: false, count: viewModel.list.count)
 }

You shouldn't initialise the view model property in the view but rather use dependency injection

init(viewModel: EmployeeListViewModel) {
    self.viewModel = viewModel
    showContents = Array(repeating: false, count: viewModel.list.count)
}
Joakim Danielson
  • 43,251
  • 5
  • 22
  • 52
0

Here is possible solution

struct EmployeeView: View {
 @ObservedObject var viewModel: EmployeeListViewModel     // << declare
 @State private var showContents: [Bool]                  // << declare

 init() {
       let vm = EmployeeListViewModel()   // create here !!

       // initialize both below
       self.viewModel = vm                     
       self._showContents = State(initialValue: Array(repeating: false, 
              count: vm.list.count))
    }
Asperi
  • 228,894
  • 20
  • 464
  • 690