From b1177feba315a36cf0a641a476f3ddfc2abe050f Mon Sep 17 00:00:00 2001 From: Pierluigi Cifani Date: Thu, 9 Jan 2025 13:00:07 +0100 Subject: [PATCH 1/3] It compiles --- Package.resolved | 42 ++++++++---- Package.swift | 10 ++- Source/SocketIO/Ack/SocketAckEmitter.swift | 3 - Source/SocketIO/Client/SocketAnyEvent.swift | 2 - Source/SocketIO/Client/SocketIOClient.swift | 22 +++--- .../Client/SocketIOClientOption.swift | 3 + .../SocketIO/Client/SocketIOClientSpec.swift | 4 +- Source/SocketIO/Client/SocketIOStatus.swift | 1 - Source/SocketIO/Client/SocketRawView.swift | 3 - Source/SocketIO/Engine/SocketEngine.swift | 9 ++- .../SocketIO/Engine/SocketEngineClient.swift | 5 +- .../Engine/SocketEnginePacketType.swift | 2 +- .../Engine/SocketEnginePollable.swift | 7 +- Source/SocketIO/Engine/SocketEngineSpec.swift | 7 +- .../Engine/SocketEngineWebsocket.swift | 4 +- Source/SocketIO/Manager/SocketManager.swift | 1 - Source/SocketIO/Util/SocketExtensions.swift | 3 + Source/SocketIO/Util/SocketTypes.swift | 4 +- Tests/TestSocketIO/SocketAckManagerTest.swift | 6 +- Tests/TestSocketIO/SocketEngineTest.swift | 21 ++++-- Tests/TestSocketIO/SocketMangerTest.swift | 34 +++++++--- Tests/TestSocketIO/SocketSideEffectTest.swift | 67 +++++++++++++------ Tests/TestSocketIO/utils.swift | 1 - 23 files changed, 176 insertions(+), 85 deletions(-) diff --git a/Package.resolved b/Package.resolved index b363050e..14059ba1 100644 --- a/Package.resolved +++ b/Package.resolved @@ -1,16 +1,32 @@ { - "object": { - "pins": [ - { - "package": "Starscream", - "repositoryURL": "https://github.com/daltoniam/Starscream", - "state": { - "branch": null, - "revision": "c6bfd1af48efcc9a9ad203665db12375ba6b145a", - "version": "4.0.8" - } + "pins" : [ + { + "identity" : "starscream", + "kind" : "remoteSourceControl", + "location" : "https://github.com/theleftbit/Starscream.git", + "state" : { + "branch" : "android", + "revision" : "fd27e744f358663eb8802692269000f45a630138" } - ] - }, - "version": 1 + }, + { + "identity" : "swift-asn1", + "kind" : "remoteSourceControl", + "location" : "https://github.com/apple/swift-asn1.git", + "state" : { + "revision" : "7faebca1ea4f9aaf0cda1cef7c43aecd2311ddf6", + "version" : "1.3.0" + } + }, + { + "identity" : "swift-crypto", + "kind" : "remoteSourceControl", + "location" : "https://github.com/apple/swift-crypto.git", + "state" : { + "revision" : "ff0f781cf7c6a22d52957e50b104f5768b50c779", + "version" : "3.10.0" + } + } + ], + "version" : 2 } diff --git a/Package.swift b/Package.swift index 89729293..8ea79831 100644 --- a/Package.swift +++ b/Package.swift @@ -1,14 +1,20 @@ -// swift-tools-version:5.4 +// swift-tools-version:5.9 import PackageDescription let package = Package( name: "SocketIO", + platforms: [ + .iOS(.v13), + .macOS(.v10_15), + .tvOS(.v13), + .watchOS(.v6) + ], products: [ .library(name: "SocketIO", targets: ["SocketIO"]) ], dependencies: [ - .package(url: "https://github.com/daltoniam/Starscream", .upToNextMajor(from: "4.0.8")), + .package(url: "https://github.com/theleftbit/Starscream.git", branch: "android"), ], targets: [ .target(name: "SocketIO", dependencies: ["Starscream"]), diff --git a/Source/SocketIO/Ack/SocketAckEmitter.swift b/Source/SocketIO/Ack/SocketAckEmitter.swift index 18bebcfc..dce21d3f 100644 --- a/Source/SocketIO/Ack/SocketAckEmitter.swift +++ b/Source/SocketIO/Ack/SocketAckEmitter.swift @@ -41,7 +41,6 @@ public final class SocketAckEmitter: NSObject { /// ``` /// /// **NOTE**: It is not safe to hold on to this view beyond the life of the socket. - @objc public private(set) lazy var rawEmitView = SocketRawAckView(socket: socket, ackNum: ackNum) // MARK: Properties @@ -83,7 +82,6 @@ public final class SocketAckEmitter: NSObject { /// Call to ack receiving this event. /// /// - parameter items: An array of items to send when acking. Use `[]` to send nothing. - @objc public func with(_ items: [Any]) { guard ackNum != -1 else { return } @@ -127,7 +125,6 @@ public final class OnAckCallback: NSObject { /// - parameter seconds: The number of seconds before this emit times out if an ack hasn't been received. /// - parameter callback: The callback called when an ack is received, or when a timeout happens. /// To check for timeout, use `SocketAckStatus`'s `noAck` case. - @objc public func timingOut(after seconds: Double, callback: @escaping AckCallback) { guard let socket = self.socket, ackNumber != -1 else { return } diff --git a/Source/SocketIO/Client/SocketAnyEvent.swift b/Source/SocketIO/Client/SocketAnyEvent.swift index 441e8c55..f27bb1cb 100644 --- a/Source/SocketIO/Client/SocketAnyEvent.swift +++ b/Source/SocketIO/Client/SocketAnyEvent.swift @@ -29,11 +29,9 @@ public final class SocketAnyEvent : NSObject { // MARK: Properties /// The event name. - @objc public let event: String /// The data items for this event. - @objc public let items: [Any]? /// The description of this event. diff --git a/Source/SocketIO/Client/SocketIOClient.swift b/Source/SocketIO/Client/SocketIOClient.swift index d97bae1e..89cbca8b 100644 --- a/Source/SocketIO/Client/SocketIOClient.swift +++ b/Source/SocketIO/Client/SocketIOClient.swift @@ -212,7 +212,7 @@ open class SocketIOClient: NSObject, SocketIOClientSpec { /// - parameter event: The event to send. /// - parameter items: The items to send with this event. May be left out. /// - parameter completion: Callback called on transport write completion. - open func emit(_ event: String, _ items: SocketData..., completion: (() -> ())? = nil) { + open func emit(_ event: String, _ items: SocketData..., completion: (@Sendable() -> ())? = nil) { emit(event, with: items, completion: completion) } @@ -224,7 +224,7 @@ open class SocketIOClient: NSObject, SocketIOClientSpec { /// - parameter event: The event to send. /// - parameter items: The items to send with this event. May be left out. /// - parameter completion: Callback called on transport write completion. - open func emit(_ event: String, with items: [SocketData], completion: (() -> ())?) { + open func emit(_ event: String, with items: [SocketData], completion: (@Sendable () -> ())?) { do { emit([event] + (try items.map({ try $0.socketRepresentation() })), completion: completion) @@ -296,15 +296,21 @@ open class SocketIOClient: NSObject, SocketIOClientSpec { ack: Int? = nil, binary: Bool = true, isAck: Bool = false, - completion: (() -> ())? = nil + completion: (@Sendable () -> ())? = nil ) { // wrap the completion handler so it always runs async via handlerQueue - let wrappedCompletion: (() -> ())? = (completion == nil) ? nil : {[weak self] in - guard let this = self else { return } - this.manager?.handleQueue.async { - completion!() + let wrappedCompletion: (@Sendable () -> ())? = { + if let completion { + return completion + } else { + return { [weak self] in + guard let this = self else { return } + this.manager?.handleQueue.async { + completion!() + } + } } - } + }() guard status == .connected else { wrappedCompletion?() diff --git a/Source/SocketIO/Client/SocketIOClientOption.swift b/Source/SocketIO/Client/SocketIOClientOption.swift index 545c210b..85af5a84 100644 --- a/Source/SocketIO/Client/SocketIOClientOption.swift +++ b/Source/SocketIO/Client/SocketIOClientOption.swift @@ -24,6 +24,9 @@ import Foundation import Starscream +#if os(Android) +import FoundationNetworking +#endif /// The socket.io version being used. public enum SocketIOVersion: Int { diff --git a/Source/SocketIO/Client/SocketIOClientSpec.swift b/Source/SocketIO/Client/SocketIOClientSpec.swift index 04b62faa..ba133739 100644 --- a/Source/SocketIO/Client/SocketIOClientSpec.swift +++ b/Source/SocketIO/Client/SocketIOClientSpec.swift @@ -106,7 +106,7 @@ public protocol SocketIOClientSpec : AnyObject { /// - parameter event: The event to send. /// - parameter items: The items to send with this event. May be left out. /// - parameter completion: Callback called on transport write completion. - func emit(_ event: String, _ items: SocketData..., completion: (() -> ())?) + func emit(_ event: String, _ items: SocketData..., completion: (@Sendable () -> ())?) /// Send an event to the server, with optional data items and optional write completion handler. /// @@ -116,7 +116,7 @@ public protocol SocketIOClientSpec : AnyObject { /// - parameter event: The event to send. /// - parameter items: The items to send with this event. May be left out. /// - parameter completion: Callback called on transport write completion. - func emit(_ event: String, with items: [SocketData], completion: (() -> ())?) + func emit(_ event: String, with items: [SocketData], completion: (@Sendable () -> ())?) /// Call when you wish to tell the server that you've received the event for `ack`. /// diff --git a/Source/SocketIO/Client/SocketIOStatus.swift b/Source/SocketIO/Client/SocketIOStatus.swift index 4f298a20..9093b531 100644 --- a/Source/SocketIO/Client/SocketIOStatus.swift +++ b/Source/SocketIO/Client/SocketIOStatus.swift @@ -25,7 +25,6 @@ import Foundation /// Represents state of a manager or client. -@objc public enum SocketIOStatus : Int, CustomStringConvertible { // MARK: Cases diff --git a/Source/SocketIO/Client/SocketRawView.swift b/Source/SocketIO/Client/SocketRawView.swift index e348ade3..8f5f1c34 100644 --- a/Source/SocketIO/Client/SocketRawView.swift +++ b/Source/SocketIO/Client/SocketRawView.swift @@ -60,7 +60,6 @@ public final class SocketRawView : NSObject { /// /// - parameter event: The event to send. /// - parameter items: The items to send with this event. Send an empty array to send no data. - @objc public func emit(_ event: String, with items: [Any]) { socket.emit([event] + items, binary: false) } @@ -113,7 +112,6 @@ public final class SocketRawView : NSObject { /// - parameter event: The event to send. /// - parameter items: The items to send with this event. Use `[]` to send nothing. /// - returns: An `OnAckCallback`. You must call the `timingOut(after:)` method before the event will be sent. - @objc public func emitWithAck(_ event: String, with items: [Any]) -> OnAckCallback { return socket.createOnAck([event] + items, binary: false) } @@ -154,7 +152,6 @@ public final class SocketRawAckView : NSObject { /// Call to ack receiving this event. /// /// - parameter items: An array of items to send when acking. Use `[]` to send nothing. - @objc public func with(_ items: [Any]) { guard ackNum != -1 else { return } diff --git a/Source/SocketIO/Engine/SocketEngine.swift b/Source/SocketIO/Engine/SocketEngine.swift index 1d1c071d..69fca81e 100644 --- a/Source/SocketIO/Engine/SocketEngine.swift +++ b/Source/SocketIO/Engine/SocketEngine.swift @@ -25,6 +25,9 @@ import Dispatch import Foundation import Starscream +#if os(Android) +import FoundationNetworking +#endif /// The class that handles the engine.io protocol and transports. /// See `SocketEnginePollable` and `SocketEngineWebsocket` for transport specific methods. @@ -563,7 +566,11 @@ open class SocketEngine: NSObject, WebSocketDelegate, URLSessionDelegate, polling = true probing = false invalidated = false + #if os(Android) + session = FoundationNetworking.URLSession(configuration: .default, delegate: sessionDelegate, delegateQueue: queue) + #else session = Foundation.URLSession(configuration: .default, delegate: sessionDelegate, delegateQueue: queue) + #endif sid = "" waitingForPoll = false waitingForPost = false @@ -656,7 +663,7 @@ open class SocketEngine: NSObject, WebSocketDelegate, URLSessionDelegate, /// - parameter type: The type of this message. /// - parameter data: Any data that this message has. /// - parameter completion: Callback called on transport write completion. - open func write(_ msg: String, withType type: SocketEnginePacketType, withData data: [Data], completion: (() -> ())? = nil) { + open func write(_ msg: String, withType type: SocketEnginePacketType, withData data: [Data], completion: (@Sendable () -> ())? = nil) { engineQueue.async { guard self.connected else { completion?() diff --git a/Source/SocketIO/Engine/SocketEngineClient.swift b/Source/SocketIO/Engine/SocketEngineClient.swift index 903fa6d8..e2eb9b2a 100644 --- a/Source/SocketIO/Engine/SocketEngineClient.swift +++ b/Source/SocketIO/Engine/SocketEngineClient.swift @@ -24,9 +24,12 @@ // import Foundation +#if os(Android) +import FoundationNetworking +#endif /// Declares that a type will be a delegate to an engine. -@objc public protocol SocketEngineClient { +public protocol SocketEngineClient: AnyObject { // MARK: Methods /// Called when the engine errors. diff --git a/Source/SocketIO/Engine/SocketEnginePacketType.swift b/Source/SocketIO/Engine/SocketEnginePacketType.swift index a3611688..8da20080 100644 --- a/Source/SocketIO/Engine/SocketEnginePacketType.swift +++ b/Source/SocketIO/Engine/SocketEnginePacketType.swift @@ -26,7 +26,7 @@ import Foundation /// Represents the type of engine.io packet types. -@objc public enum SocketEnginePacketType: Int { +public enum SocketEnginePacketType: Int { /// Open message. case open diff --git a/Source/SocketIO/Engine/SocketEnginePollable.swift b/Source/SocketIO/Engine/SocketEnginePollable.swift index a5ee0736..4d8e4661 100644 --- a/Source/SocketIO/Engine/SocketEnginePollable.swift +++ b/Source/SocketIO/Engine/SocketEnginePollable.swift @@ -23,6 +23,9 @@ // THE SOFTWARE. import Foundation +#if os(Android) +import FoundationNetworking +#endif /// Protocol that is used to implement socket.io polling support public protocol SocketEnginePollable: SocketEngineSpec { @@ -65,7 +68,7 @@ public protocol SocketEnginePollable: SocketEngineSpec { /// - parameter message: The message to send. /// - parameter withType: The type of message to send. /// - parameter withData: The data associated with this message. - func sendPollMessage(_ message: String, withType type: SocketEnginePacketType, withData datas: [Data], completion: (() -> ())?) + func sendPollMessage(_ message: String, withType type: SocketEnginePacketType, withData datas: [Data], completion: (@Sendable () -> ())?) /// Call to stop polling and invalidate the URLSession. func stopPolling() @@ -237,7 +240,7 @@ extension SocketEnginePollable { /// - parameter withType: The type of message to send. /// - parameter withData: The data associated with this message. /// - parameter completion: Callback called on transport write completion. - public func sendPollMessage(_ message: String, withType type: SocketEnginePacketType, withData datas: [Data], completion: (() -> ())? = nil) { + public func sendPollMessage(_ message: String, withType type: SocketEnginePacketType, withData datas: [Data], completion: (@Sendable () -> ())? = nil) { DefaultSocketLogger.Logger.log("Sending poll: \(message) as type: \(type.rawValue)", type: "SocketEnginePolling") postWait.append((String(type.rawValue) + message, completion)) diff --git a/Source/SocketIO/Engine/SocketEngineSpec.swift b/Source/SocketIO/Engine/SocketEngineSpec.swift index fc0aa58b..2ea437c6 100644 --- a/Source/SocketIO/Engine/SocketEngineSpec.swift +++ b/Source/SocketIO/Engine/SocketEngineSpec.swift @@ -25,6 +25,9 @@ import Foundation import Starscream +#if os(Android) +import FoundationNetworking +#endif /// Specifies a SocketEngine. public protocol SocketEngineSpec: AnyObject { @@ -141,7 +144,7 @@ public protocol SocketEngineSpec: AnyObject { /// - parameter type: The type of this message. /// - parameter data: Any data that this message has. /// - parameter completion: Callback called on transport write completion. - func write(_ msg: String, withType type: SocketEnginePacketType, withData data: [Data], completion: (() -> ())?) + func write(_ msg: String, withType type: SocketEnginePacketType, withData data: [Data], completion: (@Sendable () -> ())?) } extension SocketEngineSpec { @@ -203,7 +206,7 @@ extension SocketEngineSpec { } /// Send an engine message (4) - func send(_ msg: String, withData datas: [Data], completion: (() -> ())? = nil) { + func send(_ msg: String, withData datas: [Data], completion: (@Sendable () -> ())? = nil) { write(msg, withType: .message, withData: datas, completion: completion) } } diff --git a/Source/SocketIO/Engine/SocketEngineWebsocket.swift b/Source/SocketIO/Engine/SocketEngineWebsocket.swift index 1453ce4a..7874bc80 100644 --- a/Source/SocketIO/Engine/SocketEngineWebsocket.swift +++ b/Source/SocketIO/Engine/SocketEngineWebsocket.swift @@ -46,7 +46,7 @@ public protocol SocketEngineWebsocket: SocketEngineSpec { func sendWebSocketMessage(_ str: String, withType type: SocketEnginePacketType, withData datas: [Data], - completion: (() -> ())?) + completion: (@Sendable () -> ())?) } // WebSocket methods @@ -68,7 +68,7 @@ extension SocketEngineWebsocket { public func sendWebSocketMessage(_ str: String, withType type: SocketEnginePacketType, withData data: [Data], - completion: (() -> ())? + completion: (@Sendable () -> ())? ) { DefaultSocketLogger.Logger.log("Sending ws: \(str) as type: \(type.rawValue)", type: "SocketEngineWebSocket") diff --git a/Source/SocketIO/Manager/SocketManager.swift b/Source/SocketIO/Manager/SocketManager.swift index d69aa11f..e433cde9 100644 --- a/Source/SocketIO/Manager/SocketManager.swift +++ b/Source/SocketIO/Manager/SocketManager.swift @@ -155,7 +155,6 @@ open class SocketManager: NSObject, SocketManagerSpec, SocketParsable, SocketDat /// /// - parameter socketURL: The url of the socket.io server. /// - parameter config: The config for this socket. - @objc public convenience init(socketURL: URL, config: [String: Any]?) { self.init(socketURL: socketURL, config: config?.toSocketConfiguration() ?? []) } diff --git a/Source/SocketIO/Util/SocketExtensions.swift b/Source/SocketIO/Util/SocketExtensions.swift index d44ea26d..77a6d667 100644 --- a/Source/SocketIO/Util/SocketExtensions.swift +++ b/Source/SocketIO/Util/SocketExtensions.swift @@ -24,6 +24,9 @@ import Foundation import Starscream +#if os(Android) +import FoundationNetworking +#endif enum JSONError : Error { case notArray diff --git a/Source/SocketIO/Util/SocketTypes.swift b/Source/SocketIO/Util/SocketTypes.swift index 7bb65173..1b86da10 100644 --- a/Source/SocketIO/Util/SocketTypes.swift +++ b/Source/SocketIO/Util/SocketTypes.swift @@ -74,10 +74,10 @@ public typealias AckCallback = ([Any]) -> () public typealias NormalCallback = ([Any], SocketAckEmitter) -> () /// A typealias for a queued POST -public typealias Post = (msg: String, completion: (() -> ())?) +public typealias Post = (msg: String, completion: (@Sendable () -> ())?) typealias JSON = [String: Any] -typealias Probe = (msg: String, type: SocketEnginePacketType, data: [Data], completion: (() -> ())?) +typealias Probe = (msg: String, type: SocketEnginePacketType, data: [Data], completion: (@Sendable () -> ())?) typealias ProbeWaitQueue = [Probe] enum Either { diff --git a/Tests/TestSocketIO/SocketAckManagerTest.swift b/Tests/TestSocketIO/SocketAckManagerTest.swift index 4f7f5b0c..47be3090 100644 --- a/Tests/TestSocketIO/SocketAckManagerTest.swift +++ b/Tests/TestSocketIO/SocketAckManagerTest.swift @@ -23,7 +23,8 @@ class SocketAckManagerTest : XCTestCase { ackManager.addAck(1, callback: callback) ackManager.executeAck(1, with: itemsArray) - waitForExpectations(timeout: 3.0, handler: nil) + let result = XCTWaiter().wait(for: [callbackExpection], timeout: 3) + XCTAssert(result == .completed) } func testManagerTimeoutAck() { @@ -45,6 +46,7 @@ class SocketAckManagerTest : XCTestCase { ackManager.addAck(1, callback: callback) ackManager.timeoutAck(1) - waitForExpectations(timeout: 0.2, handler: nil) + let result = XCTWaiter().wait(for: [callbackExpection], timeout: 0.2) + XCTAssert(result == .completed) } } diff --git a/Tests/TestSocketIO/SocketEngineTest.swift b/Tests/TestSocketIO/SocketEngineTest.swift index 9f44cc86..1a5a01ac 100644 --- a/Tests/TestSocketIO/SocketEngineTest.swift +++ b/Tests/TestSocketIO/SocketEngineTest.swift @@ -20,7 +20,8 @@ class SocketEngineTest: XCTestCase { engine.setConfigs([.version(.two)]) engine.parsePollingMessage("15:42[\"blankTest\"]") - waitForExpectations(timeout: 3, handler: nil) + let result = XCTWaiter().wait(for: [expect], timeout: 3) + XCTAssert(result == .completed) } func testBasicPollingMessage() { @@ -30,7 +31,8 @@ class SocketEngineTest: XCTestCase { } engine.parsePollingMessage("42[\"blankTest\"]") - waitForExpectations(timeout: 3, handler: nil) + let result = XCTWaiter().wait(for: [expect], timeout: 3) + XCTAssert(result == .completed) } func testTwoPacketsInOnePollTest() { @@ -50,7 +52,8 @@ class SocketEngineTest: XCTestCase { } engine.parsePollingMessage("42[\"blankTest\"]\u{1e}42[\"stringTest\",\"hello\"]") - waitForExpectations(timeout: 3, handler: nil) + let result = XCTWaiter().wait(for: [finalExpectation], timeout: 3) + XCTAssert(result == .completed) } func testEngineDoesErrorOnUnknownTransport() { @@ -63,7 +66,8 @@ class SocketEngineTest: XCTestCase { } engine.parseEngineMessage("{\"code\": 0, \"message\": \"Unknown transport\"}") - waitForExpectations(timeout: 3, handler: nil) + let result = XCTWaiter().wait(for: [finalExpectation], timeout: 3) + XCTAssert(result == .completed) } func testEngineDoesErrorOnUnknownMessage() { @@ -74,7 +78,8 @@ class SocketEngineTest: XCTestCase { } engine.parseEngineMessage("afafafda") - waitForExpectations(timeout: 3, handler: nil) + let result = XCTWaiter().wait(for: [finalExpectation], timeout: 3) + XCTAssert(result == .completed) } func testEngineDecodesUTF8Properly() { @@ -88,7 +93,8 @@ class SocketEngineTest: XCTestCase { let stringMessage = "42[\"stringTest\",\"lïne one\\nlīne \\rtwo𦅙𦅛\"]" engine.parsePollingMessage("\(stringMessage)") - waitForExpectations(timeout: 3, handler: nil) + let result = XCTWaiter().wait(for: [expect], timeout: 3) + XCTAssert(result == .completed) } func testEncodeURLProperly() { @@ -123,7 +129,8 @@ class SocketEngineTest: XCTestCase { engine.parseEngineMessage(packetString) engine.parseEngineMessage(b64String) - waitForExpectations(timeout: 3, handler: nil) + let result = XCTWaiter().wait(for: [expect], timeout: 3) + XCTAssert(result == .completed) } func testSettingExtraHeadersBeforeConnectSetsEngineExtraHeaders() { diff --git a/Tests/TestSocketIO/SocketMangerTest.swift b/Tests/TestSocketIO/SocketMangerTest.swift index b2dd5715..dd6f13bd 100644 --- a/Tests/TestSocketIO/SocketMangerTest.swift +++ b/Tests/TestSocketIO/SocketMangerTest.swift @@ -48,8 +48,11 @@ class SocketMangerTest : XCTestCase { func testManagerCallsConnect() { setUpSockets() - socket.expectations[ManagerExpectation.didConnectCalled] = expectation(description: "The manager should call connect on the default socket") - socket2.expectations[ManagerExpectation.didConnectCalled] = expectation(description: "The manager should call connect on the socket") + let didConnectCalled = expectation(description: "The manager should call connect on the default socket") + let didConnectCalled2 = expectation(description: "The manager should call connect on the socket") + + socket.expectations[ManagerExpectation.didConnectCalled] = didConnectCalled + socket2.expectations[ManagerExpectation.didConnectCalled] = didConnectCalled2 socket.connect() socket2.connect() @@ -57,7 +60,8 @@ class SocketMangerTest : XCTestCase { manager.fakeConnecting() manager.fakeConnecting(toNamespace: "/swift") - waitForExpectations(timeout: 0.3) + let result = XCTWaiter().wait(for: [didConnectCalled, didConnectCalled2], timeout: 0.3) + XCTAssert(result == .completed) } func testManagerDoesNotCallConnectWhenConnectingWithLessThanOneReconnect() { @@ -77,7 +81,8 @@ class SocketMangerTest : XCTestCase { manager.connect() - waitForExpectations(timeout: 0.3) + let result = XCTWaiter().wait(for: [expect], timeout: 0.3) + XCTAssert(result == .completed) } func testManagerCallConnectWhenConnectingAndMoreThanOneReconnect() { @@ -95,14 +100,18 @@ class SocketMangerTest : XCTestCase { manager.connect() - waitForExpectations(timeout: 0.8) + let result = XCTWaiter().wait(for: [expect], timeout: 0.8) + XCTAssert(result == .completed) } func testManagerCallsDisconnect() { setUpSockets() - socket.expectations[ManagerExpectation.didDisconnectCalled] = expectation(description: "The manager should call disconnect on the default socket") - socket2.expectations[ManagerExpectation.didDisconnectCalled] = expectation(description: "The manager should call disconnect on the socket") + let didDisconnectCalled = expectation(description: "The manager should call disconnect on the default socket") + let didDisconnectCalled2 = expectation(description: "The manager should call disconnect on the socket") + + socket.expectations[ManagerExpectation.didDisconnectCalled] = didDisconnectCalled + socket2.expectations[ManagerExpectation.didDisconnectCalled] = didDisconnectCalled2 socket2.on(clientEvent: .connect) {data, ack in self.manager.disconnect() @@ -115,7 +124,8 @@ class SocketMangerTest : XCTestCase { manager.fakeConnecting() manager.fakeConnecting(toNamespace: "/swift") - waitForExpectations(timeout: 0.3) + let result = XCTWaiter().wait(for: [didDisconnectCalled, didDisconnectCalled2], timeout: 0.3) + XCTAssert(result == .completed) } // func testManagerEmitAll() { @@ -245,3 +255,11 @@ public class TestSocket: SocketIOClient { expectations[ManagerExpectation.emitAllEventCalled] = nil } } + +#if os(Android) +extension DispatchQueue: @retroactive Equatable { + public static func == (lhs: DispatchQueue, rhs: DispatchQueue) -> Bool { + return lhs === rhs + } +} +#endif diff --git a/Tests/TestSocketIO/SocketSideEffectTest.swift b/Tests/TestSocketIO/SocketSideEffectTest.swift index 749a819a..6f1e8e0a 100644 --- a/Tests/TestSocketIO/SocketSideEffectTest.swift +++ b/Tests/TestSocketIO/SocketSideEffectTest.swift @@ -9,6 +9,10 @@ import XCTest @testable import SocketIO import Starscream +import Foundation +#if os(Android) +import FoundationNetworking +#endif class SocketSideEffectTest: XCTestCase { func testInitialCurrentAck() { @@ -40,7 +44,8 @@ class SocketSideEffectTest: XCTestCase { } manager.parseEngineMessage("30[\"hello world\"]") - waitForExpectations(timeout: 3, handler: nil) + let result = XCTWaiter().wait(for: [expect], timeout: 3) + XCTAssert(result == .completed) } func testHandleAckWithAckEmit() { @@ -53,7 +58,8 @@ class SocketSideEffectTest: XCTestCase { } manager.parseEngineMessage("30[\"hello world\"]") - waitForExpectations(timeout: 3, handler: nil) + let result = XCTWaiter().wait(for: [expect], timeout: 3) + XCTAssert(result == .completed) } func testHandleAck2() { @@ -65,7 +71,8 @@ class SocketSideEffectTest: XCTestCase { manager.parseEngineMessage("61-0[{\"_placeholder\":true,\"num\":0},{\"test\":true}]") manager.parseEngineBinaryData(Data()) - waitForExpectations(timeout: 3, handler: nil) + let result = XCTWaiter().wait(for: [expect], timeout: 3) + XCTAssert(result == .completed) } func testHandleEvent() { @@ -76,7 +83,8 @@ class SocketSideEffectTest: XCTestCase { } manager.parseEngineMessage("2[\"test\",\"hello world\"]") - waitForExpectations(timeout: 3, handler: nil) + let result = XCTWaiter().wait(for: [expect], timeout: 3) + XCTAssert(result == .completed) } func testHandleStringEventWithQuotes() { @@ -87,7 +95,8 @@ class SocketSideEffectTest: XCTestCase { } manager.parseEngineMessage("2[\"test\",\"\\\"hello world\\\"\"]") - waitForExpectations(timeout: 3, handler: nil) + let result = XCTWaiter().wait(for: [expect], timeout: 3) + XCTAssert(result == .completed) } func testHandleOnceEvent() { @@ -99,7 +108,8 @@ class SocketSideEffectTest: XCTestCase { } manager.parseEngineMessage("2[\"test\",\"hello world\"]") - waitForExpectations(timeout: 3, handler: nil) + let result = XCTWaiter().wait(for: [expect], timeout: 3) + XCTAssert(result == .completed) } func testHandleOnceClientEvent() { @@ -117,7 +127,8 @@ class SocketSideEffectTest: XCTestCase { self.manager.parseEngineMessage("0/") } - waitForExpectations(timeout: 3, handler: nil) + let result = XCTWaiter().wait(for: [expect], timeout: 3) + XCTAssert(result == .completed) } func testOffWithEvent() { @@ -155,7 +166,8 @@ class SocketSideEffectTest: XCTestCase { } manager.parseEngineMessage("4\"test error\"") - waitForExpectations(timeout: 3, handler: nil) + let result = XCTWaiter().wait(for: [expect], timeout: 3) + XCTAssert(result == .completed) } func testHandleBinaryEvent() { @@ -169,7 +181,8 @@ class SocketSideEffectTest: XCTestCase { manager.parseEngineMessage("51-[\"test\",{\"test\":{\"_placeholder\":true,\"num\":0}}]") manager.parseEngineBinaryData(data) - waitForExpectations(timeout: 3, handler: nil) + let result = XCTWaiter().wait(for: [expect], timeout: 3) + XCTAssert(result == .completed) } func testHandleMultipleBinaryEvent() { @@ -186,7 +199,8 @@ class SocketSideEffectTest: XCTestCase { manager.parseEngineMessage("52-[\"test\",{\"test\":{\"_placeholder\":true,\"num\":0},\"test2\":{\"_placeholder\":true,\"num\":1}}]") manager.parseEngineBinaryData(data) manager.parseEngineBinaryData(data2) - waitForExpectations(timeout: 3, handler: nil) + let result = XCTWaiter().wait(for: [expect], timeout: 3) + XCTAssert(result == .completed) } func testChangingStatusCallsStatusChangeHandler() { @@ -207,7 +221,8 @@ class SocketSideEffectTest: XCTestCase { socket.setTestStatus(statusChange) - waitForExpectations(timeout: 0.2) + let result = XCTWaiter().wait(for: [expect], timeout: 0.2) + XCTAssert(result == .completed) } func testOnClientEvent() { @@ -229,7 +244,8 @@ class SocketSideEffectTest: XCTestCase { socket.handleClientEvent(event, data: [closeReason]) - waitForExpectations(timeout: 0.2) + let result = XCTWaiter().wait(for: [expect], timeout: 0.2) + XCTAssert(result == .completed) } func testClientEventsAreBackwardsCompatible() { @@ -251,7 +267,8 @@ class SocketSideEffectTest: XCTestCase { socket.handleClientEvent(event, data: [closeReason]) - waitForExpectations(timeout: 0.2) + let result = XCTWaiter().wait(for: [expect], timeout: 0.2) + XCTAssert(result == .completed) } func testConnectTimesOutIfNotConnected() { @@ -265,7 +282,8 @@ class SocketSideEffectTest: XCTestCase { expect.fulfill() }) - waitForExpectations(timeout: 0.8) + let result = XCTWaiter().wait(for: [expect], timeout: 0.8) + XCTAssert(result == .completed) } func testConnectDoesNotTimeOutIfConnected() { @@ -287,7 +305,8 @@ class SocketSideEffectTest: XCTestCase { self.manager.parseEngineMessage("0/") } - waitForExpectations(timeout: 2) + let result = XCTWaiter().wait(for: [expect], timeout: 2) + XCTAssert(result == .completed) } func testClientCallsConnectOnEngineOpen() { @@ -309,7 +328,8 @@ class SocketSideEffectTest: XCTestCase { XCTFail("Should not call timeout handler if status is connected") }) - waitForExpectations(timeout: 2) + let result = XCTWaiter().wait(for: [expect], timeout: 2) + XCTAssert(result == .completed) } func testConnectIsCalledWithNamespace() { @@ -341,7 +361,8 @@ class SocketSideEffectTest: XCTestCase { self.manager.parseEngineMessage("0/swift") } - waitForExpectations(timeout: 2) + let result = XCTWaiter().wait(for: [expect], timeout: 2) + XCTAssert(result == .completed) } func testErrorInCustomSocketDataCallsErrorHandler() { @@ -361,7 +382,8 @@ class SocketSideEffectTest: XCTestCase { socket.emit("myEvent", ThrowingData()) - waitForExpectations(timeout: 0.2) + let result = XCTWaiter().wait(for: [expect], timeout: 0.2) + XCTAssert(result == .completed) } func testErrorInCustomSocketDataCallsErrorHandler_ack() { @@ -383,7 +405,8 @@ class SocketSideEffectTest: XCTestCase { XCTFail("Ack callback should not be called") }) - waitForExpectations(timeout: 0.2) + let result = XCTWaiter().wait(for: [expect], timeout: 0.2) + XCTAssert(result == .completed) } func testSettingConfigAfterInit() { @@ -424,7 +447,8 @@ class SocketSideEffectTest: XCTestCase { manager.engineDidSendPong() - waitForExpectations(timeout: 0.2) + let result = XCTWaiter().wait(for: [expect], timeout: 0.2) + XCTAssert(result == .completed) } func testClientCallsGotPongHandler() { @@ -436,7 +460,8 @@ class SocketSideEffectTest: XCTestCase { manager.engineDidReceivePing() - waitForExpectations(timeout: 0.2) + let result = XCTWaiter().wait(for: [expect], timeout: 0.2) + XCTAssert(result == .completed) } let data = "test".data(using: String.Encoding.utf8)! diff --git a/Tests/TestSocketIO/utils.swift b/Tests/TestSocketIO/utils.swift index 66d99acd..a96b0b05 100644 --- a/Tests/TestSocketIO/utils.swift +++ b/Tests/TestSocketIO/utils.swift @@ -6,7 +6,6 @@ import Foundation @testable import SocketIO public class OBjcUtils: NSObject { - @objc public static func setTestStatus(socket: SocketIOClient, status: SocketIOStatus) { socket.setTestStatus(status) } From d8b385cef3562e8bd77576f9b1f52c9a9e5d2345 Mon Sep 17 00:00:00 2001 From: Pierluigi Cifani Date: Thu, 9 Jan 2025 14:42:44 +0100 Subject: [PATCH 2/3] Almost there --- Package.swift | 5 +++-- Source/SocketIO/Ack/SocketAckEmitter.swift | 4 ++-- Source/SocketIO/Client/SocketIOClient.swift | 4 ++-- Source/SocketIO/Client/SocketIOClientSpec.swift | 2 +- Source/SocketIO/Engine/SocketEngine.swift | 2 +- Source/SocketIO/Engine/SocketEnginePacketType.swift | 2 +- Source/SocketIO/Engine/SocketEnginePollable.swift | 2 +- Source/SocketIO/Engine/SocketEngineSpec.swift | 2 +- Source/SocketIO/Manager/SocketManager.swift | 10 +++++----- Source/SocketIO/Util/SocketLogger.swift | 6 +++--- Tests/TestSocketIO/SocketMangerTest.swift | 4 ++-- Tests/TestSocketIO/SocketParserTest.swift | 5 ++++- Tests/TestSocketIO/SocketSideEffectTest.swift | 4 ++-- 13 files changed, 28 insertions(+), 24 deletions(-) diff --git a/Package.swift b/Package.swift index 8ea79831..a0e1190b 100644 --- a/Package.swift +++ b/Package.swift @@ -1,4 +1,4 @@ -// swift-tools-version:5.9 +// swift-tools-version:6.0 import PackageDescription @@ -19,5 +19,6 @@ let package = Package( targets: [ .target(name: "SocketIO", dependencies: ["Starscream"]), .testTarget(name: "TestSocketIO", dependencies: ["SocketIO"]), - ] + ], + swiftLanguageModes: [.v6] ) diff --git a/Source/SocketIO/Ack/SocketAckEmitter.swift b/Source/SocketIO/Ack/SocketAckEmitter.swift index dce21d3f..8f413f7c 100644 --- a/Source/SocketIO/Ack/SocketAckEmitter.swift +++ b/Source/SocketIO/Ack/SocketAckEmitter.swift @@ -132,11 +132,11 @@ public final class OnAckCallback: NSObject { socket.emit(items, ack: ackNumber, binary: binary) guard seconds != 0 else { return } - + let _ackNumber = ackNumber socket.manager?.handleQueue.asyncAfter(deadline: DispatchTime.now() + seconds) {[weak socket] in guard let socket = socket else { return } - socket.ackHandlers.timeoutAck(self.ackNumber) + socket.ackHandlers.timeoutAck(_ackNumber) } } diff --git a/Source/SocketIO/Client/SocketIOClient.swift b/Source/SocketIO/Client/SocketIOClient.swift index 89cbca8b..77725133 100644 --- a/Source/SocketIO/Client/SocketIOClient.swift +++ b/Source/SocketIO/Client/SocketIOClient.swift @@ -40,7 +40,7 @@ import Foundation /// /// **NOTE**: The client is not thread/queue safe, all interaction with the socket should be done on the `manager.handleQueue` /// -open class SocketIOClient: NSObject, SocketIOClientSpec { +open class SocketIOClient: NSObject, SocketIOClientSpec, @unchecked Sendable { // MARK: Properties /// The namespace that this socket is currently connected to. @@ -122,7 +122,7 @@ open class SocketIOClient: NSObject, SocketIOClientSpec { /// - parameter timeoutAfter: The number of seconds after which if we are not connected we assume the connection /// has failed. Pass 0 to never timeout. /// - parameter handler: The handler to call when the client fails to connect. - open func connect(withPayload payload: [String: Any]? = nil, timeoutAfter: Double, withHandler handler: (() -> ())?) { + open func connect(withPayload payload: [String: Any]? = nil, timeoutAfter: Double, withHandler handler: (@Sendable () -> ())?) { assert(timeoutAfter >= 0, "Invalid timeout: \(timeoutAfter)") guard let manager = self.manager, status != .connected else { diff --git a/Source/SocketIO/Client/SocketIOClientSpec.swift b/Source/SocketIO/Client/SocketIOClientSpec.swift index ba133739..85757e3a 100644 --- a/Source/SocketIO/Client/SocketIOClientSpec.swift +++ b/Source/SocketIO/Client/SocketIOClientSpec.swift @@ -77,7 +77,7 @@ public protocol SocketIOClientSpec : AnyObject { /// - parameter timeoutAfter: The number of seconds after which if we are not connected we assume the connection /// has failed. Pass 0 to never timeout. /// - parameter handler: The handler to call when the client fails to connect. - func connect(withPayload payload: [String: Any]?, timeoutAfter: Double, withHandler handler: (() -> ())?) + func connect(withPayload payload: [String: Any]?, timeoutAfter: Double, withHandler handler: (@Sendable() -> ())?) /// Called when the client connects to a namespace. If the client was created with a namespace upfront, /// then this is only called when the client connects to that namespace. diff --git a/Source/SocketIO/Engine/SocketEngine.swift b/Source/SocketIO/Engine/SocketEngine.swift index 69fca81e..44e48167 100644 --- a/Source/SocketIO/Engine/SocketEngine.swift +++ b/Source/SocketIO/Engine/SocketEngine.swift @@ -32,7 +32,7 @@ import FoundationNetworking /// The class that handles the engine.io protocol and transports. /// See `SocketEnginePollable` and `SocketEngineWebsocket` for transport specific methods. open class SocketEngine: NSObject, WebSocketDelegate, URLSessionDelegate, - SocketEnginePollable, SocketEngineWebsocket, ConfigSettable { + SocketEnginePollable, SocketEngineWebsocket, ConfigSettable, @unchecked Sendable { // MARK: Properties diff --git a/Source/SocketIO/Engine/SocketEnginePacketType.swift b/Source/SocketIO/Engine/SocketEnginePacketType.swift index 8da20080..001d7bb3 100644 --- a/Source/SocketIO/Engine/SocketEnginePacketType.swift +++ b/Source/SocketIO/Engine/SocketEnginePacketType.swift @@ -26,7 +26,7 @@ import Foundation /// Represents the type of engine.io packet types. -public enum SocketEnginePacketType: Int { +public enum SocketEnginePacketType: Int, Sendable { /// Open message. case open diff --git a/Source/SocketIO/Engine/SocketEnginePollable.swift b/Source/SocketIO/Engine/SocketEnginePollable.swift index 4d8e4661..22de079e 100644 --- a/Source/SocketIO/Engine/SocketEnginePollable.swift +++ b/Source/SocketIO/Engine/SocketEnginePollable.swift @@ -119,7 +119,7 @@ extension SocketEnginePollable { doLongPoll(for: req) } - func doRequest(for req: URLRequest, callbackWith callback: @escaping (Data?, URLResponse?, Error?) -> ()) { + func doRequest(for req: URLRequest, callbackWith callback: @escaping (@Sendable(Data?, URLResponse?, Error?) -> ())) { guard polling && !closed && !invalidated && !fastUpgrade else { return } DefaultSocketLogger.Logger.log("Doing polling \(req.httpMethod ?? "") \(req)", type: "SocketEnginePolling") diff --git a/Source/SocketIO/Engine/SocketEngineSpec.swift b/Source/SocketIO/Engine/SocketEngineSpec.swift index 2ea437c6..df059510 100644 --- a/Source/SocketIO/Engine/SocketEngineSpec.swift +++ b/Source/SocketIO/Engine/SocketEngineSpec.swift @@ -30,7 +30,7 @@ import FoundationNetworking #endif /// Specifies a SocketEngine. -public protocol SocketEngineSpec: AnyObject { +public protocol SocketEngineSpec: AnyObject, Sendable { // MARK: Properties /// The client for this engine. diff --git a/Source/SocketIO/Manager/SocketManager.swift b/Source/SocketIO/Manager/SocketManager.swift index e433cde9..0c41b881 100644 --- a/Source/SocketIO/Manager/SocketManager.swift +++ b/Source/SocketIO/Manager/SocketManager.swift @@ -45,7 +45,7 @@ import Foundation /// /// **NOTE**: The manager is not thread/queue safe, all interaction with the manager should be done on the `handleQueue` /// -open class SocketManager: NSObject, SocketManagerSpec, SocketParsable, SocketDataBufferable, ConfigSettable { +open class SocketManager: NSObject, SocketManagerSpec, SocketParsable, SocketDataBufferable, ConfigSettable, @unchecked Sendable { private static let logType = "SocketManager" // MARK: Properties @@ -95,16 +95,16 @@ open class SocketManager: NSObject, SocketManagerSpec, SocketParsable, SocketDat public var nsps = [String: SocketIOClient]() /// If `true`, this client will try and reconnect on any disconnects. - public var reconnects = true + public var reconnects: Bool = true /// The minimum number of seconds to wait before attempting to reconnect. - public var reconnectWait = 10 + public var reconnectWait: Int = 10 /// The maximum number of seconds to wait before attempting to reconnect. - public var reconnectWaitMax = 30 + public var reconnectWaitMax: Int = 30 /// The randomization factor for calculating reconnect jitter. - public var randomizationFactor = 0.5 + public var randomizationFactor: Double = 0.5 /// The status of this manager. public private(set) var status: SocketIOStatus = .notConnected { diff --git a/Source/SocketIO/Util/SocketLogger.swift b/Source/SocketIO/Util/SocketLogger.swift index ccb92be4..75d18392 100644 --- a/Source/SocketIO/Util/SocketLogger.swift +++ b/Source/SocketIO/Util/SocketLogger.swift @@ -68,8 +68,8 @@ public extension SocketLogger { } } -class DefaultSocketLogger : SocketLogger { - static var Logger: SocketLogger = DefaultSocketLogger() +final class DefaultSocketLogger : SocketLogger, Sendable { + static nonisolated(unsafe) var Logger: SocketLogger = DefaultSocketLogger() - var log = false + nonisolated(unsafe) var log = false } diff --git a/Tests/TestSocketIO/SocketMangerTest.swift b/Tests/TestSocketIO/SocketMangerTest.swift index dd6f13bd..ae5f1219 100644 --- a/Tests/TestSocketIO/SocketMangerTest.swift +++ b/Tests/TestSocketIO/SocketMangerTest.swift @@ -201,7 +201,7 @@ public enum ManagerExpectation: String { case emitAllEventCalled } -public class TestManager: SocketManager { +public class TestManager: SocketManager, @unchecked Sendable { public func setCurrentReconnect(currentReconnect: Int) { self.currentReconnectAttempt = currentReconnect } @@ -233,7 +233,7 @@ public class TestManager: SocketManager { } } -public class TestSocket: SocketIOClient { +public class TestSocket: SocketIOClient, @unchecked Sendable { public var expectations = [ManagerExpectation: XCTestExpectation]() public override func didConnect(toNamespace nsp: String, payload: [String: Any]?) { diff --git a/Tests/TestSocketIO/SocketParserTest.swift b/Tests/TestSocketIO/SocketParserTest.swift index 2ac13014..308c31b7 100644 --- a/Tests/TestSocketIO/SocketParserTest.swift +++ b/Tests/TestSocketIO/SocketParserTest.swift @@ -127,7 +127,7 @@ class SocketParserTest: XCTestCase { let testManager = SocketManager(socketURL: URL(string: "http://localhost/")!) //Format key: message; namespace-data-binary-id - static let packetTypes: [String: (String, [Any], [Data], Int)] = [ + static let packetTypes: [String: (String, [any Sendable], [Data], Int)] = [ "0": ("/", [], [], -1), "1": ("/", [], [], -1), "25[\"test\"]": ("/", ["test"], [], 5), "2[\"test\",\"~~0\"]": ("/", ["test", "~~0"], [], -1), @@ -145,3 +145,6 @@ class SocketParserTest: XCTestCase { "4[1, \"hello\"]": ("/", [1, "hello"], [], -1) ] } + +extension NSArray: @retroactive @unchecked Sendable {} +extension NSDictionary: @retroactive @unchecked Sendable {} diff --git a/Tests/TestSocketIO/SocketSideEffectTest.swift b/Tests/TestSocketIO/SocketSideEffectTest.swift index 6f1e8e0a..91bac76d 100644 --- a/Tests/TestSocketIO/SocketSideEffectTest.swift +++ b/Tests/TestSocketIO/SocketSideEffectTest.swift @@ -14,7 +14,7 @@ import Foundation import FoundationNetworking #endif -class SocketSideEffectTest: XCTestCase { +class SocketSideEffectTest: XCTestCase, @unchecked Sendable { func testInitialCurrentAck() { XCTAssertEqual(socket.currentAck, -1) } @@ -490,7 +490,7 @@ struct ThrowingData: SocketData { } -class TestEngine: SocketEngineSpec { +class TestEngine: SocketEngineSpec, @unchecked Sendable { weak var client: SocketEngineClient? private(set) var closed = false private(set) var compress = false From 9a424e318b914412713fdc876ed2769562e3e812 Mon Sep 17 00:00:00 2001 From: Pierluigi Cifani Date: Thu, 9 Jan 2025 14:47:46 +0100 Subject: [PATCH 3/3] And now all the tests pass! --- Source/SocketIO/Manager/SocketManager.swift | 13 +++++++------ Tests/TestSocketIO/SocketMangerTest.swift | 3 ++- 2 files changed, 9 insertions(+), 7 deletions(-) diff --git a/Source/SocketIO/Manager/SocketManager.swift b/Source/SocketIO/Manager/SocketManager.swift index 0c41b881..4122aa27 100644 --- a/Source/SocketIO/Manager/SocketManager.swift +++ b/Source/SocketIO/Manager/SocketManager.swift @@ -521,16 +521,17 @@ open class SocketManager: NSObject, SocketManagerSpec, SocketParsable, SocketDat func reconnectInterval(attempts: Int) -> Double { // apply exponential factor - let backoffFactor = pow(1.5, attempts) - let interval = Double(reconnectWait) * Double(truncating: backoffFactor as NSNumber) - // add in a random factor smooth thundering herds + let backoffFactor = pow(1.5, Double(attempts)) + let interval = Double(reconnectWait) * backoffFactor + // add in a random factor to smooth thundering herds let rand = Double.random(in: 0 ..< 1) - let randomFactor = rand * randomizationFactor * Double(truncating: interval as NSNumber) + let randomFactor = rand * randomizationFactor * interval // add in random factor, and clamp to min and max values let combined = interval + randomFactor - return Double(fmax(Double(reconnectWait), fmin(combined, Double(reconnectWaitMax)))) + let value = Double(max(Double(reconnectWait), min(combined, Double(reconnectWaitMax)))) + return value } - + /// Sets manager specific configs. /// /// parameter config: The configs that should be set. diff --git a/Tests/TestSocketIO/SocketMangerTest.swift b/Tests/TestSocketIO/SocketMangerTest.swift index ae5f1219..21009950 100644 --- a/Tests/TestSocketIO/SocketMangerTest.swift +++ b/Tests/TestSocketIO/SocketMangerTest.swift @@ -257,9 +257,10 @@ public class TestSocket: SocketIOClient, @unchecked Sendable { } #if os(Android) +/// I know comparing labels is stupid, but it's enough for now extension DispatchQueue: @retroactive Equatable { public static func == (lhs: DispatchQueue, rhs: DispatchQueue) -> Bool { - return lhs === rhs + return lhs.label == rhs.label } } #endif