Skip to content

Commit

Permalink
Merge branch 'development'
Browse files Browse the repository at this point in the history
* 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 socketio#1147
  Bump Starscream to 3.0.6
  Update to Swift 4.2
  • Loading branch information
nuclearace committed Jan 16, 2019
2 parents f7ef126 + 6a3d82c commit 00aaea9
Show file tree
Hide file tree
Showing 16 changed files with 127 additions and 16 deletions.
1 change: 0 additions & 1 deletion .swift-version

This file was deleted.

7 changes: 6 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,9 @@
# v14.0.0

- Minimum version of the client is now Swift 4.2.
- Add exponential backoff for reconnects, with `reconnectWaitMax` and `randomizationFactor` options [#1149](https://github.com/socketio/socket.io-client-swift/pull/1149)
- `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)

# v13.4.0

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

2 changes: 1 addition & 1 deletion Package.swift
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// swift-tools-version:4.0
// swift-tools-version:4.2

import PackageDescription

Expand Down
10 changes: 5 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ SocketIOClient* socket = manager.defaultSocket;
## FAQS
Checkout the [FAQs](https://nuclearace.github.io/Socket.IO-Client-Swift/faq.html) for commonly asked questions.
Checkout the [12to13](https://nuclearace.github.io/Socket.IO-Client-Swift/12to13.html) guide for migrating to v13.
Checkout the [12to13](https://nuclearace.github.io/Socket.IO-Client-Swift/12to13.html) guide for migrating to v13+ from v12 below.
## Installation
Expand All @@ -76,7 +76,7 @@ If you need Swift 3.x use v11.1.3.
### Swift Package Manager
Add the project as a dependency to your Package.swift:
```swift
// swift-tools-version:4.0
// swift-tools-version:4.2
import PackageDescription
Expand All @@ -86,7 +86,7 @@ let package = Package(
.executable(name: "socket.io-test", targets: ["YourTargetName"])
],
dependencies: [
.package(url: "https://github.com/socketio/socket.io-client-swift", .upToNextMinor(from: "13.3.0"))
.package(url: "https://github.com/socketio/socket.io-client-swift", .upToNextMinor(from: "14.0.0"))
],
targets: [
.target(name: "YourTargetName", dependencies: ["SocketIO"], path: "./Path/To/Your/Sources")
Expand All @@ -99,7 +99,7 @@ Then import `import SocketIO`.
### Carthage
Add this line to your `Cartfile`:
```
github "socketio/socket.io-client-swift" ~> 13.3.0
github "socketio/socket.io-client-swift" ~> 14.0.0
```

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

target 'YourApp' do
pod 'Socket.IO-Client-Swift', '~> 13.3.0'
pod 'Socket.IO-Client-Swift', '~> 14.0.0'
end
```

Expand Down
4 changes: 3 additions & 1 deletion Socket.IO-Client-Swift.podspec
Original file line number Diff line number Diff line change
Expand Up @@ -21,9 +21,11 @@ Pod::Spec.new do |s|
:tag => 'v13.4.0',
:submodules => true
}

s.swift_version = "4.2"
s.pod_target_xcconfig = {
'SWIFT_VERSION' => '4.0'
}
s.source_files = "Source/SocketIO/**/*.swift", "Source/SocketIO/*.swift"
s.dependency "Starscream", "~> 3.0.2"
s.dependency "Starscream", "~> 3.0.6"
end
4 changes: 4 additions & 0 deletions Socket.IO-Client-Swift.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
/* Begin PBXBuildFile section */
1C6572803D7E252A77A86E5F /* SocketManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1C65763817782DFAC67BE05C /* SocketManager.swift */; };
1C6573B22DC9423CDFC32F05 /* SocketRawView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1C657533E849FC3E4342C602 /* SocketRawView.swift */; };
1C657CDE5D510E8E2E573E39 /* utils.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1C6577B639C34EE1C8829D9A /* utils.swift */; };
1C657FBB3F670261780FD72E /* SocketManagerSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1C6574AF9687A213814753E4 /* SocketManagerSpec.swift */; };
1C686BE21F869AFD007D8627 /* SocketIOClientConfigurationTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1C686BD21F869AF1007D8627 /* SocketIOClientConfigurationTest.swift */; };
1C686BE31F869AFD007D8627 /* SocketEngineTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1C686BD31F869AF1007D8627 /* SocketEngineTest.swift */; };
Expand Down Expand Up @@ -64,6 +65,7 @@
1C6574AF9687A213814753E4 /* SocketManagerSpec.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SocketManagerSpec.swift; sourceTree = "<group>"; };
1C657533E849FC3E4342C602 /* SocketRawView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SocketRawView.swift; sourceTree = "<group>"; };
1C65763817782DFAC67BE05C /* SocketManager.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SocketManager.swift; sourceTree = "<group>"; };
1C6577B639C34EE1C8829D9A /* utils.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = utils.swift; sourceTree = "<group>"; };
1C686BD21F869AF1007D8627 /* SocketIOClientConfigurationTest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SocketIOClientConfigurationTest.swift; sourceTree = "<group>"; };
1C686BD31F869AF1007D8627 /* SocketEngineTest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SocketEngineTest.swift; sourceTree = "<group>"; };
1C686BD41F869AF1007D8627 /* SocketSideEffectTest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SocketSideEffectTest.swift; sourceTree = "<group>"; };
Expand Down Expand Up @@ -159,6 +161,7 @@
1C686BD71F869AF1007D8627 /* SocketParserTest.swift */,
1C686BD81F869AF1007D8627 /* SocketNamespacePacketTest.swift */,
DD52BBAC5FAA7730D32CD5BF /* SocketMangerTest.swift */,
1C6577B639C34EE1C8829D9A /* utils.swift */,
);
name = TestSocketIO;
path = Tests/TestSocketIO;
Expand Down Expand Up @@ -501,6 +504,7 @@
1C686BE81F869AFD007D8627 /* SocketNamespacePacketTest.swift in Sources */,
DD52BCCD25EFA76E0F9B313C /* SocketMangerTest.swift in Sources */,
DD52B53F2609D91A683DFCDD /* ManagerObjectiveCTest.m in Sources */,
1C657CDE5D510E8E2E573E39 /* utils.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
Expand Down
2 changes: 1 addition & 1 deletion Source/SocketIO/Client/SocketIOClient.swift
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,7 @@ open class SocketIOClient : NSObject, SocketIOClientSpec {
@objc
public private(set) var status = SocketIOStatus.notConnected {
didSet {
handleClientEvent(.statusChange, data: [status])
handleClientEvent(.statusChange, data: [status, status.rawValue])
}
}

Expand Down
16 changes: 15 additions & 1 deletion Source/SocketIO/Client/SocketIOClientOption.swift
Original file line number Diff line number Diff line change
Expand Up @@ -75,8 +75,14 @@ public enum SocketIOClientOption : ClientOption {
/// The number of times to try and reconnect before giving up. Pass `-1` to [never give up](https://www.youtube.com/watch?v=dQw4w9WgXcQ).
case reconnectAttempts(Int)

/// The number of seconds to wait before reconnect attempts.
/// The minimum number of seconds to wait before reconnect attempts.
case reconnectWait(Int)

/// The maximum number of seconds to wait before reconnect attempts.
case reconnectWaitMax(Int)

/// The randomization factor for calculating reconnect jitter.
case randomizationFactor(Double)

/// Set `true` if your server is using secure transports.
case secure(Bool)
Expand Down Expand Up @@ -125,6 +131,10 @@ public enum SocketIOClientOption : ClientOption {
description = "reconnectAttempts"
case .reconnectWait:
description = "reconnectWait"
case .reconnectWaitMax:
description = "reconnectWaitMax"
case .randomizationFactor:
description = "randomizationFactor"
case .secure:
description = "secure"
case .selfSigned:
Expand Down Expand Up @@ -170,6 +180,10 @@ public enum SocketIOClientOption : ClientOption {
value = attempts
case let .reconnectWait(wait):
value = wait
case let .reconnectWaitMax(wait):
value = wait
case let .randomizationFactor(factor):
value = factor
case let .secure(secure):
value = secure
case let .security(security):
Expand Down
3 changes: 3 additions & 0 deletions Source/SocketIO/Client/SocketIOClientSpec.swift
Original file line number Diff line number Diff line change
Expand Up @@ -327,6 +327,9 @@ public enum SocketClientEvent : String {

/// Emitted every time there is a change in the client's status.
///
/// The payload for data is [SocketIOClientStatus, Int]. Where the second item is the raw value. Use the second one
/// if you are working in Objective-C.
///
/// Usage:
///
/// ```swift
Expand Down
28 changes: 26 additions & 2 deletions Source/SocketIO/Manager/SocketManager.swift
Original file line number Diff line number Diff line change
Expand Up @@ -97,9 +97,15 @@ open class SocketManager : NSObject, SocketManagerSpec, SocketParsable, SocketDa
/// If `true`, this client will try and reconnect on any disconnects.
public var reconnects = true

/// The number of seconds to wait before attempting to reconnect.
/// The minimum number of seconds to wait before attempting to reconnect.
public var reconnectWait = 10

/// The maximum number of seconds to wait before attempting to reconnect.
public var reconnectWaitMax = 30

/// The randomization factor for calculating reconnect jitter.
public var randomizationFactor = 0.5

/// The status of this manager.
public private(set) var status: SocketIOStatus = .notConnected {
didSet {
Expand Down Expand Up @@ -474,7 +480,21 @@ open class SocketManager : NSObject, SocketManagerSpec, SocketParsable, SocketDa
currentReconnectAttempt += 1
connect()

handleQueue.asyncAfter(deadline: DispatchTime.now() + Double(reconnectWait), execute: _tryReconnect)
let interval = reconnectInterval(attempts: currentReconnectAttempt)
DefaultSocketLogger.Logger.log("Scheduling reconnect in \(interval)s", type: SocketManager.logType)
handleQueue.asyncAfter(deadline: DispatchTime.now() + interval, execute: _tryReconnect)
}

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 rand = Double.random(in: 0 ..< 1)
let randomFactor = rand * randomizationFactor * Double(truncating: interval as NSNumber)
// add in random factor, and clamp to min and max values
let combined = interval + randomFactor
return Double(fmax(Double(reconnectWait), fmin(combined, Double(reconnectWaitMax))))
}

/// Sets manager specific configs.
Expand All @@ -493,6 +513,10 @@ open class SocketManager : NSObject, SocketManagerSpec, SocketParsable, SocketDa
self.reconnectAttempts = attempts
case let .reconnectWait(wait):
reconnectWait = abs(wait)
case let .reconnectWaitMax(wait):
reconnectWaitMax = abs(wait)
case let .randomizationFactor(factor):
randomizationFactor = factor
case let .log(log):
DefaultSocketLogger.Logger.log = log
case let .logger(logger):
Expand Down
8 changes: 7 additions & 1 deletion Source/SocketIO/Manager/SocketManagerSpec.swift
Original file line number Diff line number Diff line change
Expand Up @@ -69,8 +69,14 @@ public protocol SocketManagerSpec : AnyObject, SocketEngineClient {
/// If `true`, this manager will try and reconnect on any disconnects.
var reconnects: Bool { get set }

/// The number of seconds to wait before attempting to reconnect.
/// The minimum number of seconds to wait before attempting to reconnect.
var reconnectWait: Int { get set }

/// The maximum number of seconds to wait before attempting to reconnect.
var reconnectWaitMax: Int { get set }

/// The randomization factor for calculating reconnect jitter.
var randomizationFactor: Double { get set }

/// The URL of the socket.io server.
var socketURL: URL { get }
Expand Down
4 changes: 4 additions & 0 deletions Source/SocketIO/Util/SocketExtensions.swift
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,10 @@ extension Dictionary where Key == String, Value == Any {
return .reconnectAttempts(attempts)
case let ("reconnectWait", wait as Int):
return .reconnectWait(wait)
case let ("reconnectWaitMax", wait as Int):
return .reconnectWaitMax(wait)
case let ("randomizationFactor", factor as Double):
return .randomizationFactor(factor)
case let ("secure", secure as Bool):
return .secure(secure)
case let ("security", security as SSLSecurity):
Expand Down
21 changes: 21 additions & 0 deletions Tests/TestSocketIO/SocketMangerTest.swift
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@ class SocketMangerTest : XCTestCase {
XCTAssertEqual(manager.handleQueue, DispatchQueue.main)
XCTAssertTrue(manager.reconnects)
XCTAssertEqual(manager.reconnectWait, 10)
XCTAssertEqual(manager.reconnectWaitMax, 30)
XCTAssertEqual(manager.randomizationFactor, 0.5)
XCTAssertEqual(manager.status, .notConnected)
}

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

XCTAssertEqual(manager.config.first!, .secure(true))
}

func testBackoffIntervalCalulation() {
XCTAssertLessThanOrEqual(manager.reconnectInterval(attempts: -1), Double(manager.reconnectWaitMax))
XCTAssertLessThanOrEqual(manager.reconnectInterval(attempts: 0), 15)
XCTAssertLessThanOrEqual(manager.reconnectInterval(attempts: 1), 22.5)
XCTAssertLessThanOrEqual(manager.reconnectInterval(attempts: 2), 33.75)
XCTAssertLessThanOrEqual(manager.reconnectInterval(attempts: 50), Double(manager.reconnectWaitMax))
XCTAssertLessThanOrEqual(manager.reconnectInterval(attempts: 10000), Double(manager.reconnectWaitMax))

XCTAssertGreaterThanOrEqual(manager.reconnectInterval(attempts: -1), Double(manager.reconnectWait))
XCTAssertGreaterThanOrEqual(manager.reconnectInterval(attempts: 0), Double(manager.reconnectWait))
XCTAssertGreaterThanOrEqual(manager.reconnectInterval(attempts: 1), 15)
XCTAssertGreaterThanOrEqual(manager.reconnectInterval(attempts: 2), 22.5)
XCTAssertGreaterThanOrEqual(manager.reconnectInterval(attempts: 10000), Double(manager.reconnectWait))
}

func testManagerCallsConnect() {
setUpSockets()
Expand Down Expand Up @@ -90,13 +107,17 @@ class SocketMangerTest : XCTestCase {
.forceNew(true),
.reconnects(false),
.reconnectWait(5),
.reconnectWaitMax(5),
.randomizationFactor(0.7),
.reconnectAttempts(5)
])

XCTAssertEqual(manager.handleQueue, queue)
XCTAssertTrue(manager.forceNew)
XCTAssertFalse(manager.reconnects)
XCTAssertEqual(manager.reconnectWait, 5)
XCTAssertEqual(manager.reconnectWaitMax, 5)
XCTAssertEqual(manager.randomizationFactor, 0.7)
XCTAssertEqual(manager.reconnectAttempts, 5)
}

Expand Down
13 changes: 13 additions & 0 deletions Tests/TestSocketIO/utils.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
//
// Created by Erik Little on 2019-01-11.
//

import Foundation
@testable import SocketIO

public class OBjcUtils: NSObject {
@objc
public static func setTestStatus(socket: SocketIOClient, status: SocketIOStatus) {
socket.setTestStatus(status)
}
}
2 changes: 2 additions & 0 deletions Tests/TestSocketIOObjc/ManagerObjectiveCTest.m
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,8 @@ - (void)testManagerProperties {
XCTAssertEqual(self.manager.handleQueue, dispatch_get_main_queue());
XCTAssertTrue(self.manager.reconnects);
XCTAssertEqual(self.manager.reconnectWait, 10);
XCTAssertEqual(self.manager.reconnectWaitMax, 30);
XCTAssertEqual(self.manager.randomizationFactor, 0.5);
XCTAssertEqual(self.manager.status, SocketIOStatusNotConnected);
}

Expand Down
18 changes: 16 additions & 2 deletions Tests/TestSocketIOObjc/SocketObjectiveCTest.m
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
// Merely tests whether the Objective-C api breaks
//

#import "SocketIO_Tests-Swift.h"
#import "SocketObjectiveCTest.h"

@import Dispatch;
Expand Down Expand Up @@ -73,11 +74,11 @@ - (void)testEmitWriteCompletionSyntax {

- (void)testEmitWriteCompletion {
XCTestExpectation* expect = [self expectationWithDescription:@"Write completion should be called"];

[self.socket emit:@"testEmit" with:@[@YES] completion:^{
[expect fulfill];
}];

[self waitForExpectationsWithTimeout:0.3 handler:nil];
}

Expand All @@ -98,6 +99,19 @@ - (void)testSSLSecurity {
sec = nil;
}

- (void)testStatusChangeHandler {
XCTestExpectation* expect = [self expectationWithDescription:@"statusChange should be correctly called"];

[self.socket on:@"statusChange" callback:^(NSArray* data, SocketAckEmitter* ack) {
XCTAssertTrue([data[1] integerValue] == SocketIOStatusConnecting);
[expect fulfill];
}];

[OBjcUtils setTestStatusWithSocket:self.socket status:SocketIOStatusConnecting];

[self waitForExpectationsWithTimeout:0.3 handler:nil];
}

- (void)setUp {
[super setUp];
NSURL* url = [[NSURL alloc] initWithString:@"http://localhost"];
Expand Down

0 comments on commit 00aaea9

Please sign in to comment.