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

Replace NWWebsocket with URLSessionWebSocketTask #442

Draft
wants to merge 9 commits into
base: master
Choose a base branch
from
Draft
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
9 changes: 0 additions & 9 deletions Package.resolved
Original file line number Diff line number Diff line change
@@ -1,15 +1,6 @@
{
"object": {
"pins": [
{
"package": "NWWebSocket",
"repositoryURL": "https://github.com/pusher/NWWebSocket.git",
"state": {
"branch": null,
"revision": "19d23951c8099304ad98e2fc762fa23d6bfab0b9",
"version": "0.5.3"
}
},
{
"package": "TweetNacl",
"repositoryURL": "https://github.com/bitmark-inc/tweetnacl-swiftwrap",
Expand Down
2 changes: 0 additions & 2 deletions Package.swift
Original file line number Diff line number Diff line change
Expand Up @@ -9,14 +9,12 @@ let package = Package(
.library(name: "PusherSwift", targets: ["PusherSwift"])
],
dependencies: [
.package(url: "https://github.com/pusher/NWWebSocket.git", .upToNextMajor(from: "0.5.4")),
.package(url: "https://github.com/bitmark-inc/tweetnacl-swiftwrap", .upToNextMajor(from: "1.0.0")),
],
targets: [
.target(
name: "PusherSwift",
dependencies: [
"NWWebSocket",
"TweetNacl",
],
path: "Sources"
Expand Down
40 changes: 13 additions & 27 deletions Sources/Extensions/PusherConnection+WebsocketDelegate.swift
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import Foundation
import Network
import NWWebSocket

extension PusherConnection: WebSocketConnectionDelegate {

Expand Down Expand Up @@ -48,7 +47,7 @@ extension PusherConnection: WebSocketConnectionDelegate {
- parameter reason: Optional further information on the connection closure.
*/
public func webSocketDidDisconnect(connection: WebSocketConnection,
closeCode: NWProtocolWebSocket.CloseCode,
closeCode: URLSessionWebSocketTask.CloseCode,
reason: Data?) {
resetConnection()

Expand All @@ -64,7 +63,8 @@ extension PusherConnection: WebSocketConnectionDelegate {
// Attempt reconnect if possible

// `autoReconnect` option is ignored if the closure code is within the 4000-4999 range
if case .privateCode = closeCode {} else {

if (4000...4999).contains(closeCode.rawValue) {} else {
guard self.options.autoReconnect else {
return
}
Expand All @@ -86,15 +86,15 @@ extension PusherConnection: WebSocketConnectionDelegate {
}
}

public func webSocketDidAttemptBetterPathMigration(result: Result<WebSocketConnection, NWError>) {
public func webSocketDidAttemptBetterPathMigration(result: Result<WebSocketConnection, Error>) {
switch result {
case .success:
updateConnectionState(to: .reconnecting)

case .failure(let error):
Logger.shared.debug(for: .errorReceived,
context: """
Path migration error: \(error.debugDescription)
Path migration error: \(error)
""")
}
}
Expand All @@ -106,7 +106,7 @@ extension PusherConnection: WebSocketConnectionDelegate {
`PusherChannelsProtocolCloseCode.ReconnectionStrategy`.
- Parameter closeCode: The closure code received by the WebSocket connection.
*/
func attemptReconnect(closeCode: NWProtocolWebSocket.CloseCode = .protocolCode(.normalClosure)) {
func attemptReconnect(closeCode: URLSessionWebSocketTask.CloseCode = .normalClosure) {
guard connectionState != .connected else {
return
}
Expand All @@ -118,8 +118,8 @@ extension PusherConnection: WebSocketConnectionDelegate {
// Reconnect attempt according to Pusher Channels Protocol close code (if present).
// (Otherwise, the default behavior is to attempt reconnection after backing off).
var channelsCloseCode: ChannelsProtocolCloseCode?
if case let .privateCode(code) = closeCode {
channelsCloseCode = ChannelsProtocolCloseCode(rawValue: code)
if (4000...4999).contains(closeCode.rawValue) {
channelsCloseCode = ChannelsProtocolCloseCode(rawValue: UInt16(closeCode.rawValue))
}
let strategy = channelsCloseCode?.reconnectionStrategy ?? .reconnectAfterBackingOff

Expand Down Expand Up @@ -186,22 +186,8 @@ extension PusherConnection: WebSocketConnectionDelegate {
/// - Parameters:
/// - closeCode: The closure code for the websocket connection.
/// - reason: Optional further information on the connection closure.
func logDisconnection(closeCode: NWProtocolWebSocket.CloseCode, reason: Data?) {
var rawCode: UInt16!
switch closeCode {
case .protocolCode(let definedCode):
rawCode = definedCode.rawValue

case .applicationCode(let applicationCode):
rawCode = applicationCode

case .privateCode(let protocolCode):
rawCode = protocolCode
@unknown default:
fatalError()
}

var closeMessage: String = "Close code: \(String(describing: rawCode))."
func logDisconnection(closeCode: URLSessionWebSocketTask.CloseCode, reason: Data?) {
var closeMessage: String = "Close code: \(String(describing: closeCode.rawValue))."
if let reason = reason,
let reasonString = String(data: reason, encoding: .utf8) {
closeMessage += " Reason: \(reasonString)."
Expand All @@ -224,15 +210,15 @@ extension PusherConnection: WebSocketConnectionDelegate {
//
}

public func webSocketDidReceiveError(connection: WebSocketConnection, error: NWError) {
public func webSocketDidReceiveError(connection: WebSocketConnection, error: Error) {
Logger.shared.debug(for: .errorReceived,
context: """
Error: \(error.debugDescription)
Error: \(error)
""")

// Resetting connection if we receive another POSIXError
// than ENOTCONN (57 - Socket is not connected)
if case .posix(let code) = error, code != .ENOTCONN {
if let urlError = error as? URLError, urlError.code.rawValue != POSIXError.ENOTCONN.rawValue {
resetConnection()

guard !intentionalDisconnect else {
Expand Down
93 changes: 93 additions & 0 deletions Sources/Protocols/WebSocketConnection.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
import Foundation

/// Defines a WebSocket connection.
public protocol WebSocketConnection {
/// Connect to the WebSocket.
func connect()

/// Send a UTF-8 formatted `String` over the WebSocket.
/// - Parameter string: The `String` that will be sent.
func send(string: String)

/// Send some `Data` over the WebSocket.
/// - Parameter data: The `Data` that will be sent.
func send(data: Data)

/// Start listening for messages over the WebSocket.
func listen()

/// Ping the WebSocket periodically.
/// - Parameter interval: The `TimeInterval` (in seconds) with which to ping the server.
func ping(interval: TimeInterval)

/// Ping the WebSocket once.
func ping()

/// Disconnect from the WebSocket.
/// - Parameter closeCode: The code to use when closing the WebSocket connection.
func disconnect(closeCode: URLSessionWebSocketTask.CloseCode)

/// The WebSocket connection delegate.
var delegate: WebSocketConnectionDelegate? { get set }
}

/// Defines a delegate for a WebSocket connection.
public protocol WebSocketConnectionDelegate: AnyObject {
/// Tells the delegate that the WebSocket did connect successfully.
/// - Parameter connection: The active `WebSocketConnection`.
func webSocketDidConnect(connection: WebSocketConnection)

/// Tells the delegate that the WebSocket did disconnect.
/// - Parameters:
/// - connection: The `WebSocketConnection` that disconnected.
/// - closeCode: A `URLSessionWebSocketTask.CloseCode` describing how the connection closed.
/// - reason: Optional extra information explaining the disconnection. (Formatted as UTF-8 encoded `Data`).
func webSocketDidDisconnect(connection: WebSocketConnection,
closeCode: URLSessionWebSocketTask.CloseCode,
reason: Data?)

/// Tells the delegate that the WebSocket connection viability has changed.
///
/// An example scenario of when this method would be called is a Wi-Fi connection being lost due to a device
/// moving out of signal range, and then the method would be called again once the device moved back in range.
/// - Parameters:
/// - connection: The `WebSocketConnection` whose viability has changed.
/// - isViable: A `Bool` indicating if the connection is viable or not.
func webSocketViabilityDidChange(connection: WebSocketConnection,
isViable: Bool)

/// Tells the delegate that the WebSocket has attempted a migration based on a better network path becoming available.
///
/// An example of when this method would be called is if a device is using a cellular connection, and a Wi-Fi connection
/// becomes available. This method will also be called if a device loses a Wi-Fi connection, and a cellular connection is available.
/// - Parameter result: A `Result` containing the `WebSocketConnection` if the migration was successful, or a
/// `NWError` if the migration failed for some reason.
func webSocketDidAttemptBetterPathMigration(result: Result<WebSocketConnection, Error>)

/// Tells the delegate that the WebSocket received an error.
///
/// An error received by a WebSocket is not necessarily fatal.
/// - Parameters:
/// - connection: The `WebSocketConnection` that received an error.
/// - error: The `Error` that was received.
func webSocketDidReceiveError(connection: WebSocketConnection,
error: Error)

/// Tells the delegate that the WebSocket received a 'pong' from the server.
/// - Parameter connection: The active `WebSocketConnection`.
func webSocketDidReceivePong(connection: WebSocketConnection)

/// Tells the delegate that the WebSocket received a `String` message.
/// - Parameters:
/// - connection: The active `WebSocketConnection`.
/// - string: The UTF-8 formatted `String` that was received.
func webSocketDidReceiveMessage(connection: WebSocketConnection,
string: String)

/// Tells the delegate that the WebSocket received a binary `Data` message.
/// - Parameters:
/// - connection: The active `WebSocketConnection`.
/// - data: The `Data` that was received.
func webSocketDidReceiveMessage(connection: WebSocketConnection,
data: Data)
}
9 changes: 5 additions & 4 deletions Sources/PusherSwift.swift
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import Foundation
import NWWebSocket

let PROTOCOL = 7
let VERSION = "10.1.5"
Expand Down Expand Up @@ -27,9 +26,11 @@ let CLIENT_NAME = "pusher-websocket-swift"
public init(key: String, options: PusherClientOptions = PusherClientOptions()) {
self.key = key
let urlString = URL.channelsSocketUrl(key: key, options: options)
let wsOptions = NWWebSocket.defaultOptions
wsOptions.setSubprotocols(["pusher-channels-protocol-\(PROTOCOL)"])
let ws = NWWebSocket(url: URL(string: urlString)!, options: wsOptions)
let config = URLSessionConfiguration.default
config.httpAdditionalHeaders = [
"Sec-WebSocket-Protocol": "pusher-channels-protocol-\(PROTOCOL)"
]
let ws = WebSocketClient(url: URL(string: urlString)!, options: config)
connection = PusherConnection(key: key, socket: ws, url: urlString, options: options)
connection.createGlobalChannel()
}
Expand Down
5 changes: 2 additions & 3 deletions Sources/Services/PusherConnection.swift
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import Foundation
import NWWebSocket

// swiftlint:disable file_length type_body_length

Expand All @@ -12,7 +11,7 @@ import NWWebSocket
open var socketId: String?
open var connectionState = ConnectionState.disconnected
open var channels = PusherChannels()
open var socket: NWWebSocket!
open var socket: WebSocketClient!
open var URLSession: Foundation.URLSession
open var userDataFetcher: (() -> PusherPresenceChannelMember)?
open var reconnectAttemptsMax: Int?
Expand Down Expand Up @@ -60,7 +59,7 @@ import NWWebSocket
*/
public init(
key: String,
socket: NWWebSocket,
socket: WebSocketClient,
url: String,
options: PusherClientOptions,
URLSession: Foundation.URLSession = Foundation.URLSession.shared
Expand Down
Loading