0

I'm experimenting with SwiftUI and Combine, and wanted to be able to receive notification whenever an array of struct items changes (either changes within items, or changes to the array, i.e. append, remove).

I tried the following code, but all I get, given an input array of three elements, is a receiveValue for each of those three initials, and nothing else even when changes are made to the array through the UI (which correctly reflects the changes). It's as if its a fire once publisher.

struct Item: Identifiable {
    var id = UUID()
    var title: String
}

class ItemStore: ObservableObject {
    @Published var items: [Item]

    private var cancellables = Set<AnyCancellable>()

    init(items: [Item] = []) {
        self.items = items

        self.items.publisher
            .sink(receiveValue: {
                print("save=\($0)")
            })
            .store(in: &cancellables)
    }

}

After some playing around, I tried the following, which then receives a receiveValue containing the entire array, and does so for each futher update. This would actually suffice for my current experiment, but doesn't seem like how it should be done, poking into the internals of the @Published array.

struct Item: Identifiable {
    var id = UUID()
    var title: String
}

class ItemStore: ObservableObject {
    @Published var items: [Item]

    private var cancellables = Set<AnyCancellable>()

    init(items: [Item] = []) {
        self.items = items

        self._items.projectedValue
            .sink(receiveValue: {
                print("save=\($0)")
            })
            .store(in: &cancellables)
    }

}

I guess the crux of the question is, what is the right way to receive notifications over the lifecycle of the app for:

  1. An array
  2. The elements of an array

NB: I'm aware that I can suppress the initial assignment using the .dropFirst method (optionally specifying the number of elements to skip).

The ItemStore instance is passed in using environmentObject and accessed using @EnvironmentObject.

I'm aware there might be much better ways to respond to changes for persisting state, but this still seems valid for a trivial app.

Dave Meehan
  • 3,133
  • 1
  • 17
  • 24
  • "doesn't seem like how it should be done, poking into the internals of the @Published array" -- is this because you're accessing with the `_` synthesized variable? The most common way is just to use `$items` instead of `_items.projectedValue` – jnpdx Nov 01 '21 at 15:34
  • @jnpdx Ahh yes, couldn't see it for looking! I think I must have put used $self which of course wouldn't work, then lost sight of what I was doing. Thanks so much. – Dave Meehan Nov 01 '21 at 18:15

1 Answers1

0

As per the comment from @jnpdx, I was missing the $ before items, so the correct usage should have been

self.$items.sink(...
Dave Meehan
  • 3,133
  • 1
  • 17
  • 24