Meu aplicativo inclui vários widgets que indicam valores em tempo real (temperatura, uso da CPU, memória, etc....). Esses widgets são atualizados a partir de mensagens JSON recebidas por meio de um único websocket.
No momento, as atualizações são tratadas em tempo real, então atualizo meu widget assim que um novo valor é recebido.
As mensagens são as seguintes: {widget: "widgetId", value: 150}
.
Meu problema é que às vezes há várias atualizações por segundo para o mesmo widget e isso faz a tela piscar e tem um custo de desempenho inútil. Quero reduzir isso apenas atrasando a atualização da IU para atualizar apenas com o último valor recebido por um período de tempo.
Gostaria de explorar as duas opções a seguir:
Atualize todos os widgets ao mesmo tempo uma vez a cada X segundos (mantendo apenas a atualização mais recente para cada widget e enviando todas de uma vez para a interface do usuário).
Atualiza um widget no máximo uma vez a cada X segundos (um debounce por canal).
Meu manipulador de soquete se parece com isto:
socket.receive(completionHandler: { [weak self] result in
switch result {
case .success(let message):
switch message {
case .data(let data):
guard let update = try? WidgetUpdate(serializedData: data) else {
print("Failed to deserialize data")
return
}
DispatchQueue.main.async {
self?.updateWidget(update: update)
}
@unknown default:
print("unknown")
}
}
}
)
O que tentei até agora é usar algo PassthroughSubject
assim:
let subject = PassthroughSubject<WidgetUpdate, Never>()
cancellable = subject
.debounce(for: .seconds(0.5), scheduler: RunLoop.main)
.sink { update in
self?.updateWidget(update: update)
}
// And to publish inside the publisher from the socket handler like this
subject.send(update)
O problema é que ele obviamente debounces "todas" as atualizações e não por widget.
Eu gostaria de evitar criar um PassthroughSubject por widget, pois o widget pode ser adicionado ou excluído dinamicamente e eu não quero ter que gerenciar a criação/exclusão dos publicadores associados.
Acho que o que vai ajudar você é o operador ReactiveX GroupBy . Infelizmente, Combine não vem com um GroupBy nativo, mas pode ser implementado em termos de um scan :
Depois que você tiver isso, acredito que algo assim deve resolver seu problema (isso se baseia no seu exemplo original):
E aqui está algo baseado no seu problema real com WebSockets:
Além disso, parece-me que o que você quer é realmente throttle , em vez de debounce , embora para o seu caso de uso, a maioria dos usuários provavelmente não notará a diferença entre os dois. Contanto que você agrupe seus publicadores, você pode escolher throttle ou debounce.