1

I have a view that is initialized with a list of items, and then during the initialization process, we need to pick one item at random. Something like this:

struct ItemsView: View {

    var items:[Item]
    @State var current:Item?

    init(items:[Item] = []) {
        self.items = items
        if items.count > 0 {
            let index = Int.random(in: 0..<items.count)
            self.current = items[index] // doesnt work
        }
        if current != nil {
            print("init with", self.items.count, "items. Chose", current!)
        }
    }

    // ...
}

The job of the ItemsVew is to show one item at time, at random, so we start by picking an item at random, but self.current = items[index] literally doesn't do anything as far as I can tell, for reasons I don't understand. So either I am doing something silly, or I am thinking about how to solve this in an incorrectly somehow.

So how do you initialize a State variable in the init function. I have tried:

self.current = State(initialValue: items[index])

But that simply triggers a compiler error. So how do we select an initial item that will be used when the view is displayed?

Cannot assign value of type 'State<Item>' to type 'Item'

What am I doing wrong?

Jay
  • 19,649
  • 38
  • 121
  • 184

2 Answers2

1

Because @State is a property wrapper, you want to assign to the underlying variable itself, not the wrapped value type (which is Item? in this case).

self._current = State(initialValue: items[index])
//   ^ note the underscore

The best documentation for this is available in the original Swift Evolution SE-0258 proposal document.

Bradley Mackey
  • 6,777
  • 5
  • 31
  • 45
0

@State is a property wrapper, so you will need to assign a Item? object to the current property that is why the compiler gives you an error. So as long as the items array you pass is not empty, this should work as expected. Also I encourage you to use randomElement() method on the items array instead of producing a random index.

struct ItemsView: View {

    var items: [Item]
    @State var current: Item?

    init(items: [Item] = []) {
        self.items = items
        self.current = items.randomElement()
        if let item = current {
            print("init with", self.items.count, "items. Choose", item)
        }
    }

    // ...
}
Witek Bobrowski
  • 3,749
  • 1
  • 20
  • 34
  • This won't actually work because SwiftUI doesn't allow you to change the value of a state property in the initialiser. You have to initialise the underlying state property itself i.e. self._current. – Upholder Of Truth Jul 01 '21 at 17:17
  • How is that? I just just checked and I was able to assign a value to @State property in an init without any errors – Witek Bobrowski Jul 01 '21 at 17:29
  • Yep it doesn't give an error but also doesn't set the value. You should find that the State property is still the default you had for it. – Upholder Of Truth Jul 01 '21 at 18:52