From 353f8323319946729735d244792c26901c62bfa1 Mon Sep 17 00:00:00 2001 From: Zahid Date: Mon, 28 Dec 2020 18:25:08 +0500 Subject: [PATCH 1/4] Combine support added to SocketIOClient --- Publishers/OnPublisher.swift | 28 +++++++++++++++ Publishers/OnSubscription.swift | 33 ++++++++++++++++++ Socket.IO-Client-Swift.podspec | 8 ++--- Source/SocketIO/Publisher/DisposeBag.swift | 23 +++++++++++++ Source/SocketIO/Publisher/OnPublisher.swift | 30 ++++++++++++++++ .../SocketIO/Publisher/OnSubscription.swift | 34 +++++++++++++++++++ .../Publisher/SocketIOClient+Publisher.swift | 28 +++++++++++++++ 7 files changed, 180 insertions(+), 4 deletions(-) create mode 100644 Publishers/OnPublisher.swift create mode 100644 Publishers/OnSubscription.swift create mode 100644 Source/SocketIO/Publisher/DisposeBag.swift create mode 100644 Source/SocketIO/Publisher/OnPublisher.swift create mode 100644 Source/SocketIO/Publisher/OnSubscription.swift create mode 100644 Source/SocketIO/Publisher/SocketIOClient+Publisher.swift diff --git a/Publishers/OnPublisher.swift b/Publishers/OnPublisher.swift new file mode 100644 index 00000000..abf55523 --- /dev/null +++ b/Publishers/OnPublisher.swift @@ -0,0 +1,28 @@ +// +// OnPublisher.swift +// Observable +// +// Created by Zahid on 28/12/2020. +// + +import Foundation +import Combine + +struct UIControlPublisher: Publisher { + + typealias Output = Control + typealias Failure = Never + + let control: Control + let controlEvents: UIControl.Event + + init(control: Control, events: UIControl.Event) { + self.control = control + self.controlEvents = events + } + + func receive(subscriber: S) where S : Subscriber, S.Failure == UIControlPublisher.Failure, S.Input == UIControlPublisher.Output { + let subscription = UIControlSubscription(subscriber: subscriber, control: control, event: controlEvents) + subscriber.receive(subscription: subscription) + } +} diff --git a/Publishers/OnSubscription.swift b/Publishers/OnSubscription.swift new file mode 100644 index 00000000..ccc145d8 --- /dev/null +++ b/Publishers/OnSubscription.swift @@ -0,0 +1,33 @@ +// +// OnSubscription.swift +// Observable +// +// Created by Zahid on 28/12/2020. +// + +import Foundation +import Combine + +final class UIControlSubscription: Subscription where SubscriberType.Input == Control { + private var subscriber: SubscriberType? + private let control: Control + + init(subscriber: SubscriberType, control: Control, event: UIControl.Event) { + self.subscriber = subscriber + self.control = control + control.addTarget(self, action: #selector(eventHandler), for: event) + } + + func request(_ demand: Subscribers.Demand) { + // We do nothing here as we only want to send events when they occur. + // See, for more info: https://developer.apple.com/documentation/combine/subscribers/demand + } + + func cancel() { + subscriber = nil + } + + @objc private func eventHandler() { + _ = subscriber?.receive(control) + } +} diff --git a/Socket.IO-Client-Swift.podspec b/Socket.IO-Client-Swift.podspec index e9c9c8c0..051433e6 100644 --- a/Socket.IO-Client-Swift.podspec +++ b/Socket.IO-Client-Swift.podspec @@ -11,10 +11,10 @@ Pod::Spec.new do |s| s.homepage = "https://github.com/socketio/socket.io-client-swift" s.license = { :type => 'MIT' } s.author = { "Erik" => "nuclear.ace@gmail.com" } - s.ios.deployment_target = '8.0' - s.osx.deployment_target = '10.10' - s.tvos.deployment_target = '9.0' - s.watchos.deployment_target = '2.0' + s.ios.deployment_target = '13.0' + s.osx.deployment_target = '10.15' + s.tvos.deployment_target = '13.0' + s.watchos.deployment_target = '6.0' s.requires_arc = true s.source = { :git => "https://github.com/socketio/socket.io-client-swift.git", diff --git a/Source/SocketIO/Publisher/DisposeBag.swift b/Source/SocketIO/Publisher/DisposeBag.swift new file mode 100644 index 00000000..d24ea114 --- /dev/null +++ b/Source/SocketIO/Publisher/DisposeBag.swift @@ -0,0 +1,23 @@ +// +// DisposeBag.swift +// Socket.IO-Client-Swift +// +// Created by Zahid on 28/12/2020. +// + +import Foundation +import Combine + +public typealias AnyCancellableDisposeBag = [AnyCancellable] + +// MARK: - AnyCancellable+DisposeBag +/// Adds dispose functionality to `AnyCancellable` class can be can be used to store subscriber tokens. +extension AnyCancellable { + + /// Add `AnyCancellable` to dispose bag + /// - Parameter bag: `inout` bag for disposal + public func add(to bag:inout AnyCancellableDisposeBag) -> Void { + bag.append(self) + } + +} diff --git a/Source/SocketIO/Publisher/OnPublisher.swift b/Source/SocketIO/Publisher/OnPublisher.swift new file mode 100644 index 00000000..de410b7c --- /dev/null +++ b/Source/SocketIO/Publisher/OnPublisher.swift @@ -0,0 +1,30 @@ +// +// OnPublisher.swift +// Observable +// +// Created by Zahid on 28/12/2020. +// + +import Foundation +import Combine + +@available(iOS 13.0, *) +public struct OnPublisher: Publisher { + + public typealias Output = OnSocketData + public typealias Failure = Never + + let socket: SocketIOClient + let controlEvent: String + + init(socket: SocketIOClient, event: String) { + self.socket = socket + self.controlEvent = event + } + + + public func receive(subscriber: S) where S : Subscriber, S.Failure == OnPublisher.Failure, S.Input == OnPublisher.Output { + let subscription = OnSubscription(subscriber: subscriber, socket: socket, event: controlEvent) + subscriber.receive(subscription: subscription) + } +} diff --git a/Source/SocketIO/Publisher/OnSubscription.swift b/Source/SocketIO/Publisher/OnSubscription.swift new file mode 100644 index 00000000..b6981248 --- /dev/null +++ b/Source/SocketIO/Publisher/OnSubscription.swift @@ -0,0 +1,34 @@ +// +// OnSubscription.swift +// Observable +// +// Created by Zahid on 28/12/2020. +// + +import Foundation +import Combine + +public typealias OnSocketData = (data: [Any], ack: SocketAckEmitter) + +@available(iOS 13.0, *) +public final class OnSubscription: Subscription where OnSubscriber.Input == OnSocketData { + private var subscriber: OnSubscriber? + private let socket: SocketIOClient + var uuid: UUID? + init(subscriber: OnSubscriber, socket: SocketIOClient, event: String) { + self.subscriber = subscriber + self.socket = socket + uuid = socket.on(event) { (data, ack) in + _ = subscriber.receive((data, ack)) + } + } + + public func request(_ demand: Subscribers.Demand) { } + + public func cancel() { + guard let ud = self.uuid else { + return + } + socket.off(id: ud) + } +} diff --git a/Source/SocketIO/Publisher/SocketIOClient+Publisher.swift b/Source/SocketIO/Publisher/SocketIOClient+Publisher.swift new file mode 100644 index 00000000..e5007ae0 --- /dev/null +++ b/Source/SocketIO/Publisher/SocketIOClient+Publisher.swift @@ -0,0 +1,28 @@ +// +// SocketIOClient+Publisher.swift +// Socket.IO-Client-Swift +// +// Created by Zahid on 28/12/2020. +// + +import Foundation + +@available(iOS 13.0, *) +public protocol SocketIOCombineCompatible { + func publisher(on event: String) -> OnPublisher + func publisher(clientEvent event: SocketClientEvent) -> OnPublisher +} + +@available(iOS 13.0, *) +extension SocketIOClient: SocketIOCombineCompatible { } + +@available(iOS 13.0, *) +extension SocketIOCombineCompatible where Self: SocketIOClient { + public func publisher(on event: String) -> OnPublisher { + return OnPublisher(socket: self, event: event) + } + + public func publisher(clientEvent event: SocketClientEvent) -> OnPublisher { + return OnPublisher(socket: self, event: event.rawValue) + } +} From 2b0f54d0f6ee38dbdb2a1cf277aa8cdf6f9d75de Mon Sep 17 00:00:00 2001 From: Zahid Date: Mon, 28 Dec 2020 18:46:53 +0500 Subject: [PATCH 2/4] Readme update --- README.md | 41 +++++++++++++++++++ Socket.IO-Client-Swift.podspec | 8 ++-- Source/SocketIO/Publisher/DisposeBag.swift | 2 + Source/SocketIO/Publisher/OnPublisher.swift | 2 +- .../SocketIO/Publisher/OnSubscription.swift | 2 +- .../Publisher/SocketIOClient+Publisher.swift | 6 +-- 6 files changed, 52 insertions(+), 9 deletions(-) diff --git a/README.md b/README.md index 855ff67d..93650b04 100644 --- a/README.md +++ b/README.md @@ -53,6 +53,47 @@ SocketIOClient* socket = manager.defaultSocket; ``` +##Combine Support (available: iOS 13.0+ macOS 10.15+ tvOS 13.0+ watchOS 6.0+) + +Socket.IO-client adds support for combine framework. + +```swift +import SocketIO + +var disposeBag: AnyCancellableDisposeBag = [] + +let manager = SocketManager(socketURL: URL(string: "http://localhost:8080")!, config: [.log(true), .compress]) +let socket = manager.defaultSocket + +socket.publisher(on: "message") + .tryMap { output in + output.ack.with("Got your message") + let data = try JSONSerialization.data(withJSONObject: output.data[0], options: []) + let message = try self.decoder.decode(RTCMessage.self, from: data) + return message + }.catch { (error) -> AnyPublisher in + return Fail(error: HTTPError.encodingIssue(description: error.localizedDescription)).eraseToAnyPublisher() + }.sink { (error) in + print(error) // parse error + } receiveValue: { (message) in + print(message) // message received + }.add(to: &disposeBag) + + +socket.publisher(clientEvent: .statusChange) + .map { output -> ConnectionStatus in + guard let id = output.data.last as? Int else { return .notConnected } + return ConnectionStatus(rawValue: id) ?? .notConnected + } + .sink {[unowned self] (status) in + self.delegate?.webSocketConnectionStatusChanged(self, status: status) + socket.emit("user", ["id": self.UUIDString]) + }.add(to: &disposeBag) + + socket.connect() +``` + + ## Features - Supports socket.io 2.0+ (For socket.io 1.0 use v9.x) - Supports binary diff --git a/Socket.IO-Client-Swift.podspec b/Socket.IO-Client-Swift.podspec index 051433e6..e9c9c8c0 100644 --- a/Socket.IO-Client-Swift.podspec +++ b/Socket.IO-Client-Swift.podspec @@ -11,10 +11,10 @@ Pod::Spec.new do |s| s.homepage = "https://github.com/socketio/socket.io-client-swift" s.license = { :type => 'MIT' } s.author = { "Erik" => "nuclear.ace@gmail.com" } - s.ios.deployment_target = '13.0' - s.osx.deployment_target = '10.15' - s.tvos.deployment_target = '13.0' - s.watchos.deployment_target = '6.0' + s.ios.deployment_target = '8.0' + s.osx.deployment_target = '10.10' + s.tvos.deployment_target = '9.0' + s.watchos.deployment_target = '2.0' s.requires_arc = true s.source = { :git => "https://github.com/socketio/socket.io-client-swift.git", diff --git a/Source/SocketIO/Publisher/DisposeBag.swift b/Source/SocketIO/Publisher/DisposeBag.swift index d24ea114..93989396 100644 --- a/Source/SocketIO/Publisher/DisposeBag.swift +++ b/Source/SocketIO/Publisher/DisposeBag.swift @@ -8,10 +8,12 @@ import Foundation import Combine +@available(macOS 10.15, iOS 13.0, tvOS 13.0, watchOS 6.0, *) public typealias AnyCancellableDisposeBag = [AnyCancellable] // MARK: - AnyCancellable+DisposeBag /// Adds dispose functionality to `AnyCancellable` class can be can be used to store subscriber tokens. +@available(macOS 10.15, iOS 13.0, tvOS 13.0, watchOS 6.0, *) extension AnyCancellable { /// Add `AnyCancellable` to dispose bag diff --git a/Source/SocketIO/Publisher/OnPublisher.swift b/Source/SocketIO/Publisher/OnPublisher.swift index de410b7c..fd93cda7 100644 --- a/Source/SocketIO/Publisher/OnPublisher.swift +++ b/Source/SocketIO/Publisher/OnPublisher.swift @@ -8,7 +8,7 @@ import Foundation import Combine -@available(iOS 13.0, *) +@available(macOS 10.15, iOS 13.0, tvOS 13.0, watchOS 6.0, *) public struct OnPublisher: Publisher { public typealias Output = OnSocketData diff --git a/Source/SocketIO/Publisher/OnSubscription.swift b/Source/SocketIO/Publisher/OnSubscription.swift index b6981248..11919728 100644 --- a/Source/SocketIO/Publisher/OnSubscription.swift +++ b/Source/SocketIO/Publisher/OnSubscription.swift @@ -10,7 +10,7 @@ import Combine public typealias OnSocketData = (data: [Any], ack: SocketAckEmitter) -@available(iOS 13.0, *) +@available(macOS 10.15, iOS 13.0, tvOS 13.0, watchOS 6.0, *) public final class OnSubscription: Subscription where OnSubscriber.Input == OnSocketData { private var subscriber: OnSubscriber? private let socket: SocketIOClient diff --git a/Source/SocketIO/Publisher/SocketIOClient+Publisher.swift b/Source/SocketIO/Publisher/SocketIOClient+Publisher.swift index e5007ae0..fb61a46c 100644 --- a/Source/SocketIO/Publisher/SocketIOClient+Publisher.swift +++ b/Source/SocketIO/Publisher/SocketIOClient+Publisher.swift @@ -7,16 +7,16 @@ import Foundation -@available(iOS 13.0, *) +@available(macOS 10.15, iOS 13.0, tvOS 13.0, watchOS 6.0, *) public protocol SocketIOCombineCompatible { func publisher(on event: String) -> OnPublisher func publisher(clientEvent event: SocketClientEvent) -> OnPublisher } -@available(iOS 13.0, *) +@available(macOS 10.15, iOS 13.0, tvOS 13.0, watchOS 6.0, *) extension SocketIOClient: SocketIOCombineCompatible { } -@available(iOS 13.0, *) +@available(macOS 10.15, iOS 13.0, tvOS 13.0, watchOS 6.0, *) extension SocketIOCombineCompatible where Self: SocketIOClient { public func publisher(on event: String) -> OnPublisher { return OnPublisher(socket: self, event: event) From 9429c44a659b0005071d48d4b13f622ba9908594 Mon Sep 17 00:00:00 2001 From: Zahid Date: Mon, 28 Dec 2020 18:48:09 +0500 Subject: [PATCH 3/4] readme update --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 93650b04..912e6a8b 100644 --- a/README.md +++ b/README.md @@ -53,7 +53,7 @@ SocketIOClient* socket = manager.defaultSocket; ``` -##Combine Support (available: iOS 13.0+ macOS 10.15+ tvOS 13.0+ watchOS 6.0+) +## Combine Support (available: iOS 13.0+ macOS 10.15+ tvOS 13.0+ watchOS 6.0+) Socket.IO-client adds support for combine framework. From 9704944a56a1514bd7fdc573124f80fda9a2c7ad Mon Sep 17 00:00:00 2001 From: Zahid Date: Mon, 28 Dec 2020 18:49:50 +0500 Subject: [PATCH 4/4] Read me change --- README.md | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 912e6a8b..54e8afb1 100644 --- a/README.md +++ b/README.md @@ -83,11 +83,10 @@ socket.publisher(on: "message") socket.publisher(clientEvent: .statusChange) .map { output -> ConnectionStatus in guard let id = output.data.last as? Int else { return .notConnected } - return ConnectionStatus(rawValue: id) ?? .notConnected + return ConnectionStatus(rawValue: id) ?? .notConnected // parsing to local ConnectionStatus Enum } .sink {[unowned self] (status) in - self.delegate?.webSocketConnectionStatusChanged(self, status: status) - socket.emit("user", ["id": self.UUIDString]) + }.add(to: &disposeBag) socket.connect()