Home > Mobile >  Update property without running combine pipeline
Update property without running combine pipeline

Time:10-15

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?

CodePudding user response:

I'm not aware of a pause feature in Combine. Your pointer to the subscription is the returned AnyCancellable.

You added a debounce. I'm curious if you want to ignore the first value (an empty array in your case). For that, you could use .dropFirst() in your pipeline.

Otherwise, I think you have to cancel and resubscribe before mutating items.

class Test: ObservableObject {
    private var cancellable: AnyCancellable?
    @Published var items: [Int] = []
    init() {
       observe()
    }
    
    func pleaseDoNotNotifyPipeline() {
        cancel()
        items.append(1)
        observe()
    }
    
    func cancel() {
        cancellable?.cancel()
    }
    
    func observe() {
        cancellable = $items
            .dropFirst() // ignores the first value []
            .sink {
                print($0)
            }
    }
}

CodePudding user response:

This is a problem since the dawn of times :) How to avoid making a server request if the request would be the same as the previous one?

Just removeDuplicates():

init() {
    $items
        .debounce(for: 0.6, scheduler: RunLoop.main)
        .removeDuplicates()
        .sink {
            print($0)
        }
        .store(in: &cancellables)
}

This assumes your data object is an Equatable one, but that shouldn't be hard to make, if not already.

CodePudding user response:

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()!)
    }
    
}
  • Related