Home > Enterprise >  Creating a sequential publisher in Swift Combine
Creating a sequential publisher in Swift Combine

Time:12-26

I want to debounce a batch of events and process them after a delay of ~1.5 seconds. Here's what I've done.

class ViewModel: ObservableObject {

    @Published var pending: [TaskInfo]
    private var cancellable: AnyCancellable? = nil

    init() {
        processPendingTasks()
    }

    func queueTask(task: TaskInfo) { 
        pending.append(task)
    }

    private func processPendingTasks() {
        cancellable = $pendingTasks
                .debounce(for: 1.5, scheduler: RunLoop.main)
                .sink(receiveValue: { batch in 
                    // Iterate though elements and process events.
                })           
    }
}

Issue: This works fine, but the issue that I've is that it performs unnecessary view updates since the array is tagged @Published.

What I'm looking for: The ideal approach would be a streaming setup where I get all events (in a batched fashion) but the sink should wait exactly for 1.5 seconds after the last event was added.

I tried PassthroughSubject, but it seems like it only gets me the last event that happened in the last 1.5 seconds.

CodePudding user response:

A possible solution is a combination of a PassthroughSubject and the collect operator. In queueTask send the tasks to the subject.

func queueTask(task: TaskInfo) { 
    subject.send(task)
}

1.5 seconds after receiving the last item send

subject.send(completion: .finished)

and subscribe

subject
.collect()
.sink { [weak self] tasks in 
   self?.pending = tasks
 } 

If the interval of the incoming tasks is < 1.5 seconds you could also use the .timeout(1.5) operator which terminates the pipeline after the timeout interval.

  • Related