Skip to content

Commit 00aaea9

Browse files
committed
Merge branch 'development'
* development: update readme and changelog for new version add changelog entry for exponential backoff config setting and test updates for reconnectWaitMax and randomizationFactor add configurable exponential backoff Fix #1147 Bump Starscream to 3.0.6 Update to Swift 4.2
2 parents f7ef126 + 6a3d82c commit 00aaea9

16 files changed

+127
-16
lines changed

Diff for: .swift-version

-1
This file was deleted.

Diff for: CHANGELOG.md

+6-1
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,9 @@
1+
# v14.0.0
2+
3+
- Minimum version of the client is now Swift 4.2.
4+
- Add exponential backoff for reconnects, with `reconnectWaitMax` and `randomizationFactor` options [#1149](https://github.com/socketio/socket.io-client-swift/pull/1149)
5+
- `statusChange` event's data format adds a second value, the raw value of the status. This is for use in Objective-C. [#1147](https://github.com/socketio/socket.io-client-swift/issues/1147)
6+
17
# v13.4.0
28

39
- Add emits with write completion handlers. [#1096](https://github.com/socketio/socket.io-client-swift/issues/1096)
@@ -69,4 +75,3 @@ Important API changes
6975
- Adds `.sentPing` and `.gotPong` client events for tracking ping/pongs.
7076
- Makes the framework a single target.
7177
- Updates Starscream to 3.0
72-

Diff for: Package.swift

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// swift-tools-version:4.0
1+
// swift-tools-version:4.2
22

33
import PackageDescription
44

Diff for: README.md

+5-5
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,7 @@ SocketIOClient* socket = manager.defaultSocket;
6363
## FAQS
6464
Checkout the [FAQs](https://nuclearace.github.io/Socket.IO-Client-Swift/faq.html) for commonly asked questions.
6565
66-
Checkout the [12to13](https://nuclearace.github.io/Socket.IO-Client-Swift/12to13.html) guide for migrating to v13.
66+
Checkout the [12to13](https://nuclearace.github.io/Socket.IO-Client-Swift/12to13.html) guide for migrating to v13+ from v12 below.
6767
6868
6969
## Installation
@@ -76,7 +76,7 @@ If you need Swift 3.x use v11.1.3.
7676
### Swift Package Manager
7777
Add the project as a dependency to your Package.swift:
7878
```swift
79-
// swift-tools-version:4.0
79+
// swift-tools-version:4.2
8080
8181
import PackageDescription
8282
@@ -86,7 +86,7 @@ let package = Package(
8686
.executable(name: "socket.io-test", targets: ["YourTargetName"])
8787
],
8888
dependencies: [
89-
.package(url: "https://github.com/socketio/socket.io-client-swift", .upToNextMinor(from: "13.3.0"))
89+
.package(url: "https://github.com/socketio/socket.io-client-swift", .upToNextMinor(from: "14.0.0"))
9090
],
9191
targets: [
9292
.target(name: "YourTargetName", dependencies: ["SocketIO"], path: "./Path/To/Your/Sources")
@@ -99,7 +99,7 @@ Then import `import SocketIO`.
9999
### Carthage
100100
Add this line to your `Cartfile`:
101101
```
102-
github "socketio/socket.io-client-swift" ~> 13.3.0
102+
github "socketio/socket.io-client-swift" ~> 14.0.0
103103
```
104104

105105
Run `carthage update --platform ios,macosx`.
@@ -113,7 +113,7 @@ Create `Podfile` and add `pod 'Socket.IO-Client-Swift'`:
113113
use_frameworks!
114114

115115
target 'YourApp' do
116-
pod 'Socket.IO-Client-Swift', '~> 13.3.0'
116+
pod 'Socket.IO-Client-Swift', '~> 14.0.0'
117117
end
118118
```
119119

Diff for: Socket.IO-Client-Swift.podspec

+3-1
Original file line numberDiff line numberDiff line change
@@ -21,9 +21,11 @@ Pod::Spec.new do |s|
2121
:tag => 'v13.4.0',
2222
:submodules => true
2323
}
24+
25+
s.swift_version = "4.2"
2426
s.pod_target_xcconfig = {
2527
'SWIFT_VERSION' => '4.0'
2628
}
2729
s.source_files = "Source/SocketIO/**/*.swift", "Source/SocketIO/*.swift"
28-
s.dependency "Starscream", "~> 3.0.2"
30+
s.dependency "Starscream", "~> 3.0.6"
2931
end

Diff for: Socket.IO-Client-Swift.xcodeproj/project.pbxproj

+4
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
/* Begin PBXBuildFile section */
1010
1C6572803D7E252A77A86E5F /* SocketManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1C65763817782DFAC67BE05C /* SocketManager.swift */; };
1111
1C6573B22DC9423CDFC32F05 /* SocketRawView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1C657533E849FC3E4342C602 /* SocketRawView.swift */; };
12+
1C657CDE5D510E8E2E573E39 /* utils.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1C6577B639C34EE1C8829D9A /* utils.swift */; };
1213
1C657FBB3F670261780FD72E /* SocketManagerSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1C6574AF9687A213814753E4 /* SocketManagerSpec.swift */; };
1314
1C686BE21F869AFD007D8627 /* SocketIOClientConfigurationTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1C686BD21F869AF1007D8627 /* SocketIOClientConfigurationTest.swift */; };
1415
1C686BE31F869AFD007D8627 /* SocketEngineTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1C686BD31F869AF1007D8627 /* SocketEngineTest.swift */; };
@@ -64,6 +65,7 @@
6465
1C6574AF9687A213814753E4 /* SocketManagerSpec.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SocketManagerSpec.swift; sourceTree = "<group>"; };
6566
1C657533E849FC3E4342C602 /* SocketRawView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SocketRawView.swift; sourceTree = "<group>"; };
6667
1C65763817782DFAC67BE05C /* SocketManager.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SocketManager.swift; sourceTree = "<group>"; };
68+
1C6577B639C34EE1C8829D9A /* utils.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = utils.swift; sourceTree = "<group>"; };
6769
1C686BD21F869AF1007D8627 /* SocketIOClientConfigurationTest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SocketIOClientConfigurationTest.swift; sourceTree = "<group>"; };
6870
1C686BD31F869AF1007D8627 /* SocketEngineTest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SocketEngineTest.swift; sourceTree = "<group>"; };
6971
1C686BD41F869AF1007D8627 /* SocketSideEffectTest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SocketSideEffectTest.swift; sourceTree = "<group>"; };
@@ -159,6 +161,7 @@
159161
1C686BD71F869AF1007D8627 /* SocketParserTest.swift */,
160162
1C686BD81F869AF1007D8627 /* SocketNamespacePacketTest.swift */,
161163
DD52BBAC5FAA7730D32CD5BF /* SocketMangerTest.swift */,
164+
1C6577B639C34EE1C8829D9A /* utils.swift */,
162165
);
163166
name = TestSocketIO;
164167
path = Tests/TestSocketIO;
@@ -501,6 +504,7 @@
501504
1C686BE81F869AFD007D8627 /* SocketNamespacePacketTest.swift in Sources */,
502505
DD52BCCD25EFA76E0F9B313C /* SocketMangerTest.swift in Sources */,
503506
DD52B53F2609D91A683DFCDD /* ManagerObjectiveCTest.m in Sources */,
507+
1C657CDE5D510E8E2E573E39 /* utils.swift in Sources */,
504508
);
505509
runOnlyForDeploymentPostprocessing = 0;
506510
};

Diff for: Source/SocketIO/Client/SocketIOClient.swift

+1-1
Original file line numberDiff line numberDiff line change
@@ -83,7 +83,7 @@ open class SocketIOClient : NSObject, SocketIOClientSpec {
8383
@objc
8484
public private(set) var status = SocketIOStatus.notConnected {
8585
didSet {
86-
handleClientEvent(.statusChange, data: [status])
86+
handleClientEvent(.statusChange, data: [status, status.rawValue])
8787
}
8888
}
8989

Diff for: Source/SocketIO/Client/SocketIOClientOption.swift

+15-1
Original file line numberDiff line numberDiff line change
@@ -75,8 +75,14 @@ public enum SocketIOClientOption : ClientOption {
7575
/// The number of times to try and reconnect before giving up. Pass `-1` to [never give up](https://www.youtube.com/watch?v=dQw4w9WgXcQ).
7676
case reconnectAttempts(Int)
7777

78-
/// The number of seconds to wait before reconnect attempts.
78+
/// The minimum number of seconds to wait before reconnect attempts.
7979
case reconnectWait(Int)
80+
81+
/// The maximum number of seconds to wait before reconnect attempts.
82+
case reconnectWaitMax(Int)
83+
84+
/// The randomization factor for calculating reconnect jitter.
85+
case randomizationFactor(Double)
8086

8187
/// Set `true` if your server is using secure transports.
8288
case secure(Bool)
@@ -125,6 +131,10 @@ public enum SocketIOClientOption : ClientOption {
125131
description = "reconnectAttempts"
126132
case .reconnectWait:
127133
description = "reconnectWait"
134+
case .reconnectWaitMax:
135+
description = "reconnectWaitMax"
136+
case .randomizationFactor:
137+
description = "randomizationFactor"
128138
case .secure:
129139
description = "secure"
130140
case .selfSigned:
@@ -170,6 +180,10 @@ public enum SocketIOClientOption : ClientOption {
170180
value = attempts
171181
case let .reconnectWait(wait):
172182
value = wait
183+
case let .reconnectWaitMax(wait):
184+
value = wait
185+
case let .randomizationFactor(factor):
186+
value = factor
173187
case let .secure(secure):
174188
value = secure
175189
case let .security(security):

Diff for: Source/SocketIO/Client/SocketIOClientSpec.swift

+3
Original file line numberDiff line numberDiff line change
@@ -327,6 +327,9 @@ public enum SocketClientEvent : String {
327327

328328
/// Emitted every time there is a change in the client's status.
329329
///
330+
/// The payload for data is [SocketIOClientStatus, Int]. Where the second item is the raw value. Use the second one
331+
/// if you are working in Objective-C.
332+
///
330333
/// Usage:
331334
///
332335
/// ```swift

Diff for: Source/SocketIO/Manager/SocketManager.swift

+26-2
Original file line numberDiff line numberDiff line change
@@ -97,9 +97,15 @@ open class SocketManager : NSObject, SocketManagerSpec, SocketParsable, SocketDa
9797
/// If `true`, this client will try and reconnect on any disconnects.
9898
public var reconnects = true
9999

100-
/// The number of seconds to wait before attempting to reconnect.
100+
/// The minimum number of seconds to wait before attempting to reconnect.
101101
public var reconnectWait = 10
102102

103+
/// The maximum number of seconds to wait before attempting to reconnect.
104+
public var reconnectWaitMax = 30
105+
106+
/// The randomization factor for calculating reconnect jitter.
107+
public var randomizationFactor = 0.5
108+
103109
/// The status of this manager.
104110
public private(set) var status: SocketIOStatus = .notConnected {
105111
didSet {
@@ -474,7 +480,21 @@ open class SocketManager : NSObject, SocketManagerSpec, SocketParsable, SocketDa
474480
currentReconnectAttempt += 1
475481
connect()
476482

477-
handleQueue.asyncAfter(deadline: DispatchTime.now() + Double(reconnectWait), execute: _tryReconnect)
483+
let interval = reconnectInterval(attempts: currentReconnectAttempt)
484+
DefaultSocketLogger.Logger.log("Scheduling reconnect in \(interval)s", type: SocketManager.logType)
485+
handleQueue.asyncAfter(deadline: DispatchTime.now() + interval, execute: _tryReconnect)
486+
}
487+
488+
func reconnectInterval(attempts: Int) -> Double {
489+
// apply exponential factor
490+
let backoffFactor = pow(1.5, attempts)
491+
let interval = Double(reconnectWait) * Double(truncating: backoffFactor as NSNumber)
492+
// add in a random factor smooth thundering herds
493+
let rand = Double.random(in: 0 ..< 1)
494+
let randomFactor = rand * randomizationFactor * Double(truncating: interval as NSNumber)
495+
// add in random factor, and clamp to min and max values
496+
let combined = interval + randomFactor
497+
return Double(fmax(Double(reconnectWait), fmin(combined, Double(reconnectWaitMax))))
478498
}
479499

480500
/// Sets manager specific configs.
@@ -493,6 +513,10 @@ open class SocketManager : NSObject, SocketManagerSpec, SocketParsable, SocketDa
493513
self.reconnectAttempts = attempts
494514
case let .reconnectWait(wait):
495515
reconnectWait = abs(wait)
516+
case let .reconnectWaitMax(wait):
517+
reconnectWaitMax = abs(wait)
518+
case let .randomizationFactor(factor):
519+
randomizationFactor = factor
496520
case let .log(log):
497521
DefaultSocketLogger.Logger.log = log
498522
case let .logger(logger):

Diff for: Source/SocketIO/Manager/SocketManagerSpec.swift

+7-1
Original file line numberDiff line numberDiff line change
@@ -69,8 +69,14 @@ public protocol SocketManagerSpec : AnyObject, SocketEngineClient {
6969
/// If `true`, this manager will try and reconnect on any disconnects.
7070
var reconnects: Bool { get set }
7171

72-
/// The number of seconds to wait before attempting to reconnect.
72+
/// The minimum number of seconds to wait before attempting to reconnect.
7373
var reconnectWait: Int { get set }
74+
75+
/// The maximum number of seconds to wait before attempting to reconnect.
76+
var reconnectWaitMax: Int { get set }
77+
78+
/// The randomization factor for calculating reconnect jitter.
79+
var randomizationFactor: Double { get set }
7480

7581
/// The URL of the socket.io server.
7682
var socketURL: URL { get }

Diff for: Source/SocketIO/Util/SocketExtensions.swift

+4
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,10 @@ extension Dictionary where Key == String, Value == Any {
7171
return .reconnectAttempts(attempts)
7272
case let ("reconnectWait", wait as Int):
7373
return .reconnectWait(wait)
74+
case let ("reconnectWaitMax", wait as Int):
75+
return .reconnectWaitMax(wait)
76+
case let ("randomizationFactor", factor as Double):
77+
return .randomizationFactor(factor)
7478
case let ("secure", secure as Bool):
7579
return .secure(secure)
7680
case let ("security", security as SSLSecurity):

Diff for: Tests/TestSocketIO/SocketMangerTest.swift

+21
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,8 @@ class SocketMangerTest : XCTestCase {
1515
XCTAssertEqual(manager.handleQueue, DispatchQueue.main)
1616
XCTAssertTrue(manager.reconnects)
1717
XCTAssertEqual(manager.reconnectWait, 10)
18+
XCTAssertEqual(manager.reconnectWaitMax, 30)
19+
XCTAssertEqual(manager.randomizationFactor, 0.5)
1820
XCTAssertEqual(manager.status, .notConnected)
1921
}
2022

@@ -27,6 +29,21 @@ class SocketMangerTest : XCTestCase {
2729

2830
XCTAssertEqual(manager.config.first!, .secure(true))
2931
}
32+
33+
func testBackoffIntervalCalulation() {
34+
XCTAssertLessThanOrEqual(manager.reconnectInterval(attempts: -1), Double(manager.reconnectWaitMax))
35+
XCTAssertLessThanOrEqual(manager.reconnectInterval(attempts: 0), 15)
36+
XCTAssertLessThanOrEqual(manager.reconnectInterval(attempts: 1), 22.5)
37+
XCTAssertLessThanOrEqual(manager.reconnectInterval(attempts: 2), 33.75)
38+
XCTAssertLessThanOrEqual(manager.reconnectInterval(attempts: 50), Double(manager.reconnectWaitMax))
39+
XCTAssertLessThanOrEqual(manager.reconnectInterval(attempts: 10000), Double(manager.reconnectWaitMax))
40+
41+
XCTAssertGreaterThanOrEqual(manager.reconnectInterval(attempts: -1), Double(manager.reconnectWait))
42+
XCTAssertGreaterThanOrEqual(manager.reconnectInterval(attempts: 0), Double(manager.reconnectWait))
43+
XCTAssertGreaterThanOrEqual(manager.reconnectInterval(attempts: 1), 15)
44+
XCTAssertGreaterThanOrEqual(manager.reconnectInterval(attempts: 2), 22.5)
45+
XCTAssertGreaterThanOrEqual(manager.reconnectInterval(attempts: 10000), Double(manager.reconnectWait))
46+
}
3047

3148
func testManagerCallsConnect() {
3249
setUpSockets()
@@ -90,13 +107,17 @@ class SocketMangerTest : XCTestCase {
90107
.forceNew(true),
91108
.reconnects(false),
92109
.reconnectWait(5),
110+
.reconnectWaitMax(5),
111+
.randomizationFactor(0.7),
93112
.reconnectAttempts(5)
94113
])
95114

96115
XCTAssertEqual(manager.handleQueue, queue)
97116
XCTAssertTrue(manager.forceNew)
98117
XCTAssertFalse(manager.reconnects)
99118
XCTAssertEqual(manager.reconnectWait, 5)
119+
XCTAssertEqual(manager.reconnectWaitMax, 5)
120+
XCTAssertEqual(manager.randomizationFactor, 0.7)
100121
XCTAssertEqual(manager.reconnectAttempts, 5)
101122
}
102123

Diff for: Tests/TestSocketIO/utils.swift

+13
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
//
2+
// Created by Erik Little on 2019-01-11.
3+
//
4+
5+
import Foundation
6+
@testable import SocketIO
7+
8+
public class OBjcUtils: NSObject {
9+
@objc
10+
public static func setTestStatus(socket: SocketIOClient, status: SocketIOStatus) {
11+
socket.setTestStatus(status)
12+
}
13+
}

Diff for: Tests/TestSocketIOObjc/ManagerObjectiveCTest.m

+2
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,8 @@ - (void)testManagerProperties {
3434
XCTAssertEqual(self.manager.handleQueue, dispatch_get_main_queue());
3535
XCTAssertTrue(self.manager.reconnects);
3636
XCTAssertEqual(self.manager.reconnectWait, 10);
37+
XCTAssertEqual(self.manager.reconnectWaitMax, 30);
38+
XCTAssertEqual(self.manager.randomizationFactor, 0.5);
3739
XCTAssertEqual(self.manager.status, SocketIOStatusNotConnected);
3840
}
3941

Diff for: Tests/TestSocketIOObjc/SocketObjectiveCTest.m

+16-2
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
// Merely tests whether the Objective-C api breaks
88
//
99

10+
#import "SocketIO_Tests-Swift.h"
1011
#import "SocketObjectiveCTest.h"
1112

1213
@import Dispatch;
@@ -73,11 +74,11 @@ - (void)testEmitWriteCompletionSyntax {
7374

7475
- (void)testEmitWriteCompletion {
7576
XCTestExpectation* expect = [self expectationWithDescription:@"Write completion should be called"];
76-
77+
7778
[self.socket emit:@"testEmit" with:@[@YES] completion:^{
7879
[expect fulfill];
7980
}];
80-
81+
8182
[self waitForExpectationsWithTimeout:0.3 handler:nil];
8283
}
8384

@@ -98,6 +99,19 @@ - (void)testSSLSecurity {
9899
sec = nil;
99100
}
100101

102+
- (void)testStatusChangeHandler {
103+
XCTestExpectation* expect = [self expectationWithDescription:@"statusChange should be correctly called"];
104+
105+
[self.socket on:@"statusChange" callback:^(NSArray* data, SocketAckEmitter* ack) {
106+
XCTAssertTrue([data[1] integerValue] == SocketIOStatusConnecting);
107+
[expect fulfill];
108+
}];
109+
110+
[OBjcUtils setTestStatusWithSocket:self.socket status:SocketIOStatusConnecting];
111+
112+
[self waitForExpectationsWithTimeout:0.3 handler:nil];
113+
}
114+
101115
- (void)setUp {
102116
[super setUp];
103117
NSURL* url = [[NSURL alloc] initWithString:@"http://localhost"];

0 commit comments

Comments
 (0)