Search code examples
swiftcombine

Update property without running combine pipeline


If I have a combine pipeline, that prints changes to an array, is it then possible to update that array without having the pipeline run?

class ObserveableTest: ObservableObject {
    private var cancellables: Set<AnyCancellable> = []
    @Published var items: [Int] = []
    init() {
        $items
            .debounce(for: 0.6, scheduler: RunLoop.main)
            .sink {
                print($0)
            }
            .store(in: &cancellables)
    }
    
    func pleaseDoNotNotifyPipeline() {
        items.append(1)
    }
}

Update: The code provided is a simplified version of what I work on. In short, I work on a basket manager, and whenever the user changes the quantity of a product (or in another way changes the basket) a request will need to be sent to the backend, why the debounce. In the code above, it is simply shown with changes to an array instead.

The case is, that the changes the user makes to the basket should run the the pipeline, however, it can also be that the system (from a web-response etc.) updates the local basket itself, and in that case the pipeline should not run.

I fear that cancelation and resubscribing to the pipeline in rare cases can mean that, changes from the user is lost. But im also maybe realising the cancel/resub is the only solution, and I will need to take care of that rare situation. Or do you see other solutions now that I have elaborated a bit?


Solution

  • On further inspection, the answer came to me. This is the case for class PassthroughSubject.

    class ObserveableTest: ObservableObject {
        
        private var cancellables: Set<AnyCancellable> = []
    
        /// private(set) to restrict all changes to go through class functions
        @Published private(set) var items: [Int] = []
    
        /// A subject we can pass data into, when we wanna publish changes
        private let listener = PassthroughSubject<[Int], Never>()
        
        init() {
            listener
                .debounce(for: 0.6, scheduler: RunLoop.main)
                .sink {
                    print($0)
                }
                .store(in: &cancellables)
        }
        
    
        func thisFunctionIsCalledByUserAndTriggersPipeline() {
            items.append((0...1000000).randomElement()!)
            listener.send(items) // publish to pipeline
        }
        
        func thisFunctionIsCalledByServerAndDoesNotTriggerPipeline() {
            items.append((0...1000000).randomElement()!)
        }
        
    }