isPresented: on the basis of an observed object not being empty?

I am targeting iOS17 and using @Observable.

I have an array of items, and I want to present a sheet when the array is greater than zero.

My @Observable looks like this:

@Observable class BStoreOO {
    var items: [Int] = []
    var showBS: Bool { items.isEmpty }
    func updateItems(newItems: [Int]) {
        self.items = newItems
    }
}

When this array gets added to, from another view, I would like to present a sheet/popover.

Inside my ContentView I have a @State `... that uses that @Observable

struct ContentView: View {
    @State var bStore = BStoreOO()

Further down in my View, how should I toggle this view on the basis of the observed array not being empty? I have tried a number of ways. Some error, some don't, but none present the sheet!

For example:

.popover(isPresented: bStore.showBS) {
                PopoverContent()
            }

Gives the error "Cannot convert value of type 'Bool' to expected argument type 'Binding<Bool>'"

If I add a State like this: @State private var isShowingBS = false

and then add this:

.onChange(of: bStore.items) {
            self.isShowingBS = self.bStore.items.count > 0
        }

I don't get errors but nothing is presented.

What is the correct way to bind the presentation of the sheet to whether the observed items array is empty or not?

  • Forgot to add that shouldShowSheet in a function:

    func shouldShowSheet() -> Binding<Bool> {
            return .init(get: {
                return bStore.items.count > 0
            }, set: { _ in })
        }
    
Add a Comment

Replies

I’m not sure I understand your logic here. Most popover(isPresented:…) modifiers use Binding<Bool> for the isPresented argument. That’s a two-way binding: it supports both get and set. If the popover is dismissed via the user, it sets the Boolean to false.

Given that design, how do you expect to connect this to your array? Should setting the Boolean to false clear out all the items in the array?

Share and Enjoy

Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@" + "apple.com"

Imagine a shopping app. And when you add an item(s) to your basket from a child view (say there is a quantity plus/minus button), you want to bring up the cart as a popover. The user might dismiss the cart from within the cart/popover, but equally, the user might take the item back out of the cart from the child view page, and if there was only one item in the basket, and the user removes it from the ChildView, I would want the cart/popover to dismiss also.

So:

  • items in array, show the cart/popover
  • No items in cart, or cart/popover dismissed directly, hide the cart/popover

Right. But if the user dismisses the popover directly, you want it to remove the items from the cart?

Share and Enjoy

Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@" + "apple.com"

No, that would just be hiding the cart. There would be some other UI to bring it back if needed. I actually ended up with the problem being solved when someone on another forum suggested using didSet on the items variable, and use that to add the logic to toggle the @State private var isShowingBS = false

I actually ended up with the problem being solved …

Right. You need this second piece of state because of exactly the problem I raised above.

Share and Enjoy

Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@" + "apple.com"