让我们这么说:
• 我的应用程序是 Socket 服务器的客户端。
• 我可以自由地编写符合Combine
的Socket 客户端实现。我喜欢
我已经实现了 2 个解决方案,一个是 CurrentValueSubject
(很简单),第二个是我不确定的自定义订阅和自定义发布者。我真的不知道哪种方法是桥接我用来处理服务器消息的代码的最佳方法。
这是我的代码:
为了模拟套接字服务器,我创建了一个假的 SocketServerManager
每 N
生成一些事件秒:
protocol SocketServerManagerDelegate{
func newEvent(event:String)
}
class SocketServerManager {
let timing: Double
var timerHandler:Timer? = nil
var delegates:[SocketServerManagerDelegate] = []
init(timing:Double){
self.timing = timing
}
func start(){
// Just start a timer that calls generateEvent to simulate some events
timerHandler = Timer.scheduledTimer(withTimeInterval: timing, repeats: true){
[weak self] _ in
self?.generateEvent()
}
timerHandler?.fire()
}
private func generateEvent(){
let events = ["New Player", "Player Disconnected", "Server Error"]
let currentEvent = events.randomElement
for delegate in delegates{
delegate.newEvent(event: currentEvent)
}
}
}
自定义发布者和订阅
我的自定义订阅保留了对服务器管理器实例和订阅者的引用。
此外,它实现了
SocketServerManager
代表。这样,当服务器有一个新事件时,它会调用现在可以发送 receive
的订阅。订阅者上的事件 (这是我有很多疑问的选择...)class EventSubscription<S:Subscriber>:Subscription, SocketServerManagerDelegate
where S.Input == String{
private var subscriber:S?
private unowned var server:SocketServerManager
init(sub:S, server:EventsServer){
self.subscriber = sub
self.server = server
}
func request(_ demand: Subscribers.Demand) {}
func cancel() {
subscriber = nil
}
// HERE IS WHERE I SEND THE EVENT TO THE SUBSCRIBER since this subscription
is a delegate of the server manager
func newEvent(event: Event) {
_ = subscriber?.receive(event)
}
}
发布者没有什么特别的...它只会使用
receive
创建订阅功能。它还将订阅附加到在服务器上注册的代表列表中,以便 generatesEvents
函数可以通过委托(delegate)(因此,通过订阅)广播事件。// PUBLISHER CODE ----------
func receive<S>(subscriber: S)
where S:Subscriber,
EventsPublisher.Failure == S.Failure,
EventsPublisher.Output == S.Input {
let subscription = EventSubscription(sub:subscriber, server: self.server)
server.delegates.append(subscription)
subscriber.receive(subscription: subscription)
}
你怎么看这个实现?对我来说,这似乎很笨重,但我真的不知道如何将事件从服务器管理器连接到订阅者。
最佳答案
我将使用以下简单且易于管理的方法来做到这一点(IMO 这不是“代表”的正确位置)。
完全可测试的模块:消费者是 SwiftUI View 。经测试可与 Xcode 11.2/iOS 13.2 一起使用,但我没有看到任何平台限制。
演示:
这是一个粗糙的想法代码。请在线查找其他评论。
import SwiftUI
import Combine
protocol SocketServerManagerDelegate{
func newEvent(event:String)
}
class SocketServerManager {
// transparent subject that manages subscribers/subscriptions
let publisher = PassthroughSubject<String, Never>()
let timing: Double
var timerHandler:Timer? = nil
init(timing:Double){
self.timing = timing
}
func start(){
// Just start a timer that calls generateEvent to simulate some events
timerHandler = Timer.scheduledTimer(withTimeInterval: timing, repeats: true){
[weak self] _ in
self?.generateEvent()
}
timerHandler?.fire()
}
func stop(){
publisher.send(completion: .finished) // notifies all that finished
}
private func generateEvent(){
let events = ["New Player", "Player Disconnected", "Server Error"]
guard let currentEvent = events.randomElement() else { return }
publisher.send(currentEvent) // send to all subscribers
}
}
// usage
class ViewModel: ObservableObject {
private let server = SocketServerManager(timing: 1)
private var cancellables = Set<AnyCancellable>()
func setup() {
guard cancellables.isEmpty else { return } // already set up
// add one example subscriber
server.publisher
.assign(to: \.value1, on: self)
.store(in: &cancellables)
// add another example subscriber
server.publisher
.sink(receiveValue: { value in
self.value2 = value
})
.store(in: &cancellables)
server.start()
}
@Published var value1: String = "<unknown>"
@Published var value2: String = "<unknown>"
}
// view demo
struct TestSocketServerPublisher: View {
@ObservedObject var viewModel = ViewModel()
var body: some View {
VStack {
Text("Observer1: \(viewModel.value1)")
Divider()
Text("Observer2: \(viewModel.value2)")
}
.onAppear {
self.viewModel.setup()
}
}
}
struct TestSocketServerPublisher_Previews: PreviewProvider {
static var previews: some View {
TestSocketServerPublisher()
}
}
关于Swift 结合订阅、正确的流程和架构选择,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/59345963/