Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Combine support #1320

Open
wants to merge 4 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 28 additions & 0 deletions Publishers/OnPublisher.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
//
// OnPublisher.swift
// Observable
//
// Created by Zahid on 28/12/2020.
//

import Foundation
import Combine

struct UIControlPublisher<Control: UIControl>: 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<S>(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)
}
}
33 changes: 33 additions & 0 deletions Publishers/OnSubscription.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
//
// OnSubscription.swift
// Observable
//
// Created by Zahid on 28/12/2020.
//

import Foundation
import Combine

final class UIControlSubscription<SubscriberType: Subscriber, Control: UIControl>: 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)
}
}
40 changes: 40 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,46 @@ 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<RTCMessage, HTTPError> 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 // parsing to local ConnectionStatus Enum
}
.sink {[unowned self] (status) in

}.add(to: &disposeBag)

socket.connect()
```


## Features
- Supports socket.io 2.0+ (For socket.io 1.0 use v9.x)
- Supports binary
Expand Down
25 changes: 25 additions & 0 deletions Source/SocketIO/Publisher/DisposeBag.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
//
// DisposeBag.swift
// Socket.IO-Client-Swift
//
// Created by Zahid on 28/12/2020.
//

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
/// - Parameter bag: `inout` bag for disposal
public func add(to bag:inout AnyCancellableDisposeBag) -> Void {
bag.append(self)
}

}
30 changes: 30 additions & 0 deletions Source/SocketIO/Publisher/OnPublisher.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
//
// OnPublisher.swift
// Observable
//
// Created by Zahid on 28/12/2020.
//

import Foundation
import Combine

@available(macOS 10.15, iOS 13.0, tvOS 13.0, watchOS 6.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<S>(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)
}
}
34 changes: 34 additions & 0 deletions Source/SocketIO/Publisher/OnSubscription.swift
Original file line number Diff line number Diff line change
@@ -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(macOS 10.15, iOS 13.0, tvOS 13.0, watchOS 6.0, *)
public final class OnSubscription<OnSubscriber: Subscriber>: 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)
}
}
28 changes: 28 additions & 0 deletions Source/SocketIO/Publisher/SocketIOClient+Publisher.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
//
// SocketIOClient+Publisher.swift
// Socket.IO-Client-Swift
//
// Created by Zahid on 28/12/2020.
//

import Foundation

@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(macOS 10.15, iOS 13.0, tvOS 13.0, watchOS 6.0, *)
extension SocketIOClient: SocketIOCombineCompatible { }

@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)
}

public func publisher(clientEvent event: SocketClientEvent) -> OnPublisher {
return OnPublisher(socket: self, event: event.rawValue)
}
}