Swift 结合订阅、正确的流程和架构选择
Swift Combine Subscriptions, right flow and architectural choices
假设:
• 我的应用程序是套接字服务器的客户端。
• 我可以随意编写 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)
}
}
}
自定义发布者和订阅
我的自定义订阅保留对服务器管理器实例和订阅者的引用。
此外,它还实现了一个 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
函数可以通过委托(因此,通过订阅)广播事件。
// 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 这不是 "delegate"s 的正确位置)。
完全可测试的模块:消费者是 SwiftUI 视图。测试使用 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()
}
}
假设:
• 我的应用程序是套接字服务器的客户端。
• 我可以随意编写 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)
}
}
}
自定义发布者和订阅
我的自定义订阅保留对服务器管理器实例和订阅者的引用。
此外,它还实现了一个 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
函数可以通过委托(因此,通过订阅)广播事件。
// 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 这不是 "delegate"s 的正确位置)。
完全可测试的模块:消费者是 SwiftUI 视图。测试使用 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()
}
}