英文:
Custom Combine Publisher for a single database listener
问题
我有一个带有自定义数据库的iOS应用程序,为了从我的数据库中检索数据,我设置了一个监听器,如下所示:
var listener: DatabaseListener?
self.listener = db.items.addListener { items in
}
// later when I don't need the listener anymore:
self.listener?.cancel()
这个监听器在我设置它后立即提供项目,并在我的数据更新时通知我。它也会一直保持活动状态,直到我手动取消它。我还在UserDefaults
中存储了检索到的项目的缓存,以加速操作(在下面的示例中查看它的运行方式)。
现在我尝试开始使用Combine
来检索我的项目,我希望在创建新的订阅时立即设置数据库监听器(例如当调用sink
或assign
时),并在没有更多订阅时取消它。
因此,我想到了以下解决方案:
class ItemsSubscription: Subscription {
private var subscriber: AnySubscriber<[Item], Never>?
private var listener: DatabaseListener?
private var items: [Item] = UserDefaults.standard.cacheItems
init(subscriber: AnySubscriber<[Item], Never>) {
self.subscriber = subscriber
}
func request(_ demand: Subscribers.Demand) {
let _ = subscriber?.receive(items)
self.listener = db.items.addListener {
UserDefaults.standard.cacheItems = $0
self.items = $0
let _ = self.subscriber?.receive($0)
}
}
func cancel() {
self.listener?.cancel()
self.listener = nil
self.subscriber = nil
}
}
struct ItemsPublisher: Publisher {
typealias Output = [Item]
typealias Failure = Never
func receive<S>(subscriber: S) where S: Subscriber, S.Input == [Item], S.Failure == Never {
let subscription = ItemsSubscription(subscriber: subscriber)
subscriber.receive(subscription: subscription)
}
}
然后,我使用ItemsPublisher
如下:
private var cancellables: Set<AnyCancellable> = []
ItemsPublisher()
.sink { items in
}
.store(&cancellables)
目前,这种方法有效,但它为每个我创建的ItemsPublisher
创建了一个新的数据库监听器(这是一种昂贵的资源)。相反,我希望在有至少一个订阅者时保持单个数据库监听器,并希望任何后续的订阅者从同一订阅接收到最新的项目。
我考虑过创建一个单个的ItemsPublisher
实例并在整个应用程序中使用它,但后来的订阅者根本不接收任何数据。
我还考虑过使用CurrentValueSubject
(或@Published
属性)来存储项目,但我无法弄清楚何时设置数据库监听器,或者什么时候取消它。希望这些信息对你有所帮助。
英文:
I have an iOS app with a custom database, To retrieve data from my database I setup a listener like this:
var listener: DatabaseListener?
self.listener = db.items.addListener { items in
}
// later when I don't need the listener any more:
self.listener?.cancel()
This listener gives the items as soon as I set it up and notifies me whenever my data is updated, It also stays alive until I manually cancel it. I also store a cache of the retrieved items in UserDefaults
to speed things up (See it in action in the example bellow).
Now I'm trying to start using Combine
to retrieve my items, I want to setup the database listener as soon as a new subscription is created (for example when sink
or assign
are called) and cancel it when there's no more subscriptions left.
So here's what I came up with:
class ItemsSubscription: Subscription {
private var subscriber: (any Subscriber<[Item], Never>)?
private var listener: DatabaseListener?
private var items: [Item] = UserDefaults.standard.cacheItems
init(subscriber: any Subscriber<[Item], Never>) {
self.subscriber = subscriber
}
func request(_ demand: Subscribers.Demand) {
let _ = subscriber?.receive(items)
self.listener = db.items.addListener {
UserDefaults.standard.cacheItems = $0
self.items = $0
let _ = self.subscriber?.receive($0)
}
}
func cancel() {
self.listener?.cancel()
self.listener = nil
self.subscriber = nil
}
}
struct ItemsPublisher: Publisher {
typealias Output = [Item]
typealias Failure = Never
func receive<S>(subscriber: S) where S: Subscriber, S.Input == [Item], S.Failure == Never {
let subscription = ItemsSubscription(subscriber: subscriber)
subscriber.receive(subscription: subscription)
}
}
Then I'm using ItemsPublisher
like this:
private var cancellables: Set<AnyCancellable> = []
ItemsPublisher()
.sink { items in
}
.store(&cancellables)
Currently this method is working but it's creating a new database listener (which is an expensive resource) for every ItemsPublisher
I create. Instead I want to maintain a single database listener while I have a least 1 subscriber and I want any following subscriber to receive the latest items from the same subscription.
I considered creating a single ItemsPublisher
instance and using it throughout the app, but later subscribers didn't receive any data at all.
I also considered using CurrentValueSubject
(or a @Published
property) to store the items but I couldn't figure out when to setup database listener or when to cancel it for that matter.
Any help or advice would be appreciated.
答案1
得分: 1
> Instead I want to maintain a single database listener while I have a least 1 subscriber and I want any following subscriber to receive the latest items from the same subscription.
这是share()
的用途。 查看文档以获取更多信息。
你可能还希望根据情况考虑使用具有CurrentValueSubject
的多播。
英文:
> Instead I want to maintain a single database listener while I have a least 1 subscriber and I want any following subscriber to receive the latest items from the same subscription.
That's exactly what share()
is for. View the documentation for more information.
You might also want to consider using multicast with a CurrentValueSubject depending on the situation.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论