Swift 结合订阅、正确的流程和架构选择

Mat*_*oal 5 swift combine

让我们说:

• 我的应用程序是 Socket 服务器的客户端。

• 我可以自由编写 Socket 客户端实现以适应 Combine我喜欢

我已经实现了 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)
        }
    }            
}
Run Code Online (Sandbox Code Playgroud)

自定义发布者和订阅

我的自定义订阅保留对服务器管理器实例和订阅者的引用。此外,它实现了一个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) 
    }
}
Run Code Online (Sandbox Code Playgroud)

发布者没有什么特别的……它只会用receive函数创建订阅。它还将订阅附加到服务器上注册的委托列表,以便generatesEvents函数可以通过委托(因此,通过订阅)广播事件。

// 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)
}
Run Code Online (Sandbox Code Playgroud)

你怎么看这个实现?对我来说它看起来很笨重,但我真的不知道如何将事件从服务器管理器桥接到订阅者。

Asp*_*eri 4

我将使用以下简单且易于管理的方法来完成此操作(IMO,这不是“代表”的正确位置)。

完全可测试的模块:消费者是 SwiftUI 视图。已在 Xcode 11.2 / iOS 13.2 上进行测试,但我没有看到任何平台限制。

演示:

SockerServer 模拟合并演示

这是一个粗糙的想法代码。请查找内嵌的其他评论。

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()
    }
}
Run Code Online (Sandbox Code Playgroud)