57

Let's see the simple source code:

import SwiftUI

struct MyView: View {
    @State var mapState: Int
    
    init(inputMapState: Int)
    {
        mapState = inputMapState //Error: 'self' used before all stored properties are initialized
    } //Error: Return from initializer without initializing all stored properties
    
    var body: some View {
        Text("Hello World!")
    }
}

I need the init function here because I want to do some data loading here, but there is one problem, the @State variable could not be initialize here! How could I do with that? Maybe it's a very simple question, but I don't know how to do. Thanks very much!

ricardopereira
  • 11,118
  • 5
  • 63
  • 81
norains
  • 743
  • 1
  • 5
  • 12

3 Answers3

91

Property wrappers are generating some code for you. What you need to know is the actual generated stored property is of the type of the wrapper, hence you need to use its constructors, and it is prefixed with a _. In your case this means var _mapState: State<Int>, so following your example:

import SwiftUI

struct MyView: View {
    @State var mapState: Int

    init(inputMapState: Int)
    {
        _mapState = /*State<Int>*/.init(initialValue: inputMapState)
    }

    var body: some View {
        Text("Hello World!")
    }
}
GuillermoMP
  • 1,694
  • 16
  • 19
  • 9
    _mapState = State(initialValue: inputMapState) – Jason Jun 17 '20 at 16:30
  • 1
    That uderscore and `.init` function solved my problem. Is this preferred over using `.onAppear()` on the view itself? – Manngo Oct 01 '20 at 21:46
  • https://developer.apple.com/forums/thread/657393 etc. doesn't seem to be a good idea to use `init(initialValue:)` like this. That initializer may only be there for ABI stability: https://forums.swift.org/t/swiftui-state-init-wrappedvalue-vs-init-initialvalue-whats-the-difference/38904 — might work fine in some cases but behave strangely sometimes. – shim Jan 06 '22 at 04:13
  • 1
    WARNING: The init will only be called the first time. If `inputMapState` changes, you won't see an update to this View. – joshuakcockrell Feb 17 '22 at 19:41
12

We can inject the data that the view needs.

Used a model which has access to the data that you wanted. Make a map view and use that instance of it in your parent view. This will also help to unit test the model.

Used the property wrapper @Binding to pass the data from the parent view to MapView and used _mapState which holds the value of mapState.

struct Model {
 //some data
}

struct MapView {
    private let model: Model
    @Binding var mapState: Int

    init(model: Model, mapState: Binding<Int>) {
        self.model = model
        self._mapState = mapState
    }
}

extension MapView: View {

    var body: some View {
        Text("Map Data")
    }
}
Partha G
  • 1,056
  • 8
  • 13
2

I think that it would better to initialize when you write the code, just like:

@State var mapState = 0

or, if you want to binding the value with another view, use @Binding.

You have more information at https://www.hackingwithswift.com/quick-start/swiftui/what-is-the-binding-property-wrapper

ricardopereira
  • 11,118
  • 5
  • 63
  • 81
Kreimben
  • 284
  • 4
  • 16