1

I keep receiving an error/lint which reads Variable 'self.item' used before being initialized. This message only appears when I seemingly add a @State of type Date (see commented line below).

Variable item is a CoreData value that I'm attempting to update through a form. All of the other required data types (int, string, data, etc.) all work as expected.

I'm fairly confident that this is an issue which stems from my lack of experience with Swift or declarative-style languages in general, but I'm also wary that it could be a compiler issue as I seem to run into a few of those as well.

import SwiftUI

struct SelectionView: View {
    @State var item : Item
    
    @Environment(\.managedObjectContext) private var viewContext
    
    @State private var name : String
    @State private var imageData : Data
    @State private var timestamp : Date // error only appears when this line is added
    
    init(item : Item) {
        self.item = item
        self._name = State(initialValue: item.name!)
        self._imageData = State(initialValue: item.imageData!)
        self._timestamp = State(initialValue: item.timestamp!)
    }

    var body: some View {
        VStack {
            Form
            {
                TextField("Name", text: $name)
                Image(uiImage: UIImage(data: imageData)!)
                Button(action: changeImage, label: { Text("Change image") })
                Button(action: save, label: { Text("Save") })
            }
        }
        .padding()
    }

    ...
5
  • 1
    You have declared the Date and trying to assign timestamp. Use same data time for @State var self._timestamp = State(initialValue: Date(timeIntervalSince1970: item.timestamp)) Commented Jul 30, 2021 at 13:54
  • Thanks for your comment. Why does this not apply to name or imageData? Commented Jul 30, 2021 at 13:55
  • 1
    Do you really need the 3 other state variables? You could just use item. $item.name, item.imageData Commented Jul 30, 2021 at 13:56
  • Especially for Core Data I would use an Observable proxy class with from and to methods in an extension of the NSManagedObject subclass. The benefit is a more convenient handling of the optionals and you don't need to insert a new object into the context if you also want to use the form for adding a new item. Commented Jul 30, 2021 at 14:35
  • @vadian would you be able to point me in the direction of some online resources which demonstrate this? I'm always looking to improve my code with relation to best possible practice, so any help would be greatly appreciated. Commented Jul 30, 2021 at 16:04

1 Answer 1

4

Just do the following:

@State var item: Item

init(item: Item) {
    _item = State(initialValue: item)
}

You can access all of the things you may need:

  • item
  • $item.name (equivalent to when you had $name, same for other below)
  • $item.imageData
  • $item.timestamp

Also, if you don't need to pass anything else in, you can also get rid of the initializer because it is an inferred memberwise intializer.

Reason for the error you were having:

self.item doesn't exist yet. You set the @State with _item, and that will make the item and $item variables.

We can see this because seeing the definition of State shows the following:

public var wrappedValue: Value { get nonmutating set }  // Normal value

/* ... */

public var projectedValue: Binding<Value> { get }  // Binding value

So since you can't access self.item because _item hasn't been initialized yet, you get the error. That's why we set _item using State(initialValue:).

Sign up to request clarification or add additional context in comments.

5 Comments

Thank you for the answer. Do you have any idea why my solution is producing that specific error, though?
@Dan Added some explanation.
Often times in the initializer you will find that the order of initialization matters to the compiler. However, you should accept the above answer as it is the most correct and elegant solution. You should not declare 4 State variables where one will do.
Thank you. Excellent answer and it’ll help me to develop better applications in the future.
While you can do this _item = ... with property wrappers, it's considered an anti pattern if you initialise a @State variable in a SwiftUI View initialiser. This statement will have no effect when the associated storage for State for the "conceptual view" has been already initialised. Note, that the lifetime of struct View is not bound to the State, but to it's identity - or the "conceptual view" with the same identity. The proper way to use "external" values is to pass them in as Bindings whose backing value usually come from an @ObservableObject or the like hosted by a parent view.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.