Merge branch 'development'
* development: (31 commits) Add note about namespace methods Add note in changelog to checkout the migration guide Add more to the migration guide don't return optional from defaultSocket Add link to 12to13 and style Start working on a migration guide Fix #855 refactor SocketIOClient.handlePacket bump version Do we really need to update carthage? Make sure to turn off logging in setup Don't use NSDictionary in definitions Fix tests Fix changelog update starscream versions nsp should be readonly now Add SocketManager for multiplexing namespaces Change sentPing/gotPong to ping/pong since those are already reserved Fix comment Add ability to track ping/pongs ...
This commit is contained in:
commit
176986d22a
10
.travis.yml
10
.travis.yml
@ -7,12 +7,12 @@ branches:
|
|||||||
- master
|
- master
|
||||||
- development
|
- development
|
||||||
before_install:
|
before_install:
|
||||||
- brew update
|
# - brew update
|
||||||
# - brew outdated xctool || brew upgrade xctool
|
# - brew outdated xctool || brew upgrade xctool
|
||||||
- brew outdated carthage || brew upgrade carthage
|
# - brew outdated carthage || brew upgrade carthage
|
||||||
- carthage update --platform macosx
|
- carthage update --platform macosx
|
||||||
script:
|
script:
|
||||||
- xcodebuild -project Socket.IO-Client-Swift.xcodeproj -scheme SocketIO-Mac build test -quiet
|
- xcodebuild -project Socket.IO-Client-Swift.xcodeproj -scheme SocketIO build test -quiet
|
||||||
# - xcodebuild -project Socket.IO-Client-Swift.xcodeproj -scheme SocketIO-Mac build-for-testing -quiet
|
# - xcodebuild -project Socket.IO-Client-Swift.xcodeproj -scheme SocketIO build-for-testing -quiet
|
||||||
# - xctool -project Socket.IO-Client-Swift.xcodeproj -scheme SocketIO-Mac run-tests --parallelize
|
# - xctool -project Socket.IO-Client-Swift.xcodeproj -scheme SocketIO run-tests --parallelize
|
||||||
- swift test
|
- swift test
|
||||||
|
|||||||
20
CHANGELOG.md
Normal file
20
CHANGELOG.md
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
# v13.0.0
|
||||||
|
|
||||||
|
Checkout out the migration guide in Usage Docs for a more detailed guide on how to migrate to this version.
|
||||||
|
|
||||||
|
What's new:
|
||||||
|
---
|
||||||
|
|
||||||
|
- Adds a new `SocketManager` class that multiplexes multiple namespaces through a single engine.
|
||||||
|
- Adds `.sentPing` and `.gotPong` client events for tracking ping/pongs.
|
||||||
|
- watchOS support.
|
||||||
|
|
||||||
|
Important API changes
|
||||||
|
---
|
||||||
|
|
||||||
|
- Many properties that were previously on `SocketIOClient` have been moved to the `SocketManager`.
|
||||||
|
- `SocketIOClientOption.nsp` has been removed. Use `SocketManager.socket(forNamespace:)` to create/get a socket attached to a specific namespace.
|
||||||
|
- Adds `.sentPing` and `.gotPong` client events for tracking ping/pongs.
|
||||||
|
- Makes the framework a single target.
|
||||||
|
- Updates Starscream to 3.0
|
||||||
|
|
||||||
2
Cartfile
2
Cartfile
@ -1 +1 @@
|
|||||||
github "daltoniam/Starscream" ~> 2.0
|
github "daltoniam/Starscream" ~> 3.0
|
||||||
|
|||||||
@ -1 +1,3 @@
|
|||||||
github "daltoniam/Starscream" "2.1.1"
|
github "daltoniam/common-crypto-spm" "1.1.0"
|
||||||
|
github "daltoniam/zlib-spm" "1.1.0"
|
||||||
|
github "daltoniam/Starscream" "3.0.2"
|
||||||
|
|||||||
@ -24,8 +24,8 @@
|
|||||||
"repositoryURL": "https://github.com/daltoniam/Starscream",
|
"repositoryURL": "https://github.com/daltoniam/Starscream",
|
||||||
"state": {
|
"state": {
|
||||||
"branch": null,
|
"branch": null,
|
||||||
"revision": "21678c9426dde2a77152a0d5982cdb952baf0455",
|
"revision": "44ce58956fae7db22fb0106cb4ce3dbef3d06d00",
|
||||||
"version": "2.1.1"
|
"version": "3.0.2"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|||||||
@ -8,7 +8,7 @@ let package = Package(
|
|||||||
.library(name: "SocketIO", targets: ["SocketIO"])
|
.library(name: "SocketIO", targets: ["SocketIO"])
|
||||||
],
|
],
|
||||||
dependencies: [
|
dependencies: [
|
||||||
.package(url: "https://github.com/daltoniam/Starscream", .upToNextMajor(from: "2.1.1")),
|
.package(url: "https://github.com/daltoniam/Starscream", .upToNextMinor(from: "3.0.0")),
|
||||||
],
|
],
|
||||||
targets: [
|
targets: [
|
||||||
.target(name: "SocketIO", dependencies: ["Starscream"]),
|
.target(name: "SocketIO", dependencies: ["Starscream"]),
|
||||||
|
|||||||
27
README.md
27
README.md
@ -7,20 +7,21 @@ Socket.IO-client for iOS/OS X.
|
|||||||
```swift
|
```swift
|
||||||
import SocketIO
|
import SocketIO
|
||||||
|
|
||||||
let socket = SocketIOClient(socketURL: URL(string: "http://localhost:8080")!, config: [.log(true), .compress])
|
let manager = SocketManager(socketURL: URL(string: "http://localhost:8080")!, config: [.log(true), .compress])
|
||||||
|
let socket = manager.defaultSocket
|
||||||
|
|
||||||
socket.on(clientEvent: .connect) {data, ack in
|
socket.on(clientEvent: .connect) {data, ack in
|
||||||
print("socket connected")
|
print("socket connected")
|
||||||
}
|
}
|
||||||
|
|
||||||
socket.on("currentAmount") {data, ack in
|
socket.on("currentAmount") {data, ack in
|
||||||
if let cur = data[0] as? Double {
|
guard let cur = data[0] as? Double else { return }
|
||||||
socket.emitWithAck("canUpdate", cur).timingOut(after: 0) {data in
|
|
||||||
socket.emit("update", ["amount": cur + 2.50])
|
|
||||||
}
|
|
||||||
|
|
||||||
ack.with("Got your currentAmount", "dude")
|
socket.emitWithAck("canUpdate", cur).timingOut(after: 0) {data in
|
||||||
|
socket.emit("update", ["amount": cur + 2.50])
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ack.with("Got your currentAmount", "dude")
|
||||||
}
|
}
|
||||||
|
|
||||||
socket.connect()
|
socket.connect()
|
||||||
@ -29,8 +30,10 @@ socket.connect()
|
|||||||
## Objective-C Example
|
## Objective-C Example
|
||||||
```objective-c
|
```objective-c
|
||||||
@import SocketIO;
|
@import SocketIO;
|
||||||
|
|
||||||
NSURL* url = [[NSURL alloc] initWithString:@"http://localhost:8080"];
|
NSURL* url = [[NSURL alloc] initWithString:@"http://localhost:8080"];
|
||||||
SocketIOClient* socket = [[SocketIOClient alloc] initWithSocketURL:url config:@{@"log": @YES, @"compress": @YES}];
|
SocketManager* manager = [[SocketManager alloc] initWithSocketURL:url config:@{@"log": @YES, @"compress": @YES}];
|
||||||
|
SocketIOClient* socket = manager.defaultSocket;
|
||||||
|
|
||||||
[socket on:@"connect" callback:^(NSArray* data, SocketAckEmitter* ack) {
|
[socket on:@"connect" callback:^(NSArray* data, SocketAckEmitter* ack) {
|
||||||
NSLog(@"socket connected");
|
NSLog(@"socket connected");
|
||||||
@ -60,6 +63,9 @@ SocketIOClient* socket = [[SocketIOClient alloc] initWithSocketURL:url config:@{
|
|||||||
## FAQS
|
## FAQS
|
||||||
Checkout the [FAQs](https://nuclearace.github.io/Socket.IO-Client-Swift/faq.html) for commonly asked questions.
|
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.
|
||||||
|
|
||||||
|
|
||||||
## Installation
|
## Installation
|
||||||
Requires Swift 4/Xcode 9.x
|
Requires Swift 4/Xcode 9.x
|
||||||
|
|
||||||
@ -80,7 +86,7 @@ let package = Package(
|
|||||||
.executable(name: "socket.io-test", targets: ["YourTargetName"])
|
.executable(name: "socket.io-test", targets: ["YourTargetName"])
|
||||||
],
|
],
|
||||||
dependencies: [
|
dependencies: [
|
||||||
.package(url: "https://github.com/socketio/socket.io-client-swift", .upToNextMajor(from: "12.1.0"))
|
.package(url: "https://github.com/socketio/socket.io-client-swift", .upToNextMinor(from: "13.0.0"))
|
||||||
],
|
],
|
||||||
targets: [
|
targets: [
|
||||||
.target(name: "YourTargetName", dependencies: ["SocketIO"], path: "./Path/To/Your/Sources")
|
.target(name: "YourTargetName", dependencies: ["SocketIO"], path: "./Path/To/Your/Sources")
|
||||||
@ -93,7 +99,7 @@ Then import `import SocketIO`.
|
|||||||
### Carthage
|
### Carthage
|
||||||
Add this line to your `Cartfile`:
|
Add this line to your `Cartfile`:
|
||||||
```
|
```
|
||||||
github "socketio/socket.io-client-swift" ~> 12.1.3 # Or latest version
|
github "socketio/socket.io-client-swift" ~> 13.0.0 # Or latest version
|
||||||
```
|
```
|
||||||
|
|
||||||
Run `carthage update --platform ios,macosx`.
|
Run `carthage update --platform ios,macosx`.
|
||||||
@ -107,7 +113,7 @@ Create `Podfile` and add `pod 'Socket.IO-Client-Swift'`:
|
|||||||
use_frameworks!
|
use_frameworks!
|
||||||
|
|
||||||
target 'YourApp' do
|
target 'YourApp' do
|
||||||
pod 'Socket.IO-Client-Swift', '~> 12.1.3' # Or latest version
|
pod 'Socket.IO-Client-Swift', '~> 13.0.0' # Or latest version
|
||||||
end
|
end
|
||||||
```
|
```
|
||||||
|
|
||||||
@ -134,6 +140,7 @@ Objective-C:
|
|||||||
# [Docs](https://nuclearace.github.io/Socket.IO-Client-Swift/index.html)
|
# [Docs](https://nuclearace.github.io/Socket.IO-Client-Swift/index.html)
|
||||||
|
|
||||||
- [Client](https://nuclearace.github.io/Socket.IO-Client-Swift/Classes/SocketIOClient.html)
|
- [Client](https://nuclearace.github.io/Socket.IO-Client-Swift/Classes/SocketIOClient.html)
|
||||||
|
- [Manager](https://nuclearace.github.io/Socket.IO-Client-Swift/Classes/SocketManager.html)
|
||||||
- [Engine](https://nuclearace.github.io/Socket.IO-Client-Swift/Classes/SocketEngine.html)
|
- [Engine](https://nuclearace.github.io/Socket.IO-Client-Swift/Classes/SocketEngine.html)
|
||||||
- [Options](https://nuclearace.github.io/Socket.IO-Client-Swift/Enums/SocketIOClientOption.html)
|
- [Options](https://nuclearace.github.io/Socket.IO-Client-Swift/Enums/SocketIOClientOption.html)
|
||||||
|
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
Pod::Spec.new do |s|
|
Pod::Spec.new do |s|
|
||||||
s.name = "Socket.IO-Client-Swift"
|
s.name = "Socket.IO-Client-Swift"
|
||||||
s.module_name = "SocketIO"
|
s.module_name = "SocketIO"
|
||||||
s.version = "12.1.3"
|
s.version = "13.0.0"
|
||||||
s.summary = "Socket.IO-client for iOS and OS X"
|
s.summary = "Socket.IO-client for iOS and OS X"
|
||||||
s.description = <<-DESC
|
s.description = <<-DESC
|
||||||
Socket.IO-client for iOS and OS X.
|
Socket.IO-client for iOS and OS X.
|
||||||
@ -14,15 +14,16 @@ Pod::Spec.new do |s|
|
|||||||
s.ios.deployment_target = '8.0'
|
s.ios.deployment_target = '8.0'
|
||||||
s.osx.deployment_target = '10.10'
|
s.osx.deployment_target = '10.10'
|
||||||
s.tvos.deployment_target = '9.0'
|
s.tvos.deployment_target = '9.0'
|
||||||
|
s.watchos.deployment_target = '2.0'
|
||||||
s.requires_arc = true
|
s.requires_arc = true
|
||||||
s.source = {
|
s.source = {
|
||||||
:git => "https://github.com/socketio/socket.io-client-swift.git",
|
:git => "https://github.com/socketio/socket.io-client-swift.git",
|
||||||
:tag => 'v12.1.3',
|
:tag => 'v13.0.0',
|
||||||
:submodules => true
|
:submodules => true
|
||||||
}
|
}
|
||||||
s.pod_target_xcconfig = {
|
s.pod_target_xcconfig = {
|
||||||
'SWIFT_VERSION' => '4.0'
|
'SWIFT_VERSION' => '4.0'
|
||||||
}
|
}
|
||||||
s.source_files = "Source/SocketIO/**/*.swift", "Source/SocketIO/*.swift"
|
s.source_files = "Source/SocketIO/**/*.swift", "Source/SocketIO/*.swift"
|
||||||
s.dependency "Starscream", "~> 2.1.1"
|
s.dependency "Starscream", "~> 3.0.2"
|
||||||
end
|
end
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@ -1,115 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<Scheme
|
|
||||||
LastUpgradeVersion = "0900"
|
|
||||||
version = "1.3">
|
|
||||||
<BuildAction
|
|
||||||
parallelizeBuildables = "YES"
|
|
||||||
buildImplicitDependencies = "YES">
|
|
||||||
<BuildActionEntries>
|
|
||||||
<BuildActionEntry
|
|
||||||
buildForTesting = "YES"
|
|
||||||
buildForRunning = "YES"
|
|
||||||
buildForProfiling = "YES"
|
|
||||||
buildForArchiving = "YES"
|
|
||||||
buildForAnalyzing = "YES">
|
|
||||||
<BuildableReference
|
|
||||||
BuildableIdentifier = "primary"
|
|
||||||
BlueprintIdentifier = "572EF2181B51F16C00EEBB58"
|
|
||||||
BuildableName = "SocketIO.framework"
|
|
||||||
BlueprintName = "SocketIO-iOS"
|
|
||||||
ReferencedContainer = "container:Socket.IO-Client-Swift.xcodeproj">
|
|
||||||
</BuildableReference>
|
|
||||||
</BuildActionEntry>
|
|
||||||
<BuildActionEntry
|
|
||||||
buildForTesting = "YES"
|
|
||||||
buildForRunning = "NO"
|
|
||||||
buildForProfiling = "NO"
|
|
||||||
buildForArchiving = "NO"
|
|
||||||
buildForAnalyzing = "YES">
|
|
||||||
<BuildableReference
|
|
||||||
BuildableIdentifier = "primary"
|
|
||||||
BlueprintIdentifier = "572EF2231B51F16C00EEBB58"
|
|
||||||
BuildableName = "SocketIO-iOSTests.xctest"
|
|
||||||
BlueprintName = "SocketIO-iOSTests"
|
|
||||||
ReferencedContainer = "container:Socket.IO-Client-Swift.xcodeproj">
|
|
||||||
</BuildableReference>
|
|
||||||
</BuildActionEntry>
|
|
||||||
</BuildActionEntries>
|
|
||||||
</BuildAction>
|
|
||||||
<TestAction
|
|
||||||
buildConfiguration = "Debug"
|
|
||||||
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
|
||||||
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
|
||||||
language = ""
|
|
||||||
shouldUseLaunchSchemeArgsEnv = "YES">
|
|
||||||
<Testables>
|
|
||||||
<TestableReference
|
|
||||||
skipped = "NO">
|
|
||||||
<BuildableReference
|
|
||||||
BuildableIdentifier = "primary"
|
|
||||||
BlueprintIdentifier = "572EF2231B51F16C00EEBB58"
|
|
||||||
BuildableName = "SocketIO-iOSTests.xctest"
|
|
||||||
BlueprintName = "SocketIO-iOSTests"
|
|
||||||
ReferencedContainer = "container:Socket.IO-Client-Swift.xcodeproj">
|
|
||||||
</BuildableReference>
|
|
||||||
</TestableReference>
|
|
||||||
</Testables>
|
|
||||||
<MacroExpansion>
|
|
||||||
<BuildableReference
|
|
||||||
BuildableIdentifier = "primary"
|
|
||||||
BlueprintIdentifier = "572EF2181B51F16C00EEBB58"
|
|
||||||
BuildableName = "SocketIO.framework"
|
|
||||||
BlueprintName = "SocketIO-iOS"
|
|
||||||
ReferencedContainer = "container:Socket.IO-Client-Swift.xcodeproj">
|
|
||||||
</BuildableReference>
|
|
||||||
</MacroExpansion>
|
|
||||||
<AdditionalOptions>
|
|
||||||
</AdditionalOptions>
|
|
||||||
</TestAction>
|
|
||||||
<LaunchAction
|
|
||||||
buildConfiguration = "Debug"
|
|
||||||
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
|
||||||
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
|
||||||
language = ""
|
|
||||||
launchStyle = "0"
|
|
||||||
useCustomWorkingDirectory = "NO"
|
|
||||||
ignoresPersistentStateOnLaunch = "NO"
|
|
||||||
debugDocumentVersioning = "YES"
|
|
||||||
debugServiceExtension = "internal"
|
|
||||||
allowLocationSimulation = "YES">
|
|
||||||
<MacroExpansion>
|
|
||||||
<BuildableReference
|
|
||||||
BuildableIdentifier = "primary"
|
|
||||||
BlueprintIdentifier = "572EF2181B51F16C00EEBB58"
|
|
||||||
BuildableName = "SocketIO.framework"
|
|
||||||
BlueprintName = "SocketIO-iOS"
|
|
||||||
ReferencedContainer = "container:Socket.IO-Client-Swift.xcodeproj">
|
|
||||||
</BuildableReference>
|
|
||||||
</MacroExpansion>
|
|
||||||
<AdditionalOptions>
|
|
||||||
</AdditionalOptions>
|
|
||||||
</LaunchAction>
|
|
||||||
<ProfileAction
|
|
||||||
buildConfiguration = "Release"
|
|
||||||
shouldUseLaunchSchemeArgsEnv = "YES"
|
|
||||||
savedToolIdentifier = ""
|
|
||||||
useCustomWorkingDirectory = "NO"
|
|
||||||
debugDocumentVersioning = "YES">
|
|
||||||
<MacroExpansion>
|
|
||||||
<BuildableReference
|
|
||||||
BuildableIdentifier = "primary"
|
|
||||||
BlueprintIdentifier = "572EF2181B51F16C00EEBB58"
|
|
||||||
BuildableName = "SocketIO.framework"
|
|
||||||
BlueprintName = "SocketIO-iOS"
|
|
||||||
ReferencedContainer = "container:Socket.IO-Client-Swift.xcodeproj">
|
|
||||||
</BuildableReference>
|
|
||||||
</MacroExpansion>
|
|
||||||
</ProfileAction>
|
|
||||||
<AnalyzeAction
|
|
||||||
buildConfiguration = "Debug">
|
|
||||||
</AnalyzeAction>
|
|
||||||
<ArchiveAction
|
|
||||||
buildConfiguration = "Release"
|
|
||||||
revealArchiveInOrganizer = "YES">
|
|
||||||
</ArchiveAction>
|
|
||||||
</Scheme>
|
|
||||||
@ -1,115 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<Scheme
|
|
||||||
LastUpgradeVersion = "0900"
|
|
||||||
version = "1.3">
|
|
||||||
<BuildAction
|
|
||||||
parallelizeBuildables = "YES"
|
|
||||||
buildImplicitDependencies = "YES">
|
|
||||||
<BuildActionEntries>
|
|
||||||
<BuildActionEntry
|
|
||||||
buildForTesting = "YES"
|
|
||||||
buildForRunning = "YES"
|
|
||||||
buildForProfiling = "YES"
|
|
||||||
buildForArchiving = "YES"
|
|
||||||
buildForAnalyzing = "YES">
|
|
||||||
<BuildableReference
|
|
||||||
BuildableIdentifier = "primary"
|
|
||||||
BlueprintIdentifier = "576349FA1BD9B46A00E19CD7"
|
|
||||||
BuildableName = "SocketIO.framework"
|
|
||||||
BlueprintName = "SocketIO-tvOS"
|
|
||||||
ReferencedContainer = "container:Socket.IO-Client-Swift.xcodeproj">
|
|
||||||
</BuildableReference>
|
|
||||||
</BuildActionEntry>
|
|
||||||
<BuildActionEntry
|
|
||||||
buildForTesting = "YES"
|
|
||||||
buildForRunning = "NO"
|
|
||||||
buildForProfiling = "NO"
|
|
||||||
buildForArchiving = "NO"
|
|
||||||
buildForAnalyzing = "NO">
|
|
||||||
<BuildableReference
|
|
||||||
BuildableIdentifier = "primary"
|
|
||||||
BlueprintIdentifier = "57634A181BD9B46D00E19CD7"
|
|
||||||
BuildableName = "SocketIO-tvOSTests.xctest"
|
|
||||||
BlueprintName = "SocketIO-tvOSTests"
|
|
||||||
ReferencedContainer = "container:Socket.IO-Client-Swift.xcodeproj">
|
|
||||||
</BuildableReference>
|
|
||||||
</BuildActionEntry>
|
|
||||||
</BuildActionEntries>
|
|
||||||
</BuildAction>
|
|
||||||
<TestAction
|
|
||||||
buildConfiguration = "Debug"
|
|
||||||
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
|
||||||
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
|
||||||
language = ""
|
|
||||||
shouldUseLaunchSchemeArgsEnv = "YES">
|
|
||||||
<Testables>
|
|
||||||
<TestableReference
|
|
||||||
skipped = "NO">
|
|
||||||
<BuildableReference
|
|
||||||
BuildableIdentifier = "primary"
|
|
||||||
BlueprintIdentifier = "57634A181BD9B46D00E19CD7"
|
|
||||||
BuildableName = "SocketIO-tvOSTests.xctest"
|
|
||||||
BlueprintName = "SocketIO-tvOSTests"
|
|
||||||
ReferencedContainer = "container:Socket.IO-Client-Swift.xcodeproj">
|
|
||||||
</BuildableReference>
|
|
||||||
</TestableReference>
|
|
||||||
</Testables>
|
|
||||||
<MacroExpansion>
|
|
||||||
<BuildableReference
|
|
||||||
BuildableIdentifier = "primary"
|
|
||||||
BlueprintIdentifier = "576349FA1BD9B46A00E19CD7"
|
|
||||||
BuildableName = "SocketIO.framework"
|
|
||||||
BlueprintName = "SocketIO-tvOS"
|
|
||||||
ReferencedContainer = "container:Socket.IO-Client-Swift.xcodeproj">
|
|
||||||
</BuildableReference>
|
|
||||||
</MacroExpansion>
|
|
||||||
<AdditionalOptions>
|
|
||||||
</AdditionalOptions>
|
|
||||||
</TestAction>
|
|
||||||
<LaunchAction
|
|
||||||
buildConfiguration = "Debug"
|
|
||||||
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
|
||||||
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
|
||||||
language = ""
|
|
||||||
launchStyle = "0"
|
|
||||||
useCustomWorkingDirectory = "NO"
|
|
||||||
ignoresPersistentStateOnLaunch = "NO"
|
|
||||||
debugDocumentVersioning = "YES"
|
|
||||||
debugServiceExtension = "internal"
|
|
||||||
allowLocationSimulation = "YES">
|
|
||||||
<MacroExpansion>
|
|
||||||
<BuildableReference
|
|
||||||
BuildableIdentifier = "primary"
|
|
||||||
BlueprintIdentifier = "576349FA1BD9B46A00E19CD7"
|
|
||||||
BuildableName = "SocketIO.framework"
|
|
||||||
BlueprintName = "SocketIO-tvOS"
|
|
||||||
ReferencedContainer = "container:Socket.IO-Client-Swift.xcodeproj">
|
|
||||||
</BuildableReference>
|
|
||||||
</MacroExpansion>
|
|
||||||
<AdditionalOptions>
|
|
||||||
</AdditionalOptions>
|
|
||||||
</LaunchAction>
|
|
||||||
<ProfileAction
|
|
||||||
buildConfiguration = "Release"
|
|
||||||
shouldUseLaunchSchemeArgsEnv = "YES"
|
|
||||||
savedToolIdentifier = ""
|
|
||||||
useCustomWorkingDirectory = "NO"
|
|
||||||
debugDocumentVersioning = "YES">
|
|
||||||
<MacroExpansion>
|
|
||||||
<BuildableReference
|
|
||||||
BuildableIdentifier = "primary"
|
|
||||||
BlueprintIdentifier = "576349FA1BD9B46A00E19CD7"
|
|
||||||
BuildableName = "SocketIO.framework"
|
|
||||||
BlueprintName = "SocketIO-tvOS"
|
|
||||||
ReferencedContainer = "container:Socket.IO-Client-Swift.xcodeproj">
|
|
||||||
</BuildableReference>
|
|
||||||
</MacroExpansion>
|
|
||||||
</ProfileAction>
|
|
||||||
<AnalyzeAction
|
|
||||||
buildConfiguration = "Debug">
|
|
||||||
</AnalyzeAction>
|
|
||||||
<ArchiveAction
|
|
||||||
buildConfiguration = "Release"
|
|
||||||
revealArchiveInOrganizer = "YES">
|
|
||||||
</ArchiveAction>
|
|
||||||
</Scheme>
|
|
||||||
@ -16,7 +16,7 @@
|
|||||||
BuildableIdentifier = "primary"
|
BuildableIdentifier = "primary"
|
||||||
BlueprintIdentifier = "572EF2371B51F18A00EEBB58"
|
BlueprintIdentifier = "572EF2371B51F18A00EEBB58"
|
||||||
BuildableName = "SocketIO.framework"
|
BuildableName = "SocketIO.framework"
|
||||||
BlueprintName = "SocketIO-Mac"
|
BlueprintName = "SocketIO"
|
||||||
ReferencedContainer = "container:Socket.IO-Client-Swift.xcodeproj">
|
ReferencedContainer = "container:Socket.IO-Client-Swift.xcodeproj">
|
||||||
</BuildableReference>
|
</BuildableReference>
|
||||||
</BuildActionEntry>
|
</BuildActionEntry>
|
||||||
@ -29,8 +29,8 @@
|
|||||||
<BuildableReference
|
<BuildableReference
|
||||||
BuildableIdentifier = "primary"
|
BuildableIdentifier = "primary"
|
||||||
BlueprintIdentifier = "572EF2411B51F18A00EEBB58"
|
BlueprintIdentifier = "572EF2411B51F18A00EEBB58"
|
||||||
BuildableName = "SocketIO-MacTests.xctest"
|
BuildableName = "SocketIO-Tests.xctest"
|
||||||
BlueprintName = "SocketIO-MacTests"
|
BlueprintName = "SocketIO-Tests"
|
||||||
ReferencedContainer = "container:Socket.IO-Client-Swift.xcodeproj">
|
ReferencedContainer = "container:Socket.IO-Client-Swift.xcodeproj">
|
||||||
</BuildableReference>
|
</BuildableReference>
|
||||||
</BuildActionEntry>
|
</BuildActionEntry>
|
||||||
@ -48,8 +48,8 @@
|
|||||||
<BuildableReference
|
<BuildableReference
|
||||||
BuildableIdentifier = "primary"
|
BuildableIdentifier = "primary"
|
||||||
BlueprintIdentifier = "572EF2411B51F18A00EEBB58"
|
BlueprintIdentifier = "572EF2411B51F18A00EEBB58"
|
||||||
BuildableName = "SocketIO-MacTests.xctest"
|
BuildableName = "SocketIO-Tests.xctest"
|
||||||
BlueprintName = "SocketIO-MacTests"
|
BlueprintName = "SocketIO-Tests"
|
||||||
ReferencedContainer = "container:Socket.IO-Client-Swift.xcodeproj">
|
ReferencedContainer = "container:Socket.IO-Client-Swift.xcodeproj">
|
||||||
</BuildableReference>
|
</BuildableReference>
|
||||||
</TestableReference>
|
</TestableReference>
|
||||||
@ -59,7 +59,7 @@
|
|||||||
BuildableIdentifier = "primary"
|
BuildableIdentifier = "primary"
|
||||||
BlueprintIdentifier = "572EF2371B51F18A00EEBB58"
|
BlueprintIdentifier = "572EF2371B51F18A00EEBB58"
|
||||||
BuildableName = "SocketIO.framework"
|
BuildableName = "SocketIO.framework"
|
||||||
BlueprintName = "SocketIO-Mac"
|
BlueprintName = "SocketIO"
|
||||||
ReferencedContainer = "container:Socket.IO-Client-Swift.xcodeproj">
|
ReferencedContainer = "container:Socket.IO-Client-Swift.xcodeproj">
|
||||||
</BuildableReference>
|
</BuildableReference>
|
||||||
</MacroExpansion>
|
</MacroExpansion>
|
||||||
@ -82,7 +82,7 @@
|
|||||||
BuildableIdentifier = "primary"
|
BuildableIdentifier = "primary"
|
||||||
BlueprintIdentifier = "572EF2371B51F18A00EEBB58"
|
BlueprintIdentifier = "572EF2371B51F18A00EEBB58"
|
||||||
BuildableName = "SocketIO.framework"
|
BuildableName = "SocketIO.framework"
|
||||||
BlueprintName = "SocketIO-Mac"
|
BlueprintName = "SocketIO"
|
||||||
ReferencedContainer = "container:Socket.IO-Client-Swift.xcodeproj">
|
ReferencedContainer = "container:Socket.IO-Client-Swift.xcodeproj">
|
||||||
</BuildableReference>
|
</BuildableReference>
|
||||||
</MacroExpansion>
|
</MacroExpansion>
|
||||||
@ -100,7 +100,7 @@
|
|||||||
BuildableIdentifier = "primary"
|
BuildableIdentifier = "primary"
|
||||||
BlueprintIdentifier = "572EF2371B51F18A00EEBB58"
|
BlueprintIdentifier = "572EF2371B51F18A00EEBB58"
|
||||||
BuildableName = "SocketIO.framework"
|
BuildableName = "SocketIO.framework"
|
||||||
BlueprintName = "SocketIO-Mac"
|
BlueprintName = "SocketIO"
|
||||||
ReferencedContainer = "container:Socket.IO-Client-Swift.xcodeproj">
|
ReferencedContainer = "container:Socket.IO-Client-Swift.xcodeproj">
|
||||||
</BuildableReference>
|
</BuildableReference>
|
||||||
</MacroExpansion>
|
</MacroExpansion>
|
||||||
@ -1,26 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
|
||||||
<plist version="1.0">
|
|
||||||
<dict>
|
|
||||||
<key>CFBundleDevelopmentRegion</key>
|
|
||||||
<string>en</string>
|
|
||||||
<key>CFBundleExecutable</key>
|
|
||||||
<string>$(EXECUTABLE_NAME)</string>
|
|
||||||
<key>CFBundleIdentifier</key>
|
|
||||||
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
|
|
||||||
<key>CFBundleInfoDictionaryVersion</key>
|
|
||||||
<string>6.0</string>
|
|
||||||
<key>CFBundleName</key>
|
|
||||||
<string>$(PRODUCT_NAME)</string>
|
|
||||||
<key>CFBundlePackageType</key>
|
|
||||||
<string>FMWK</string>
|
|
||||||
<key>CFBundleShortVersionString</key>
|
|
||||||
<string>1.0</string>
|
|
||||||
<key>CFBundleSignature</key>
|
|
||||||
<string>????</string>
|
|
||||||
<key>CFBundleVersion</key>
|
|
||||||
<string>$(CURRENT_PROJECT_VERSION)</string>
|
|
||||||
<key>NSPrincipalClass</key>
|
|
||||||
<string></string>
|
|
||||||
</dict>
|
|
||||||
</plist>
|
|
||||||
@ -1,19 +0,0 @@
|
|||||||
//
|
|
||||||
// SocketIO-iOS.h
|
|
||||||
// SocketIO-iOS
|
|
||||||
//
|
|
||||||
// Created by Nacho Soto on 7/11/15.
|
|
||||||
//
|
|
||||||
//
|
|
||||||
|
|
||||||
#import <UIKit/UIKit.h>
|
|
||||||
|
|
||||||
//! Project version number for SocketIO-iOS.
|
|
||||||
FOUNDATION_EXPORT double SocketIO_iOSVersionNumber;
|
|
||||||
|
|
||||||
//! Project version string for SocketIO-iOS.
|
|
||||||
FOUNDATION_EXPORT const unsigned char SocketIO_iOSVersionString[];
|
|
||||||
|
|
||||||
// In this header, you should import all the public headers of your framework using statements like #import <SocketIO_iOS/PublicHeader.h>
|
|
||||||
|
|
||||||
|
|
||||||
@ -6,7 +6,7 @@
|
|||||||
//
|
//
|
||||||
//
|
//
|
||||||
|
|
||||||
#import <Cocoa/Cocoa.h>
|
#import <Foundation/Foundation.h>
|
||||||
|
|
||||||
//! Project version number for SocketIO-Mac.
|
//! Project version number for SocketIO-Mac.
|
||||||
FOUNDATION_EXPORT double SocketIO_MacVersionNumber;
|
FOUNDATION_EXPORT double SocketIO_MacVersionNumber;
|
||||||
@ -120,8 +120,10 @@ public final class OnAckCallback : NSObject {
|
|||||||
|
|
||||||
guard seconds != 0 else { return }
|
guard seconds != 0 else { return }
|
||||||
|
|
||||||
socket.handleQueue.asyncAfter(deadline: DispatchTime.now() + seconds) {
|
socket.manager?.handleQueue.asyncAfter(deadline: DispatchTime.now() + seconds) {[weak socket] in
|
||||||
socket.ackHandlers.timeoutAck(self.ackNumber, onQueue: socket.handleQueue)
|
guard let socket = socket, let manager = socket.manager else { return }
|
||||||
|
|
||||||
|
socket.ackHandlers.timeoutAck(self.ackNumber, onQueue: manager.handleQueue)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -25,108 +25,52 @@
|
|||||||
import Dispatch
|
import Dispatch
|
||||||
import Foundation
|
import Foundation
|
||||||
|
|
||||||
/// The main class for SocketIOClientSwift.
|
/// Represents a socket.io-client.
|
||||||
///
|
///
|
||||||
/// **NOTE**: The client is not thread/queue safe, all interaction with the socket should be done on the `handleQueue`
|
/// Clients are created through a `SocketManager`, which owns the `SocketEngineSpec` that controls the connection to the server.
|
||||||
///
|
///
|
||||||
/// Represents a socket.io-client. Most interaction with socket.io will be through this class.
|
/// For example:
|
||||||
open class SocketIOClient : NSObject, SocketIOClientSpec, SocketEngineClient, SocketParsable {
|
///
|
||||||
|
/// ```swift
|
||||||
|
/// // Create a socket for the /swift namespace
|
||||||
|
/// let socket = manager.socket(forNamespace: "/swift")
|
||||||
|
///
|
||||||
|
/// // Add some handlers and connect
|
||||||
|
/// ```
|
||||||
|
///
|
||||||
|
/// **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 {
|
||||||
// MARK: Properties
|
// MARK: Properties
|
||||||
|
|
||||||
private static let logType = "SocketIOClient"
|
|
||||||
|
|
||||||
/// If `true` then every time `connect` is called, a new engine will be created.
|
|
||||||
@objc
|
|
||||||
public var forceNew = false
|
|
||||||
|
|
||||||
/// The queue that all interaction with the client should occur on. This is the queue that event handlers are
|
|
||||||
/// called on.
|
|
||||||
@objc
|
|
||||||
public var handleQueue = DispatchQueue.main
|
|
||||||
|
|
||||||
/// The namespace that this socket is currently connected to.
|
/// The namespace that this socket is currently connected to.
|
||||||
///
|
///
|
||||||
/// **Must** start with a `/`.
|
/// **Must** start with a `/`.
|
||||||
@objc
|
@objc
|
||||||
public var nsp = "/"
|
public let nsp: String
|
||||||
|
|
||||||
/// The configuration for this client.
|
|
||||||
///
|
|
||||||
/// **This cannot be set after calling one of the connect methods**.
|
|
||||||
public var config: SocketIOClientConfiguration {
|
|
||||||
get {
|
|
||||||
return _config
|
|
||||||
}
|
|
||||||
|
|
||||||
set {
|
|
||||||
guard status == .notConnected else {
|
|
||||||
DefaultSocketLogger.Logger.error("Tried setting config after calling connect",
|
|
||||||
type: SocketIOClient.logType)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
_config = newValue
|
|
||||||
|
|
||||||
if socketURL.absoluteString.hasPrefix("https://") {
|
|
||||||
_config.insert(.secure(true))
|
|
||||||
}
|
|
||||||
|
|
||||||
_config.insert(.path("/socket.io/"), replacing: false)
|
|
||||||
setConfigs()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// If `true`, this client will try and reconnect on any disconnects.
|
|
||||||
@objc
|
|
||||||
public var reconnects = true
|
|
||||||
|
|
||||||
/// The number of seconds to wait before attempting to reconnect.
|
|
||||||
@objc
|
|
||||||
public var reconnectWait = 10
|
|
||||||
|
|
||||||
/// The session id of this client.
|
/// The session id of this client.
|
||||||
@objc
|
@objc
|
||||||
public var sid: String? {
|
public var sid: String {
|
||||||
return engine?.sid
|
guard let engine = manager?.engine else { return "" }
|
||||||
|
|
||||||
|
return nsp == "/" ? engine.sid : "\(nsp)#\(engine.sid)"
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The URL of the socket.io server.
|
|
||||||
///
|
|
||||||
/// If changed after calling `init`, `forceNew` must be set to `true`, or it will only connect to the url set in the
|
|
||||||
/// init.
|
|
||||||
@objc
|
|
||||||
public var socketURL: URL
|
|
||||||
|
|
||||||
/// A list of packets that are waiting for binary data.
|
|
||||||
///
|
|
||||||
/// The way that socket.io works all data should be sent directly after each packet.
|
|
||||||
/// So this should ideally be an array of one packet waiting for data.
|
|
||||||
///
|
|
||||||
/// **This should not be modified directly.**
|
|
||||||
public var waitingPackets = [SocketPacket]()
|
|
||||||
|
|
||||||
/// A handler that will be called on any event.
|
/// A handler that will be called on any event.
|
||||||
public private(set) var anyHandler: ((SocketAnyEvent) -> ())?
|
public private(set) var anyHandler: ((SocketAnyEvent) -> ())?
|
||||||
|
|
||||||
/// The engine for this client.
|
|
||||||
@objc
|
|
||||||
public internal(set) var engine: SocketEngineSpec?
|
|
||||||
|
|
||||||
/// The array of handlers for this socket.
|
/// The array of handlers for this socket.
|
||||||
public private(set) var handlers = [SocketEventHandler]()
|
public private(set) var handlers = [SocketEventHandler]()
|
||||||
|
|
||||||
|
/// The manager for this socket.
|
||||||
|
@objc
|
||||||
|
public private(set) weak var manager: SocketManagerSpec?
|
||||||
|
|
||||||
/// The status of this client.
|
/// The status of this client.
|
||||||
@objc
|
@objc
|
||||||
public private(set) var status = SocketIOClientStatus.notConnected {
|
public private(set) var status = SocketIOStatus.notConnected {
|
||||||
didSet {
|
didSet {
|
||||||
switch status {
|
|
||||||
case .connected:
|
|
||||||
reconnecting = false
|
|
||||||
currentReconnectAttempt = 0
|
|
||||||
default:
|
|
||||||
break
|
|
||||||
}
|
|
||||||
|
|
||||||
handleClientEvent(.statusChange, data: [status])
|
handleClientEvent(.statusChange, data: [status])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -134,60 +78,29 @@ open class SocketIOClient : NSObject, SocketIOClientSpec, SocketEngineClient, So
|
|||||||
var ackHandlers = SocketAckManager()
|
var ackHandlers = SocketAckManager()
|
||||||
|
|
||||||
private(set) var currentAck = -1
|
private(set) var currentAck = -1
|
||||||
private(set) var reconnectAttempts = -1
|
|
||||||
|
|
||||||
private var _config: SocketIOClientConfiguration
|
private lazy var logType = "SocketIOClient{\(nsp)}"
|
||||||
private var currentReconnectAttempt = 0
|
|
||||||
private var reconnecting = false
|
|
||||||
|
|
||||||
// MARK: Initializers
|
// MARK: Initializers
|
||||||
|
|
||||||
/// Type safe way to create a new SocketIOClient. `opts` can be omitted.
|
/// Type safe way to create a new SocketIOClient. `opts` can be omitted.
|
||||||
///
|
///
|
||||||
|
/// - parameter manager: The manager for this socket.
|
||||||
/// - parameter socketURL: The url of the socket.io server.
|
/// - parameter socketURL: The url of the socket.io server.
|
||||||
/// - parameter config: The config for this socket.
|
@objc
|
||||||
public init(socketURL: URL, config: SocketIOClientConfiguration = []) {
|
public init(manager: SocketManagerSpec, nsp: String) {
|
||||||
self._config = config
|
self.manager = manager
|
||||||
self.socketURL = socketURL
|
self.nsp = nsp
|
||||||
|
|
||||||
if socketURL.absoluteString.hasPrefix("https://") {
|
|
||||||
self._config.insert(.secure(true))
|
|
||||||
}
|
|
||||||
|
|
||||||
self._config.insert(.path("/socket.io/"), replacing: false)
|
|
||||||
|
|
||||||
super.init()
|
super.init()
|
||||||
|
|
||||||
setConfigs()
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Not so type safe way to create a SocketIOClient, meant for Objective-C compatiblity.
|
|
||||||
/// If using Swift it's recommended to use `init(socketURL: NSURL, options: Set<SocketIOClientOption>)`
|
|
||||||
///
|
|
||||||
/// - parameter socketURL: The url of the socket.io server.
|
|
||||||
/// - parameter config: The config for this socket.
|
|
||||||
@objc
|
|
||||||
public convenience init(socketURL: NSURL, config: NSDictionary?) {
|
|
||||||
self.init(socketURL: socketURL as URL, config: config?.toSocketConfiguration() ?? [])
|
|
||||||
}
|
}
|
||||||
|
|
||||||
deinit {
|
deinit {
|
||||||
DefaultSocketLogger.Logger.log("Client is being released", type: SocketIOClient.logType)
|
DefaultSocketLogger.Logger.log("Client is being released", type: logType)
|
||||||
engine?.disconnect(reason: "Client Deinit")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// MARK: Methods
|
// MARK: Methods
|
||||||
|
|
||||||
private func addEngine() {
|
|
||||||
DefaultSocketLogger.Logger.log("Adding engine", type: SocketIOClient.logType)
|
|
||||||
|
|
||||||
engine?.engineQueue.sync {
|
|
||||||
self.engine?.client = nil
|
|
||||||
}
|
|
||||||
|
|
||||||
engine = SocketEngine(client: self, url: socketURL, config: config)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Connect to the server. The same as calling `connect(timeoutAfter:withHandler:)` with a timeout of 0.
|
/// Connect to the server. The same as calling `connect(timeoutAfter:withHandler:)` with a timeout of 0.
|
||||||
///
|
///
|
||||||
/// Only call after adding your event listeners, unless you know what you're doing.
|
/// Only call after adding your event listeners, unless you know what you're doing.
|
||||||
@ -207,27 +120,22 @@ open class SocketIOClient : NSObject, SocketIOClientSpec, SocketEngineClient, So
|
|||||||
open func connect(timeoutAfter: Double, withHandler handler: (() -> ())?) {
|
open func connect(timeoutAfter: Double, withHandler handler: (() -> ())?) {
|
||||||
assert(timeoutAfter >= 0, "Invalid timeout: \(timeoutAfter)")
|
assert(timeoutAfter >= 0, "Invalid timeout: \(timeoutAfter)")
|
||||||
|
|
||||||
guard status != .connected else {
|
guard let manager = self.manager, status != .connected else {
|
||||||
DefaultSocketLogger.Logger.log("Tried connecting on an already connected socket",
|
DefaultSocketLogger.Logger.log("Tried connecting on an already connected socket", type: logType)
|
||||||
type: SocketIOClient.logType)
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
status = .connecting
|
status = .connecting
|
||||||
|
|
||||||
if engine == nil || forceNew {
|
manager.connectSocket(self)
|
||||||
addEngine()
|
|
||||||
}
|
|
||||||
|
|
||||||
engine?.connect()
|
|
||||||
|
|
||||||
guard timeoutAfter != 0 else { return }
|
guard timeoutAfter != 0 else { return }
|
||||||
|
|
||||||
handleQueue.asyncAfter(deadline: DispatchTime.now() + timeoutAfter) {[weak self] in
|
manager.handleQueue.asyncAfter(deadline: DispatchTime.now() + timeoutAfter) {[weak self] in
|
||||||
guard let this = self, this.status == .connecting || this.status == .notConnected else { return }
|
guard let this = self, this.status == .connecting || this.status == .notConnected else { return }
|
||||||
|
|
||||||
this.status = .disconnected
|
this.status = .disconnected
|
||||||
this.engine?.disconnect(reason: "Connect timeout")
|
this.leaveNamespace()
|
||||||
|
|
||||||
handler?()
|
handler?()
|
||||||
}
|
}
|
||||||
@ -246,7 +154,7 @@ open class SocketIOClient : NSObject, SocketIOClientSpec, SocketEngineClient, So
|
|||||||
open func didConnect(toNamespace namespace: String) {
|
open func didConnect(toNamespace namespace: String) {
|
||||||
guard status != .connected else { return }
|
guard status != .connected else { return }
|
||||||
|
|
||||||
DefaultSocketLogger.Logger.log("Socket connected", type: SocketIOClient.logType)
|
DefaultSocketLogger.Logger.log("Socket connected", type: logType)
|
||||||
|
|
||||||
status = .connected
|
status = .connected
|
||||||
|
|
||||||
@ -259,21 +167,22 @@ open class SocketIOClient : NSObject, SocketIOClientSpec, SocketEngineClient, So
|
|||||||
open func didDisconnect(reason: String) {
|
open func didDisconnect(reason: String) {
|
||||||
guard status != .disconnected else { return }
|
guard status != .disconnected else { return }
|
||||||
|
|
||||||
DefaultSocketLogger.Logger.log("Disconnected: \(reason)", type: SocketIOClient.logType)
|
DefaultSocketLogger.Logger.log("Disconnected: \(reason)", type: logType)
|
||||||
|
|
||||||
reconnecting = false
|
|
||||||
status = .disconnected
|
status = .disconnected
|
||||||
|
|
||||||
// Make sure the engine is actually dead.
|
|
||||||
engine?.disconnect(reason: reason)
|
|
||||||
handleClientEvent(.disconnect, data: [reason])
|
handleClientEvent(.disconnect, data: [reason])
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Disconnects the socket.
|
/// Disconnects the socket.
|
||||||
|
///
|
||||||
|
/// This will cause the socket to leave the namespace it is associated to, as well as remove itself from the
|
||||||
|
/// `manager`.
|
||||||
@objc
|
@objc
|
||||||
open func disconnect() {
|
open func disconnect() {
|
||||||
DefaultSocketLogger.Logger.log("Closing socket", type: SocketIOClient.logType)
|
DefaultSocketLogger.Logger.log("Closing socket", type: logType)
|
||||||
|
|
||||||
|
leaveNamespace()
|
||||||
didDisconnect(reason: "Disconnect")
|
didDisconnect(reason: "Disconnect")
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -289,7 +198,7 @@ open class SocketIOClient : NSObject, SocketIOClientSpec, SocketEngineClient, So
|
|||||||
try emit(event, with: items.map({ try $0.socketRepresentation() }))
|
try emit(event, with: items.map({ try $0.socketRepresentation() }))
|
||||||
} catch let err {
|
} catch let err {
|
||||||
DefaultSocketLogger.Logger.error("Error creating socketRepresentation for emit: \(event), \(items)",
|
DefaultSocketLogger.Logger.error("Error creating socketRepresentation for emit: \(event), \(items)",
|
||||||
type: SocketIOClient.logType)
|
type: logType)
|
||||||
|
|
||||||
handleClientEvent(.error, data: [event, items, err])
|
handleClientEvent(.error, data: [event, items, err])
|
||||||
}
|
}
|
||||||
@ -333,7 +242,7 @@ open class SocketIOClient : NSObject, SocketIOClientSpec, SocketEngineClient, So
|
|||||||
return emitWithAck(event, with: try items.map({ try $0.socketRepresentation() }))
|
return emitWithAck(event, with: try items.map({ try $0.socketRepresentation() }))
|
||||||
} catch let err {
|
} catch let err {
|
||||||
DefaultSocketLogger.Logger.error("Error creating socketRepresentation for emit: \(event), \(items)",
|
DefaultSocketLogger.Logger.error("Error creating socketRepresentation for emit: \(event), \(items)",
|
||||||
type: SocketIOClient.logType)
|
type: logType)
|
||||||
|
|
||||||
handleClientEvent(.error, data: [event, items, err])
|
handleClientEvent(.error, data: [event, items, err])
|
||||||
|
|
||||||
@ -371,9 +280,9 @@ open class SocketIOClient : NSObject, SocketIOClientSpec, SocketEngineClient, So
|
|||||||
let packet = SocketPacket.packetFromEmit(data, id: ack ?? -1, nsp: nsp, ack: false)
|
let packet = SocketPacket.packetFromEmit(data, id: ack ?? -1, nsp: nsp, ack: false)
|
||||||
let str = packet.packetString
|
let str = packet.packetString
|
||||||
|
|
||||||
DefaultSocketLogger.Logger.log("Emitting: \(str)", type: SocketIOClient.logType)
|
DefaultSocketLogger.Logger.log("Emitting: \(str)", type: logType)
|
||||||
|
|
||||||
engine?.send(str, withData: packet.binary)
|
manager?.engine?.send(str, withData: packet.binary)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Call when you wish to tell the server that you've received the event for `ack`.
|
/// Call when you wish to tell the server that you've received the event for `ack`.
|
||||||
@ -388,69 +297,9 @@ open class SocketIOClient : NSObject, SocketIOClientSpec, SocketEngineClient, So
|
|||||||
let packet = SocketPacket.packetFromEmit(items, id: ack, nsp: nsp, ack: true)
|
let packet = SocketPacket.packetFromEmit(items, id: ack, nsp: nsp, ack: true)
|
||||||
let str = packet.packetString
|
let str = packet.packetString
|
||||||
|
|
||||||
DefaultSocketLogger.Logger.log("Emitting Ack: \(str)", type: SocketIOClient.logType)
|
DefaultSocketLogger.Logger.log("Emitting Ack: \(str)", type: logType)
|
||||||
|
|
||||||
engine?.send(str, withData: packet.binary)
|
manager?.engine?.send(str, withData: packet.binary)
|
||||||
}
|
|
||||||
|
|
||||||
/// Called when the engine closes.
|
|
||||||
///
|
|
||||||
/// - parameter reason: The reason that the engine closed.
|
|
||||||
open func engineDidClose(reason: String) {
|
|
||||||
handleQueue.async {
|
|
||||||
self._engineDidClose(reason: reason)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private func _engineDidClose(reason: String) {
|
|
||||||
waitingPackets.removeAll()
|
|
||||||
|
|
||||||
if status != .disconnected {
|
|
||||||
status = .notConnected
|
|
||||||
}
|
|
||||||
|
|
||||||
if status == .disconnected || !reconnects {
|
|
||||||
didDisconnect(reason: reason)
|
|
||||||
} else if !reconnecting {
|
|
||||||
reconnecting = true
|
|
||||||
tryReconnect(reason: reason)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Called when the engine errors.
|
|
||||||
///
|
|
||||||
/// - parameter reason: The reason the engine errored.
|
|
||||||
open func engineDidError(reason: String) {
|
|
||||||
handleQueue.async {
|
|
||||||
self._engineDidError(reason: reason)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private func _engineDidError(reason: String) {
|
|
||||||
DefaultSocketLogger.Logger.error("\(reason)", type: SocketIOClient.logType)
|
|
||||||
|
|
||||||
handleClientEvent(.error, data: [reason])
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Called when the engine opens.
|
|
||||||
///
|
|
||||||
/// - parameter reason: The reason the engine opened.
|
|
||||||
open func engineDidOpen(reason: String) {
|
|
||||||
handleQueue.async {
|
|
||||||
self._engineDidOpen(reason: reason)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private func _engineDidOpen(reason: String) {
|
|
||||||
DefaultSocketLogger.Logger.log("Engine opened \(reason)", type: SocketIOClient.logType)
|
|
||||||
|
|
||||||
guard nsp != "/" else {
|
|
||||||
didConnect(toNamespace: "/")
|
|
||||||
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
joinNamespace(nsp)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Called when socket.io has acked one of our emits. Causes the corresponding ack callback to be called.
|
/// Called when socket.io has acked one of our emits. Causes the corresponding ack callback to be called.
|
||||||
@ -459,11 +308,19 @@ open class SocketIOClient : NSObject, SocketIOClientSpec, SocketEngineClient, So
|
|||||||
/// - parameter data: The data sent back with this ack.
|
/// - parameter data: The data sent back with this ack.
|
||||||
@objc
|
@objc
|
||||||
open func handleAck(_ ack: Int, data: [Any]) {
|
open func handleAck(_ ack: Int, data: [Any]) {
|
||||||
guard status == .connected else { return }
|
guard status == .connected, let manager = self.manager else { return }
|
||||||
|
|
||||||
DefaultSocketLogger.Logger.log("Handling ack: \(ack) with data: \(data)", type: SocketIOClient.logType)
|
DefaultSocketLogger.Logger.log("Handling ack: \(ack) with data: \(data)", type: logType)
|
||||||
|
|
||||||
ackHandlers.executeAck(ack, with: data, onQueue: handleQueue)
|
ackHandlers.executeAck(ack, with: data, onQueue: manager.handleQueue)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Called on socket.io specific events.
|
||||||
|
///
|
||||||
|
/// - parameter event: The `SocketClientEvent`.
|
||||||
|
/// - parameter data: The data for this event.
|
||||||
|
open func handleClientEvent(_ event: SocketClientEvent, data: [Any]) {
|
||||||
|
handleEvent(event.rawValue, data: data, isInternalMessage: true)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Called when we get an event from socket.io.
|
/// Called when we get an event from socket.io.
|
||||||
@ -476,7 +333,7 @@ open class SocketIOClient : NSObject, SocketIOClientSpec, SocketEngineClient, So
|
|||||||
open func handleEvent(_ event: String, data: [Any], isInternalMessage: Bool, withAck ack: Int = -1) {
|
open func handleEvent(_ event: String, data: [Any], isInternalMessage: Bool, withAck ack: Int = -1) {
|
||||||
guard status == .connected || isInternalMessage else { return }
|
guard status == .connected || isInternalMessage else { return }
|
||||||
|
|
||||||
DefaultSocketLogger.Logger.log("Handling event: \(event) with data: \(data)", type: SocketIOClient.logType)
|
DefaultSocketLogger.Logger.log("Handling event: \(event) with data: \(data)", type: logType)
|
||||||
|
|
||||||
anyHandler?(SocketAnyEvent(event: event, items: data))
|
anyHandler?(SocketAnyEvent(event: event, items: data))
|
||||||
|
|
||||||
@ -485,36 +342,45 @@ open class SocketIOClient : NSObject, SocketIOClientSpec, SocketEngineClient, So
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Called on socket.io specific events.
|
/// Causes a client to handle a socket.io packet. The namespace for the packet must match the namespace of the
|
||||||
|
/// socket.
|
||||||
///
|
///
|
||||||
/// - parameter event: The `SocketClientEvent`.
|
/// - parameter packet: The packet to handle.
|
||||||
/// - parameter data: The data for this event.
|
open func handlePacket(_ packet: SocketPacket) {
|
||||||
open func handleClientEvent(_ event: SocketClientEvent, data: [Any]) {
|
guard packet.nsp == nsp else { return }
|
||||||
handleEvent(event.rawValue, data: data, isInternalMessage: true)
|
|
||||||
|
switch packet.type {
|
||||||
|
case .event, .binaryEvent:
|
||||||
|
handleEvent(packet.event, data: packet.args, isInternalMessage: false, withAck: packet.id)
|
||||||
|
case .ack, .binaryAck:
|
||||||
|
handleAck(packet.id, data: packet.data)
|
||||||
|
case .connect:
|
||||||
|
didConnect(toNamespace: nsp)
|
||||||
|
case .disconnect:
|
||||||
|
didDisconnect(reason: "Got Disconnect")
|
||||||
|
case .error:
|
||||||
|
handleEvent("error", data: packet.data, isInternalMessage: true, withAck: packet.id)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Call when you wish to leave a namespace and return to the default namespace.
|
/// Call when you wish to leave a namespace and disconnect this socket.
|
||||||
@objc
|
@objc
|
||||||
open func leaveNamespace() {
|
open func leaveNamespace() {
|
||||||
guard nsp != "/" else { return }
|
guard nsp != "/" else { return }
|
||||||
|
|
||||||
engine?.send("1\(nsp)", withData: [])
|
status = .disconnected
|
||||||
nsp = "/"
|
|
||||||
|
manager?.disconnectSocket(self)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Joins `namespace`.
|
/// Joins `nsp`.
|
||||||
///
|
|
||||||
/// **Do not use this to join the default namespace.** Instead call `leaveNamespace`.
|
|
||||||
///
|
|
||||||
/// - parameter namespace: The namespace to join.
|
|
||||||
@objc
|
@objc
|
||||||
open func joinNamespace(_ namespace: String) {
|
open func joinNamespace() {
|
||||||
guard namespace != "/" else { return }
|
guard nsp != "/" else { return }
|
||||||
|
|
||||||
DefaultSocketLogger.Logger.log("Joining namespace \(namespace)", type: SocketIOClient.logType)
|
DefaultSocketLogger.Logger.log("Joining namespace \(nsp)", type: logType)
|
||||||
|
|
||||||
nsp = namespace
|
manager?.engine?.send("0\(nsp)", withData: [])
|
||||||
engine?.send("0\(nsp)", withData: [])
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Removes handler(s) for a client event.
|
/// Removes handler(s) for a client event.
|
||||||
@ -533,7 +399,7 @@ open class SocketIOClient : NSObject, SocketIOClientSpec, SocketEngineClient, So
|
|||||||
/// - parameter event: The event to remove handlers for.
|
/// - parameter event: The event to remove handlers for.
|
||||||
@objc
|
@objc
|
||||||
open func off(_ event: String) {
|
open func off(_ event: String) {
|
||||||
DefaultSocketLogger.Logger.log("Removing handler for event: \(event)", type: SocketIOClient.logType)
|
DefaultSocketLogger.Logger.log("Removing handler for event: \(event)", type: logType)
|
||||||
|
|
||||||
handlers = handlers.filter({ $0.event != event })
|
handlers = handlers.filter({ $0.event != event })
|
||||||
}
|
}
|
||||||
@ -545,7 +411,7 @@ open class SocketIOClient : NSObject, SocketIOClientSpec, SocketEngineClient, So
|
|||||||
/// - parameter id: The UUID of the handler you wish to remove.
|
/// - parameter id: The UUID of the handler you wish to remove.
|
||||||
@objc
|
@objc
|
||||||
open func off(id: UUID) {
|
open func off(id: UUID) {
|
||||||
DefaultSocketLogger.Logger.log("Removing handler with id: \(id)", type: SocketIOClient.logType)
|
DefaultSocketLogger.Logger.log("Removing handler with id: \(id)", type: logType)
|
||||||
|
|
||||||
handlers = handlers.filter({ $0.id != id })
|
handlers = handlers.filter({ $0.id != id })
|
||||||
}
|
}
|
||||||
@ -558,7 +424,7 @@ open class SocketIOClient : NSObject, SocketIOClientSpec, SocketEngineClient, So
|
|||||||
@objc
|
@objc
|
||||||
@discardableResult
|
@discardableResult
|
||||||
open func on(_ event: String, callback: @escaping NormalCallback) -> UUID {
|
open func on(_ event: String, callback: @escaping NormalCallback) -> UUID {
|
||||||
DefaultSocketLogger.Logger.log("Adding handler for event: \(event)", type: SocketIOClient.logType)
|
DefaultSocketLogger.Logger.log("Adding handler for event: \(event)", type: logType)
|
||||||
|
|
||||||
let handler = SocketEventHandler(event: event, id: UUID(), callback: callback)
|
let handler = SocketEventHandler(event: event, id: UUID(), callback: callback)
|
||||||
handlers.append(handler)
|
handlers.append(handler)
|
||||||
@ -581,12 +447,7 @@ open class SocketIOClient : NSObject, SocketIOClientSpec, SocketEngineClient, So
|
|||||||
/// - returns: A unique id for the handler that can be used to remove it.
|
/// - returns: A unique id for the handler that can be used to remove it.
|
||||||
@discardableResult
|
@discardableResult
|
||||||
open func on(clientEvent event: SocketClientEvent, callback: @escaping NormalCallback) -> UUID {
|
open func on(clientEvent event: SocketClientEvent, callback: @escaping NormalCallback) -> UUID {
|
||||||
DefaultSocketLogger.Logger.log("Adding handler for event: \(event)", type: SocketIOClient.logType)
|
return on(event.rawValue, callback: callback)
|
||||||
|
|
||||||
let handler = SocketEventHandler(event: event.rawValue, id: UUID(), callback: callback)
|
|
||||||
handlers.append(handler)
|
|
||||||
|
|
||||||
return handler.id
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Adds a single-use handler for a client event.
|
/// Adds a single-use handler for a client event.
|
||||||
@ -607,7 +468,7 @@ open class SocketIOClient : NSObject, SocketIOClientSpec, SocketEngineClient, So
|
|||||||
@objc
|
@objc
|
||||||
@discardableResult
|
@discardableResult
|
||||||
open func once(_ event: String, callback: @escaping NormalCallback) -> UUID {
|
open func once(_ event: String, callback: @escaping NormalCallback) -> UUID {
|
||||||
DefaultSocketLogger.Logger.log("Adding once handler for event: \(event)", type: SocketIOClient.logType)
|
DefaultSocketLogger.Logger.log("Adding once handler for event: \(event)", type: logType)
|
||||||
|
|
||||||
let id = UUID()
|
let id = UUID()
|
||||||
|
|
||||||
@ -630,31 +491,10 @@ open class SocketIOClient : NSObject, SocketIOClientSpec, SocketEngineClient, So
|
|||||||
anyHandler = handler
|
anyHandler = handler
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Called when the engine has a message that must be parsed.
|
|
||||||
///
|
|
||||||
/// - parameter msg: The message that needs parsing.
|
|
||||||
public func parseEngineMessage(_ msg: String) {
|
|
||||||
DefaultSocketLogger.Logger.log("Should parse message: \(msg)", type: SocketIOClient.logType)
|
|
||||||
|
|
||||||
handleQueue.async { self.parseSocketMessage(msg) }
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Called when the engine receives binary data.
|
|
||||||
///
|
|
||||||
/// - parameter data: The data the engine received.
|
|
||||||
public func parseEngineBinaryData(_ data: Data) {
|
|
||||||
handleQueue.async { self.parseBinaryData(data) }
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Tries to reconnect to the server.
|
/// Tries to reconnect to the server.
|
||||||
///
|
|
||||||
/// This will cause a `disconnect` event to be emitted, as well as an `reconnectAttempt` event.
|
|
||||||
@objc
|
@objc
|
||||||
open func reconnect() {
|
@available(*, unavailable, message: "Call the manager's reconnect method")
|
||||||
guard !reconnecting else { return }
|
open func reconnect() { }
|
||||||
|
|
||||||
engine?.disconnect(reason: "manual reconnect")
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Removes all handlers.
|
/// Removes all handlers.
|
||||||
///
|
///
|
||||||
@ -664,54 +504,15 @@ open class SocketIOClient : NSObject, SocketIOClientSpec, SocketEngineClient, So
|
|||||||
handlers.removeAll(keepingCapacity: false)
|
handlers.removeAll(keepingCapacity: false)
|
||||||
}
|
}
|
||||||
|
|
||||||
private func tryReconnect(reason: String) {
|
/// Puts the socket back into the connecting state.
|
||||||
guard reconnecting else { return }
|
/// Called when the manager detects a broken connection, or when a manual reconnect is triggered.
|
||||||
|
///
|
||||||
|
/// - parameter reason: The reason this socket is reconnecting.
|
||||||
|
@objc
|
||||||
|
open func setReconnecting(reason: String) {
|
||||||
|
status = .connecting
|
||||||
|
|
||||||
DefaultSocketLogger.Logger.log("Starting reconnect", type: SocketIOClient.logType)
|
|
||||||
handleClientEvent(.reconnect, data: [reason])
|
handleClientEvent(.reconnect, data: [reason])
|
||||||
|
|
||||||
_tryReconnect()
|
|
||||||
}
|
|
||||||
|
|
||||||
private func _tryReconnect() {
|
|
||||||
guard reconnects && reconnecting && status != .disconnected else { return }
|
|
||||||
|
|
||||||
if reconnectAttempts != -1 && currentReconnectAttempt + 1 > reconnectAttempts {
|
|
||||||
return didDisconnect(reason: "Reconnect Failed")
|
|
||||||
}
|
|
||||||
|
|
||||||
DefaultSocketLogger.Logger.log("Trying to reconnect", type: SocketIOClient.logType)
|
|
||||||
handleClientEvent(.reconnectAttempt, data: [(reconnectAttempts - currentReconnectAttempt)])
|
|
||||||
|
|
||||||
currentReconnectAttempt += 1
|
|
||||||
connect()
|
|
||||||
|
|
||||||
handleQueue.asyncAfter(deadline: DispatchTime.now() + Double(reconnectWait), execute: _tryReconnect)
|
|
||||||
}
|
|
||||||
|
|
||||||
private func setConfigs() {
|
|
||||||
for option in config {
|
|
||||||
switch option {
|
|
||||||
case let .reconnects(reconnects):
|
|
||||||
self.reconnects = reconnects
|
|
||||||
case let .reconnectAttempts(attempts):
|
|
||||||
reconnectAttempts = attempts
|
|
||||||
case let .reconnectWait(wait):
|
|
||||||
reconnectWait = abs(wait)
|
|
||||||
case let .nsp(nsp):
|
|
||||||
self.nsp = nsp
|
|
||||||
case let .log(log):
|
|
||||||
DefaultSocketLogger.Logger.log = log
|
|
||||||
case let .logger(logger):
|
|
||||||
DefaultSocketLogger.Logger = logger
|
|
||||||
case let .handleQueue(queue):
|
|
||||||
handleQueue = queue
|
|
||||||
case let .forceNew(force):
|
|
||||||
forceNew = force
|
|
||||||
default:
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Test properties
|
// Test properties
|
||||||
@ -724,14 +525,10 @@ open class SocketIOClient : NSObject, SocketIOClientSpec, SocketEngineClient, So
|
|||||||
status = .connected
|
status = .connected
|
||||||
}
|
}
|
||||||
|
|
||||||
func setTestStatus(_ status: SocketIOClientStatus) {
|
func setTestStatus(_ status: SocketIOStatus) {
|
||||||
self.status = status
|
self.status = status
|
||||||
}
|
}
|
||||||
|
|
||||||
func setTestEngine(_ engine: SocketEngineSpec?) {
|
|
||||||
self.engine = engine
|
|
||||||
}
|
|
||||||
|
|
||||||
func emitTest(event: String, _ data: Any...) {
|
func emitTest(event: String, _ data: Any...) {
|
||||||
emit([event] + data)
|
emit([event] + data)
|
||||||
}
|
}
|
||||||
|
|||||||
@ -125,5 +125,12 @@ public struct SocketIOClientConfiguration : ExpressibleByArrayLiteral, Collectio
|
|||||||
|
|
||||||
backingArray.append(element)
|
backingArray.append(element)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Declares that a type can set configs from a `SocketIOClientConfiguration`.
|
||||||
|
public protocol ConfigSettable {
|
||||||
|
/// Called when an `ConfigSettable` should set/update its configs from a given configuration.
|
||||||
|
///
|
||||||
|
/// - parameter config: The `SocketIOClientConfiguration` that should be used to set/update configs.
|
||||||
|
mutating func setConfigs(_ config: SocketIOClientConfiguration)
|
||||||
}
|
}
|
||||||
|
|||||||
@ -40,10 +40,6 @@ public enum SocketIOClientOption : ClientOption {
|
|||||||
/// An array of cookies that will be sent during the initial connection.
|
/// An array of cookies that will be sent during the initial connection.
|
||||||
case cookies([HTTPCookie])
|
case cookies([HTTPCookie])
|
||||||
|
|
||||||
/// Deprecated
|
|
||||||
@available(*, deprecated, message: "No longer needed in socket.io 2.0+")
|
|
||||||
case doubleEncodeUTF8(Bool)
|
|
||||||
|
|
||||||
/// Any extra HTTP headers that should be sent during the initial connection.
|
/// Any extra HTTP headers that should be sent during the initial connection.
|
||||||
case extraHeaders([String: String])
|
case extraHeaders([String: String])
|
||||||
|
|
||||||
@ -67,10 +63,6 @@ public enum SocketIOClientOption : ClientOption {
|
|||||||
/// Used to pass in a custom logger.
|
/// Used to pass in a custom logger.
|
||||||
case logger(SocketLogger)
|
case logger(SocketLogger)
|
||||||
|
|
||||||
/// The namespace that this client should connect to. Can be changed during use using the `joinNamespace`
|
|
||||||
/// and `leaveNamespace` methods on `SocketIOClient`.
|
|
||||||
case nsp(String)
|
|
||||||
|
|
||||||
/// A custom path to socket.io. Only use this if the socket.io server is configured to look for this path.
|
/// A custom path to socket.io. Only use this if the socket.io server is configured to look for this path.
|
||||||
case path(String)
|
case path(String)
|
||||||
|
|
||||||
@ -96,11 +88,6 @@ public enum SocketIOClientOption : ClientOption {
|
|||||||
/// Sets an NSURLSessionDelegate for the underlying engine. Useful if you need to handle self-signed certs.
|
/// Sets an NSURLSessionDelegate for the underlying engine. Useful if you need to handle self-signed certs.
|
||||||
case sessionDelegate(URLSessionDelegate)
|
case sessionDelegate(URLSessionDelegate)
|
||||||
|
|
||||||
/// If passed `true`, the WebSocket transport will try and use voip logic to keep network connections open in
|
|
||||||
/// the background. **This option is experimental as socket.io shouldn't be used for background communication.**
|
|
||||||
@available(*, deprecated, message: "No longer has any effect, and will be removed in v11.0")
|
|
||||||
case voipEnabled(Bool)
|
|
||||||
|
|
||||||
// MARK: Properties
|
// MARK: Properties
|
||||||
|
|
||||||
/// The description of this option.
|
/// The description of this option.
|
||||||
@ -114,8 +101,6 @@ public enum SocketIOClientOption : ClientOption {
|
|||||||
description = "connectParams"
|
description = "connectParams"
|
||||||
case .cookies:
|
case .cookies:
|
||||||
description = "cookies"
|
description = "cookies"
|
||||||
case .doubleEncodeUTF8:
|
|
||||||
description = "doubleEncodeUTF8"
|
|
||||||
case .extraHeaders:
|
case .extraHeaders:
|
||||||
description = "extraHeaders"
|
description = "extraHeaders"
|
||||||
case .forceNew:
|
case .forceNew:
|
||||||
@ -130,8 +115,6 @@ public enum SocketIOClientOption : ClientOption {
|
|||||||
description = "log"
|
description = "log"
|
||||||
case .logger:
|
case .logger:
|
||||||
description = "logger"
|
description = "logger"
|
||||||
case .nsp:
|
|
||||||
description = "nsp"
|
|
||||||
case .path:
|
case .path:
|
||||||
description = "path"
|
description = "path"
|
||||||
case .reconnects:
|
case .reconnects:
|
||||||
@ -148,8 +131,6 @@ public enum SocketIOClientOption : ClientOption {
|
|||||||
description = "security"
|
description = "security"
|
||||||
case .sessionDelegate:
|
case .sessionDelegate:
|
||||||
description = "sessionDelegate"
|
description = "sessionDelegate"
|
||||||
case .voipEnabled:
|
|
||||||
description = "voipEnabled"
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return description
|
return description
|
||||||
@ -165,8 +146,6 @@ public enum SocketIOClientOption : ClientOption {
|
|||||||
value = params
|
value = params
|
||||||
case let .cookies(cookies):
|
case let .cookies(cookies):
|
||||||
value = cookies
|
value = cookies
|
||||||
case let .doubleEncodeUTF8(encode):
|
|
||||||
value = encode
|
|
||||||
case let .extraHeaders(headers):
|
case let .extraHeaders(headers):
|
||||||
value = headers
|
value = headers
|
||||||
case let .forceNew(force):
|
case let .forceNew(force):
|
||||||
@ -181,8 +160,6 @@ public enum SocketIOClientOption : ClientOption {
|
|||||||
value = log
|
value = log
|
||||||
case let .logger(logger):
|
case let .logger(logger):
|
||||||
value = logger
|
value = logger
|
||||||
case let .nsp(nsp):
|
|
||||||
value = nsp
|
|
||||||
case let .path(path):
|
case let .path(path):
|
||||||
value = path
|
value = path
|
||||||
case let .reconnects(reconnects):
|
case let .reconnects(reconnects):
|
||||||
@ -199,8 +176,6 @@ public enum SocketIOClientOption : ClientOption {
|
|||||||
value = signed
|
value = signed
|
||||||
case let .sessionDelegate(delegate):
|
case let .sessionDelegate(delegate):
|
||||||
value = delegate
|
value = delegate
|
||||||
case let .voipEnabled(enabled):
|
|
||||||
value = enabled
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return value
|
return value
|
||||||
|
|||||||
@ -32,22 +32,19 @@ public protocol SocketIOClientSpec : class {
|
|||||||
/// A handler that will be called on any event.
|
/// A handler that will be called on any event.
|
||||||
var anyHandler: ((SocketAnyEvent) -> ())? { get }
|
var anyHandler: ((SocketAnyEvent) -> ())? { get }
|
||||||
|
|
||||||
/// The configuration for this client.
|
|
||||||
var config: SocketIOClientConfiguration { get set }
|
|
||||||
|
|
||||||
/// The queue that all interaction with the client must be on.
|
|
||||||
var handleQueue: DispatchQueue { get set }
|
|
||||||
|
|
||||||
/// The array of handlers for this socket.
|
/// The array of handlers for this socket.
|
||||||
var handlers: [SocketEventHandler] { get }
|
var handlers: [SocketEventHandler] { get }
|
||||||
|
|
||||||
|
/// The manager for this socket.
|
||||||
|
var manager: SocketManagerSpec? { get }
|
||||||
|
|
||||||
/// The namespace that this socket is currently connected to.
|
/// The namespace that this socket is currently connected to.
|
||||||
///
|
///
|
||||||
/// **Must** start with a `/`.
|
/// **Must** start with a `/`.
|
||||||
var nsp: String { get set }
|
var nsp: String { get }
|
||||||
|
|
||||||
/// The status of this client.
|
/// The status of this client.
|
||||||
var status: SocketIOClientStatus { get }
|
var status: SocketIOStatus { get }
|
||||||
|
|
||||||
// MARK: Methods
|
// MARK: Methods
|
||||||
|
|
||||||
@ -126,6 +123,12 @@ public protocol SocketIOClientSpec : class {
|
|||||||
/// - parameter data: The data sent back with this ack.
|
/// - parameter data: The data sent back with this ack.
|
||||||
func handleAck(_ ack: Int, data: [Any])
|
func handleAck(_ ack: Int, data: [Any])
|
||||||
|
|
||||||
|
/// Called on socket.io specific events.
|
||||||
|
///
|
||||||
|
/// - parameter event: The `SocketClientEvent`.
|
||||||
|
/// - parameter data: The data for this event.
|
||||||
|
func handleClientEvent(_ event: SocketClientEvent, data: [Any])
|
||||||
|
|
||||||
/// Called when we get an event from socket.io.
|
/// Called when we get an event from socket.io.
|
||||||
///
|
///
|
||||||
/// - parameter event: The name of the event.
|
/// - parameter event: The name of the event.
|
||||||
@ -134,21 +137,17 @@ public protocol SocketIOClientSpec : class {
|
|||||||
/// - parameter withAck: If > 0 then this event expects to get an ack back from the client.
|
/// - parameter withAck: If > 0 then this event expects to get an ack back from the client.
|
||||||
func handleEvent(_ event: String, data: [Any], isInternalMessage: Bool, withAck ack: Int)
|
func handleEvent(_ event: String, data: [Any], isInternalMessage: Bool, withAck ack: Int)
|
||||||
|
|
||||||
/// Called on socket.io specific events.
|
/// Causes a client to handle a socket.io packet. The namespace for the packet must match the namespace of the
|
||||||
|
/// socket.
|
||||||
///
|
///
|
||||||
/// - parameter event: The `SocketClientEvent`.
|
/// - parameter packet: The packet to handle.
|
||||||
/// - parameter data: The data for this event.
|
func handlePacket(_ packet: SocketPacket)
|
||||||
func handleClientEvent(_ event: SocketClientEvent, data: [Any])
|
|
||||||
|
|
||||||
/// Call when you wish to leave a namespace and return to the default namespace.
|
/// Call when you wish to leave a namespace and disconnect this socket.
|
||||||
func leaveNamespace()
|
func leaveNamespace()
|
||||||
|
|
||||||
/// Joins `namespace`.
|
/// Joins `nsp`.
|
||||||
///
|
func joinNamespace()
|
||||||
/// **Do not use this to join the default namespace.** Instead call `leaveNamespace`.
|
|
||||||
///
|
|
||||||
/// - parameter namespace: The namespace to join.
|
|
||||||
func joinNamespace(_ namespace: String)
|
|
||||||
|
|
||||||
/// Removes handler(s) for a client event.
|
/// Removes handler(s) for a client event.
|
||||||
///
|
///
|
||||||
@ -212,13 +211,16 @@ public protocol SocketIOClientSpec : class {
|
|||||||
/// - parameter handler: The callback that will execute whenever an event is received.
|
/// - parameter handler: The callback that will execute whenever an event is received.
|
||||||
func onAny(_ handler: @escaping (SocketAnyEvent) -> ())
|
func onAny(_ handler: @escaping (SocketAnyEvent) -> ())
|
||||||
|
|
||||||
/// Tries to reconnect to the server.
|
|
||||||
func reconnect()
|
|
||||||
|
|
||||||
/// Removes all handlers.
|
/// Removes all handlers.
|
||||||
///
|
///
|
||||||
/// Can be used after disconnecting to break any potential remaining retain cycles.
|
/// Can be used after disconnecting to break any potential remaining retain cycles.
|
||||||
func removeAllHandlers()
|
func removeAllHandlers()
|
||||||
|
|
||||||
|
/// Puts the socket back into the connecting state.
|
||||||
|
/// Called when the manager detects a broken connection, or when a manual reconnect is triggered.
|
||||||
|
///
|
||||||
|
/// parameter reason: The reason this socket is going reconnecting.
|
||||||
|
func setReconnecting(reason: String)
|
||||||
}
|
}
|
||||||
|
|
||||||
public extension SocketIOClientSpec {
|
public extension SocketIOClientSpec {
|
||||||
@ -245,18 +247,80 @@ public enum SocketClientEvent : String {
|
|||||||
/// ```
|
/// ```
|
||||||
case connect
|
case connect
|
||||||
|
|
||||||
/// Called when the socket has disconnected and will not attempt to try to reconnect.
|
/// Emitted when the socket has disconnected and will not attempt to try to reconnect.
|
||||||
|
///
|
||||||
|
/// Usage:
|
||||||
|
///
|
||||||
|
/// ```swift
|
||||||
|
/// socket.on(clientEvent: .disconnect) {data, ack in
|
||||||
|
/// // Some cleanup logic
|
||||||
|
/// }
|
||||||
|
/// ```
|
||||||
case disconnect
|
case disconnect
|
||||||
|
|
||||||
/// Called when an error occurs.
|
/// Emitted when an error occurs.
|
||||||
|
///
|
||||||
|
/// Usage:
|
||||||
|
///
|
||||||
|
/// ```swift
|
||||||
|
/// socket.on(clientEvent: .error) {data, ack in
|
||||||
|
/// // Some logging
|
||||||
|
/// }
|
||||||
|
/// ```
|
||||||
case error
|
case error
|
||||||
|
|
||||||
/// Called when the client begins the reconnection process.
|
/// Emitted whenever the engine sends a ping.
|
||||||
|
///
|
||||||
|
/// Usage:
|
||||||
|
///
|
||||||
|
/// ```swift
|
||||||
|
/// socket.on(clientEvent: .ping) {_, _ in
|
||||||
|
/// // Maybe keep track of latency?
|
||||||
|
/// }
|
||||||
|
/// ```
|
||||||
|
case ping
|
||||||
|
|
||||||
|
/// Emitted whenever the engine gets a pong.
|
||||||
|
///
|
||||||
|
/// Usage:
|
||||||
|
///
|
||||||
|
/// ```swift
|
||||||
|
/// socket.on(clientEvent: .pong) {_, _ in
|
||||||
|
/// // Maybe keep track of latency?
|
||||||
|
/// }
|
||||||
|
/// ```
|
||||||
|
case pong
|
||||||
|
|
||||||
|
/// Emitted when the client begins the reconnection process.
|
||||||
|
///
|
||||||
|
/// Usage:
|
||||||
|
///
|
||||||
|
/// ```swift
|
||||||
|
/// socket.on(clientEvent: .reconnect) {data, ack in
|
||||||
|
/// // Some reconnect event logic
|
||||||
|
/// }
|
||||||
|
/// ```
|
||||||
case reconnect
|
case reconnect
|
||||||
|
|
||||||
/// Called each time the client tries to reconnect to the server.
|
/// Emitted each time the client tries to reconnect to the server.
|
||||||
|
///
|
||||||
|
/// Usage:
|
||||||
|
///
|
||||||
|
/// ```swift
|
||||||
|
/// socket.on(clientEvent: .reconnectAttempt) {data, ack in
|
||||||
|
/// // Some reconnect attempt logging
|
||||||
|
/// }
|
||||||
|
/// ```
|
||||||
case reconnectAttempt
|
case reconnectAttempt
|
||||||
|
|
||||||
/// Called every time there is a change in the client's status.
|
/// Emitted every time there is a change in the client's status.
|
||||||
|
///
|
||||||
|
/// Usage:
|
||||||
|
///
|
||||||
|
/// ```swift
|
||||||
|
/// socket.on(clientEvent: .statusChange) {data, ack in
|
||||||
|
/// // Some status changing logging
|
||||||
|
/// }
|
||||||
|
/// ```
|
||||||
case statusChange
|
case statusChange
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
//
|
//
|
||||||
// SocketIOClientStatus.swift
|
// SocketIOStatus.swift
|
||||||
// Socket.IO-Client-Swift
|
// Socket.IO-Client-Swift
|
||||||
//
|
//
|
||||||
// Created by Erik Little on 8/14/15.
|
// Created by Erik Little on 8/14/15.
|
||||||
@ -24,17 +24,36 @@
|
|||||||
|
|
||||||
import Foundation
|
import Foundation
|
||||||
|
|
||||||
/// Represents the state of the client.
|
/// Represents state of a manager or client.
|
||||||
@objc public enum SocketIOClientStatus : Int {
|
@objc
|
||||||
/// The client has never been connected. Or the client has been reset.
|
public enum SocketIOStatus : Int, CustomStringConvertible {
|
||||||
|
// MARK: Cases
|
||||||
|
|
||||||
|
/// The client/manager has never been connected. Or the client has been reset.
|
||||||
case notConnected
|
case notConnected
|
||||||
|
|
||||||
/// The client was once connected, but not anymore.
|
/// The client/manager was once connected, but not anymore.
|
||||||
case disconnected
|
case disconnected
|
||||||
|
|
||||||
/// The client is in the process of connecting.
|
/// The client/manager is in the process of connecting.
|
||||||
case connecting
|
case connecting
|
||||||
|
|
||||||
/// The client is currently connected.
|
/// The client/manager is currently connected.
|
||||||
case connected
|
case connected
|
||||||
|
|
||||||
|
// MARK: Properties
|
||||||
|
|
||||||
|
/// - returns: True if this client/manager is connected/connecting to a server.
|
||||||
|
public var active: Bool {
|
||||||
|
return self == .connected || self == .connecting
|
||||||
|
}
|
||||||
|
|
||||||
|
public var description: String {
|
||||||
|
switch self {
|
||||||
|
case .connected: return "connected"
|
||||||
|
case .connecting: return "connecting"
|
||||||
|
case .disconnected: return "disconnected"
|
||||||
|
case .notConnected: return "notConnected"
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@ -28,7 +28,8 @@ import Starscream
|
|||||||
|
|
||||||
/// The class that handles the engine.io protocol and transports.
|
/// The class that handles the engine.io protocol and transports.
|
||||||
/// See `SocketEnginePollable` and `SocketEngineWebsocket` for transport specific methods.
|
/// See `SocketEnginePollable` and `SocketEngineWebsocket` for transport specific methods.
|
||||||
public final class SocketEngine : NSObject, URLSessionDelegate, SocketEnginePollable, SocketEngineWebsocket {
|
public final class SocketEngine : NSObject, URLSessionDelegate, SocketEnginePollable, SocketEngineWebsocket,
|
||||||
|
ConfigSettable {
|
||||||
// MARK: Properties
|
// MARK: Properties
|
||||||
|
|
||||||
private static let logType = "SocketEngine"
|
private static let logType = "SocketEngine"
|
||||||
@ -147,41 +148,11 @@ public final class SocketEngine : NSObject, URLSessionDelegate, SocketEnginePoll
|
|||||||
public init(client: SocketEngineClient, url: URL, config: SocketIOClientConfiguration) {
|
public init(client: SocketEngineClient, url: URL, config: SocketIOClientConfiguration) {
|
||||||
self.client = client
|
self.client = client
|
||||||
self.url = url
|
self.url = url
|
||||||
for option in config {
|
|
||||||
switch option {
|
|
||||||
case let .connectParams(params):
|
|
||||||
connectParams = params
|
|
||||||
case let .cookies(cookies):
|
|
||||||
self.cookies = cookies
|
|
||||||
case let .extraHeaders(headers):
|
|
||||||
extraHeaders = headers
|
|
||||||
case let .sessionDelegate(delegate):
|
|
||||||
sessionDelegate = delegate
|
|
||||||
case let .forcePolling(force):
|
|
||||||
forcePolling = force
|
|
||||||
case let .forceWebsockets(force):
|
|
||||||
forceWebsockets = force
|
|
||||||
case let .path(path):
|
|
||||||
socketPath = path
|
|
||||||
|
|
||||||
if !socketPath.hasSuffix("/") {
|
|
||||||
socketPath += "/"
|
|
||||||
}
|
|
||||||
case let .secure(secure):
|
|
||||||
self.secure = secure
|
|
||||||
case let .selfSigned(selfSigned):
|
|
||||||
self.selfSigned = selfSigned
|
|
||||||
case let .security(security):
|
|
||||||
self.security = security
|
|
||||||
case .compress:
|
|
||||||
self.compress = true
|
|
||||||
default:
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
super.init()
|
super.init()
|
||||||
|
|
||||||
|
setConfigs(config)
|
||||||
|
|
||||||
sessionDelegate = sessionDelegate ?? self
|
sessionDelegate = sessionDelegate ?? self
|
||||||
|
|
||||||
(urlPolling, urlWebSocket) = createURLs()
|
(urlPolling, urlWebSocket) = createURLs()
|
||||||
@ -192,7 +163,7 @@ public final class SocketEngine : NSObject, URLSessionDelegate, SocketEnginePoll
|
|||||||
/// - parameter client: The client for this engine.
|
/// - parameter client: The client for this engine.
|
||||||
/// - parameter url: The url for this engine.
|
/// - parameter url: The url for this engine.
|
||||||
/// - parameter options: The options for this engine.
|
/// - parameter options: The options for this engine.
|
||||||
public convenience init(client: SocketEngineClient, url: URL, options: NSDictionary?) {
|
public convenience init(client: SocketEngineClient, url: URL, options: [String: Any]?) {
|
||||||
self.init(client: client, url: url, config: options?.toSocketConfiguration() ?? [])
|
self.init(client: client, url: url, config: options?.toSocketConfiguration() ?? [])
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -310,22 +281,22 @@ public final class SocketEngine : NSObject, URLSessionDelegate, SocketEnginePoll
|
|||||||
|
|
||||||
private func createWebSocketAndConnect() {
|
private func createWebSocketAndConnect() {
|
||||||
ws?.delegate = nil // TODO this seems a bit defensive, is this really needed?
|
ws?.delegate = nil // TODO this seems a bit defensive, is this really needed?
|
||||||
ws = WebSocket(url: urlWebSocketWithSid)
|
var req = URLRequest(url: urlWebSocketWithSid)
|
||||||
|
|
||||||
if cookies != nil {
|
if cookies != nil {
|
||||||
let headers = HTTPCookie.requestHeaderFields(with: cookies!)
|
let headers = HTTPCookie.requestHeaderFields(with: cookies!)
|
||||||
for (key, value) in headers {
|
for (headerName, value) in headers {
|
||||||
ws?.headers[key] = value
|
req.setValue(value, forHTTPHeaderField: headerName)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if extraHeaders != nil {
|
if extraHeaders != nil {
|
||||||
for (headerName, value) in extraHeaders! {
|
for (headerName, value) in extraHeaders! {
|
||||||
ws?.headers[headerName] = value
|
req.setValue(value, forHTTPHeaderField: headerName)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ws = WebSocket(request: req)
|
||||||
ws?.callbackQueue = engineQueue
|
ws?.callbackQueue = engineQueue
|
||||||
ws?.enableCompression = compress
|
ws?.enableCompression = compress
|
||||||
ws?.delegate = self
|
ws?.delegate = self
|
||||||
@ -485,6 +456,8 @@ public final class SocketEngine : NSObject, URLSessionDelegate, SocketEnginePoll
|
|||||||
if message == "3probe" {
|
if message == "3probe" {
|
||||||
upgradeTransport()
|
upgradeTransport()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
client?.engineDidReceivePong()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Parses raw binary received from engine.io.
|
/// Parses raw binary received from engine.io.
|
||||||
@ -569,6 +542,46 @@ public final class SocketEngine : NSObject, URLSessionDelegate, SocketEnginePoll
|
|||||||
|
|
||||||
this.sendPing()
|
this.sendPing()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
client?.engineDidSendPing()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Called when the engine should set/update its configs from a given configuration.
|
||||||
|
///
|
||||||
|
/// parameter config: The `SocketIOClientConfiguration` that should be used to set/update configs.
|
||||||
|
open func setConfigs(_ config: SocketIOClientConfiguration) {
|
||||||
|
for option in config {
|
||||||
|
switch option {
|
||||||
|
case let .connectParams(params):
|
||||||
|
connectParams = params
|
||||||
|
case let .cookies(cookies):
|
||||||
|
self.cookies = cookies
|
||||||
|
case let .extraHeaders(headers):
|
||||||
|
extraHeaders = headers
|
||||||
|
case let .sessionDelegate(delegate):
|
||||||
|
sessionDelegate = delegate
|
||||||
|
case let .forcePolling(force):
|
||||||
|
forcePolling = force
|
||||||
|
case let .forceWebsockets(force):
|
||||||
|
forceWebsockets = force
|
||||||
|
case let .path(path):
|
||||||
|
socketPath = path
|
||||||
|
|
||||||
|
if !socketPath.hasSuffix("/") {
|
||||||
|
socketPath += "/"
|
||||||
|
}
|
||||||
|
case let .secure(secure):
|
||||||
|
self.secure = secure
|
||||||
|
case let .selfSigned(selfSigned):
|
||||||
|
self.selfSigned = selfSigned
|
||||||
|
case let .security(security):
|
||||||
|
self.security = security
|
||||||
|
case .compress:
|
||||||
|
self.compress = true
|
||||||
|
default:
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Moves from long-polling to websockets
|
// Moves from long-polling to websockets
|
||||||
@ -608,7 +621,7 @@ public final class SocketEngine : NSObject, URLSessionDelegate, SocketEnginePoll
|
|||||||
// MARK: Starscream delegate conformance
|
// MARK: Starscream delegate conformance
|
||||||
|
|
||||||
/// Delegate method for connection.
|
/// Delegate method for connection.
|
||||||
public func websocketDidConnect(socket: WebSocket) {
|
public func websocketDidConnect(socket: WebSocketClient) {
|
||||||
if !forceWebsockets {
|
if !forceWebsockets {
|
||||||
probing = true
|
probing = true
|
||||||
probeWebSocket()
|
probeWebSocket()
|
||||||
@ -620,7 +633,7 @@ public final class SocketEngine : NSObject, URLSessionDelegate, SocketEnginePoll
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Delegate method for disconnection.
|
/// Delegate method for disconnection.
|
||||||
public func websocketDidDisconnect(socket: WebSocket, error: NSError?) {
|
public func websocketDidDisconnect(socket: WebSocketClient, error: Error?) {
|
||||||
probing = false
|
probing = false
|
||||||
|
|
||||||
if closed {
|
if closed {
|
||||||
@ -644,6 +657,12 @@ public final class SocketEngine : NSObject, URLSessionDelegate, SocketEnginePoll
|
|||||||
client?.engineDidClose(reason: "Socket Disconnected")
|
client?.engineDidClose(reason: "Socket Disconnected")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Test Properties
|
||||||
|
|
||||||
|
func setConnected(_ value: Bool) {
|
||||||
|
connected = value
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
extension SocketEngine {
|
extension SocketEngine {
|
||||||
|
|||||||
@ -44,6 +44,12 @@ import Foundation
|
|||||||
/// - parameter reason: The reason the engine opened.
|
/// - parameter reason: The reason the engine opened.
|
||||||
func engineDidOpen(reason: String)
|
func engineDidOpen(reason: String)
|
||||||
|
|
||||||
|
/// Called when the engine receives a pong message.
|
||||||
|
func engineDidReceivePong()
|
||||||
|
|
||||||
|
/// Called when the engine sends a ping to the server.
|
||||||
|
func engineDidSendPing()
|
||||||
|
|
||||||
/// Called when the engine has a message that must be parsed.
|
/// Called when the engine has a message that must be parsed.
|
||||||
///
|
///
|
||||||
/// - parameter msg: The message that needs parsing.
|
/// - parameter msg: The message that needs parsing.
|
||||||
|
|||||||
@ -34,6 +34,9 @@ import Starscream
|
|||||||
/// `true` if this engine is closed.
|
/// `true` if this engine is closed.
|
||||||
var closed: Bool { get }
|
var closed: Bool { get }
|
||||||
|
|
||||||
|
/// If `true` the engine will attempt to use WebSocket compression.
|
||||||
|
var compress: Bool { get }
|
||||||
|
|
||||||
/// `true` if this engine is connected. Connected means that the initial poll connect has succeeded.
|
/// `true` if this engine is connected. Connected means that the initial poll connect has succeeded.
|
||||||
var connected: Bool { get }
|
var connected: Bool { get }
|
||||||
|
|
||||||
@ -87,7 +90,7 @@ import Starscream
|
|||||||
/// - parameter client: The client for this engine.
|
/// - parameter client: The client for this engine.
|
||||||
/// - parameter url: The url for this engine.
|
/// - parameter url: The url for this engine.
|
||||||
/// - parameter options: The options for this engine.
|
/// - parameter options: The options for this engine.
|
||||||
init(client: SocketEngineClient, url: URL, options: NSDictionary?)
|
init(client: SocketEngineClient, url: URL, options: [String: Any]?)
|
||||||
|
|
||||||
/// Starts the connection to the server.
|
/// Starts the connection to the server.
|
||||||
func connect()
|
func connect()
|
||||||
|
|||||||
@ -68,12 +68,12 @@ extension SocketEngineWebsocket {
|
|||||||
// MARK: Starscream delegate methods
|
// MARK: Starscream delegate methods
|
||||||
|
|
||||||
/// Delegate method for when a message is received.
|
/// Delegate method for when a message is received.
|
||||||
public func websocketDidReceiveMessage(socket: WebSocket, text: String) {
|
public func websocketDidReceiveMessage(socket: WebSocketClient, text: String) {
|
||||||
parseEngineMessage(text)
|
parseEngineMessage(text)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Delegate method for when binary is received.
|
/// Delegate method for when binary is received.
|
||||||
public func websocketDidReceiveData(socket: WebSocket, data: Data) {
|
public func websocketDidReceiveData(socket: WebSocketClient, data: Data) {
|
||||||
parseEngineData(data)
|
parseEngineData(data)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
519
Source/SocketIO/Manager/SocketManager.swift
Normal file
519
Source/SocketIO/Manager/SocketManager.swift
Normal file
@ -0,0 +1,519 @@
|
|||||||
|
//
|
||||||
|
// Created by Erik Little on 10/14/17.
|
||||||
|
//
|
||||||
|
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
// of this software and associated documentation files (the "Software"), to deal
|
||||||
|
// in the Software without restriction, including without limitation the rights
|
||||||
|
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
// copies of the Software, and to permit persons to whom the Software is
|
||||||
|
// furnished to do so, subject to the following conditions:
|
||||||
|
//
|
||||||
|
// The above copyright notice and this permission notice shall be included in
|
||||||
|
// all copies or substantial portions of the Software.
|
||||||
|
//
|
||||||
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
// THE SOFTWARE.
|
||||||
|
|
||||||
|
import Dispatch
|
||||||
|
import Foundation
|
||||||
|
|
||||||
|
///
|
||||||
|
/// A manager for a socket.io connection.
|
||||||
|
///
|
||||||
|
/// A `SocketManager` is responsible for multiplexing multiple namespaces through a single `SocketEngineSpec`.
|
||||||
|
///
|
||||||
|
/// Example:
|
||||||
|
///
|
||||||
|
/// ```swift
|
||||||
|
/// let manager = SocketManager(socketURL: URL(string:"http://localhost:8080/")!)
|
||||||
|
/// let defaultNamespaceSocket = manager.defaultSocket
|
||||||
|
/// let swiftSocket = manager.socket(forNamespace: "/swift")
|
||||||
|
///
|
||||||
|
/// // defaultNamespaceSocket and swiftSocket both share a single connection to the server
|
||||||
|
/// ```
|
||||||
|
///
|
||||||
|
/// Sockets created through the manager are retained by the manager. So at the very least, a single strong reference
|
||||||
|
/// to the manager must be maintained to keep sockets alive.
|
||||||
|
///
|
||||||
|
/// To disconnect a socket and remove it from the manager, either call `SocketIOClient.disconnect()` on the socket,
|
||||||
|
/// or call one of the `disconnectSocket` methods on this class.
|
||||||
|
///
|
||||||
|
open class SocketManager : NSObject, SocketManagerSpec, SocketParsable, SocketDataBufferable, ConfigSettable {
|
||||||
|
private static let logType = "SocketManager"
|
||||||
|
|
||||||
|
// MARK Properties
|
||||||
|
|
||||||
|
/// The socket associated with the default namespace ("/").
|
||||||
|
public var defaultSocket: SocketIOClient {
|
||||||
|
return socket(forNamespace: "/")
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The URL of the socket.io server.
|
||||||
|
///
|
||||||
|
/// If changed after calling `init`, `forceNew` must be set to `true`, or it will only connect to the url set in the
|
||||||
|
/// init.
|
||||||
|
public let socketURL: URL
|
||||||
|
|
||||||
|
/// The configuration for this client.
|
||||||
|
///
|
||||||
|
/// **Some configs will not take affect until after a reconnect if set after calling a connect method**.
|
||||||
|
public var config: SocketIOClientConfiguration {
|
||||||
|
get {
|
||||||
|
return _config
|
||||||
|
}
|
||||||
|
|
||||||
|
set {
|
||||||
|
if status.active {
|
||||||
|
DefaultSocketLogger.Logger.log("Setting configs on active manager. Some configs may not be applied until reconnect",
|
||||||
|
type: SocketManager.logType)
|
||||||
|
}
|
||||||
|
|
||||||
|
setConfigs(newValue)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The engine for this manager.
|
||||||
|
public var engine: SocketEngineSpec?
|
||||||
|
|
||||||
|
/// If `true` then every time `connect` is called, a new engine will be created.
|
||||||
|
public var forceNew = false
|
||||||
|
|
||||||
|
/// The queue that all interaction with the client should occur on. This is the queue that event handlers are
|
||||||
|
/// called on.
|
||||||
|
///
|
||||||
|
/// **This should be a serial queue! Concurrent queues are not supported and might cause crashes and races**.
|
||||||
|
public var handleQueue = DispatchQueue.main
|
||||||
|
|
||||||
|
/// The sockets in this manager indexed by namespace.
|
||||||
|
public var nsps = [String: SocketIOClient]()
|
||||||
|
|
||||||
|
/// 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.
|
||||||
|
public var reconnectWait = 10
|
||||||
|
|
||||||
|
/// The status of this manager.
|
||||||
|
public private(set) var status: SocketIOStatus = .notConnected {
|
||||||
|
didSet {
|
||||||
|
switch status {
|
||||||
|
case .connected:
|
||||||
|
reconnecting = false
|
||||||
|
currentReconnectAttempt = 0
|
||||||
|
default:
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A list of packets that are waiting for binary data.
|
||||||
|
///
|
||||||
|
/// The way that socket.io works all data should be sent directly after each packet.
|
||||||
|
/// So this should ideally be an array of one packet waiting for data.
|
||||||
|
///
|
||||||
|
/// **This should not be modified directly.**
|
||||||
|
public var waitingPackets = [SocketPacket]()
|
||||||
|
|
||||||
|
private(set) var reconnectAttempts = -1
|
||||||
|
|
||||||
|
private var _config: SocketIOClientConfiguration
|
||||||
|
private var currentReconnectAttempt = 0
|
||||||
|
private var reconnecting = false
|
||||||
|
|
||||||
|
/// Type safe way to create a new SocketIOClient. `opts` can be omitted.
|
||||||
|
///
|
||||||
|
/// - parameter socketURL: The url of the socket.io server.
|
||||||
|
/// - parameter config: The config for this socket.
|
||||||
|
public init(socketURL: URL, config: SocketIOClientConfiguration = []) {
|
||||||
|
self._config = config
|
||||||
|
self.socketURL = socketURL
|
||||||
|
|
||||||
|
if socketURL.absoluteString.hasPrefix("https://") {
|
||||||
|
self._config.insert(.secure(true))
|
||||||
|
}
|
||||||
|
|
||||||
|
self._config.insert(.path("/socket.io/"), replacing: false)
|
||||||
|
|
||||||
|
super.init()
|
||||||
|
|
||||||
|
setConfigs(_config)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Not so type safe way to create a SocketIOClient, meant for Objective-C compatiblity.
|
||||||
|
/// If using Swift it's recommended to use `init(socketURL: NSURL, options: Set<SocketIOClientOption>)`
|
||||||
|
///
|
||||||
|
/// - 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() ?? [])
|
||||||
|
}
|
||||||
|
|
||||||
|
deinit {
|
||||||
|
DefaultSocketLogger.Logger.log("Manager is being released", type: SocketManager.logType)
|
||||||
|
|
||||||
|
engine?.disconnect(reason: "Manager Deinit")
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: Methods
|
||||||
|
|
||||||
|
private func addEngine() {
|
||||||
|
DefaultSocketLogger.Logger.log("Adding engine", type: SocketManager.logType)
|
||||||
|
|
||||||
|
engine?.engineQueue.sync {
|
||||||
|
self.engine?.client = nil
|
||||||
|
}
|
||||||
|
|
||||||
|
engine = SocketEngine(client: self, url: socketURL, config: config)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Connects the underlying transport and the default namespace socket.
|
||||||
|
///
|
||||||
|
/// Override if you wish to attach a custom `SocketEngineSpec`.
|
||||||
|
open func connect() {
|
||||||
|
guard !status.active else {
|
||||||
|
DefaultSocketLogger.Logger.log("Tried connecting an already active socket", type: SocketManager.logType)
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if engine == nil || forceNew {
|
||||||
|
addEngine()
|
||||||
|
}
|
||||||
|
|
||||||
|
status = .connecting
|
||||||
|
|
||||||
|
engine?.connect()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Connects a socket through this manager's engine.
|
||||||
|
///
|
||||||
|
/// - parameter socket: The socket who we should connect through this manager.
|
||||||
|
open func connectSocket(_ socket: SocketIOClient) {
|
||||||
|
guard status == .connected else {
|
||||||
|
DefaultSocketLogger.Logger.log("Tried connecting socket when engine isn't open. Connecting",
|
||||||
|
type: SocketManager.logType)
|
||||||
|
|
||||||
|
connect()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
engine?.send("0\(socket.nsp)", withData: [])
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Called when the manager has disconnected from socket.io.
|
||||||
|
///
|
||||||
|
/// - parameter reason: The reason for the disconnection.
|
||||||
|
open func didDisconnect(reason: String) {
|
||||||
|
forAll {socket in
|
||||||
|
socket.didDisconnect(reason: reason)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Disconnects the manager and all associated sockets.
|
||||||
|
open func disconnect() {
|
||||||
|
DefaultSocketLogger.Logger.log("Manager closing", type: SocketManager.logType)
|
||||||
|
|
||||||
|
status = .disconnected
|
||||||
|
|
||||||
|
engine?.disconnect(reason: "Disconnect")
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Disconnects the given socket.
|
||||||
|
///
|
||||||
|
/// This will remove the socket for the manager's control, and make the socket instance useless and ready for
|
||||||
|
/// releasing.
|
||||||
|
///
|
||||||
|
/// - parameter socket: The socket to disconnect.
|
||||||
|
open func disconnectSocket(_ socket: SocketIOClient) {
|
||||||
|
// Make sure we remove socket from nsps
|
||||||
|
nsps.removeValue(forKey: socket.nsp)
|
||||||
|
|
||||||
|
engine?.send("1\(socket.nsp)", withData: [])
|
||||||
|
socket.didDisconnect(reason: "Namespace leave")
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Disconnects the socket associated with `forNamespace`.
|
||||||
|
///
|
||||||
|
/// This will remove the socket for the manager's control, and make the socket instance useless and ready for
|
||||||
|
/// releasing.
|
||||||
|
///
|
||||||
|
/// - parameter forNamespace: The namespace to disconnect from.
|
||||||
|
open func disconnectSocket(forNamespace nsp: String) {
|
||||||
|
guard let socket = nsps.removeValue(forKey: nsp) else {
|
||||||
|
DefaultSocketLogger.Logger.log("Could not find socket for \(nsp) to disconnect",
|
||||||
|
type: SocketManager.logType)
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
disconnectSocket(socket)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Sends a client event to all sockets in `nsps`
|
||||||
|
///
|
||||||
|
/// - parameter clientEvent: The event to emit.
|
||||||
|
open func emitAll(clientEvent event: SocketClientEvent, data: [Any]) {
|
||||||
|
forAll {socket in
|
||||||
|
socket.handleClientEvent(event, data: data)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Sends an event to the server on all namespaces in this manager.
|
||||||
|
///
|
||||||
|
/// - parameter event: The event to send.
|
||||||
|
/// - parameter items: The data to send with this event.
|
||||||
|
open func emitAll(_ event: String, _ items: SocketData...) {
|
||||||
|
guard let emitData = try? items.map({ try $0.socketRepresentation() }) else {
|
||||||
|
DefaultSocketLogger.Logger.error("Error creating socketRepresentation for emit: \(event), \(items)",
|
||||||
|
type: SocketManager.logType)
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
emitAll(event, withItems: emitData)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Sends an event to the server on all namespaces in this manager.
|
||||||
|
///
|
||||||
|
/// Same as `emitAll(_:_:)`, but meant for Objective-C.
|
||||||
|
///
|
||||||
|
/// - parameter event: The event to send.
|
||||||
|
/// - parameter withItems: The data to send with this event.
|
||||||
|
open func emitAll(_ event: String, withItems items: [Any]) {
|
||||||
|
forAll {socket in
|
||||||
|
socket.emit(event, with: items)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Called when the engine closes.
|
||||||
|
///
|
||||||
|
/// - parameter reason: The reason that the engine closed.
|
||||||
|
open func engineDidClose(reason: String) {
|
||||||
|
handleQueue.async {
|
||||||
|
self._engineDidClose(reason: reason)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private func _engineDidClose(reason: String) {
|
||||||
|
waitingPackets.removeAll()
|
||||||
|
|
||||||
|
if status != .disconnected {
|
||||||
|
status = .notConnected
|
||||||
|
}
|
||||||
|
|
||||||
|
if status == .disconnected || !reconnects {
|
||||||
|
didDisconnect(reason: reason)
|
||||||
|
} else if !reconnecting {
|
||||||
|
reconnecting = true
|
||||||
|
tryReconnect(reason: reason)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Called when the engine errors.
|
||||||
|
///
|
||||||
|
/// - parameter reason: The reason the engine errored.
|
||||||
|
open func engineDidError(reason: String) {
|
||||||
|
handleQueue.async {
|
||||||
|
self._engineDidError(reason: reason)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private func _engineDidError(reason: String) {
|
||||||
|
DefaultSocketLogger.Logger.error("\(reason)", type: SocketManager.logType)
|
||||||
|
|
||||||
|
emitAll(clientEvent: .error, data: [reason])
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Called when the engine opens.
|
||||||
|
///
|
||||||
|
/// - parameter reason: The reason the engine opened.
|
||||||
|
open func engineDidOpen(reason: String) {
|
||||||
|
handleQueue.async {
|
||||||
|
self._engineDidOpen(reason: reason)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private func _engineDidOpen(reason: String) {
|
||||||
|
DefaultSocketLogger.Logger.log("Engine opened \(reason)", type: SocketManager.logType)
|
||||||
|
|
||||||
|
status = .connected
|
||||||
|
nsps["/"]?.didConnect(toNamespace: "/")
|
||||||
|
|
||||||
|
for (nsp, socket) in nsps where nsp != "/" && socket.status == .connecting {
|
||||||
|
connectSocket(socket)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Called when the engine receives a pong message.
|
||||||
|
open func engineDidReceivePong() {
|
||||||
|
handleQueue.async {
|
||||||
|
self._engineDidReceivePong()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private func _engineDidReceivePong() {
|
||||||
|
emitAll(clientEvent: .pong, data: [])
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Called when the sends a ping to the server.
|
||||||
|
open func engineDidSendPing() {
|
||||||
|
handleQueue.async {
|
||||||
|
self._engineDidSendPing()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private func _engineDidSendPing() {
|
||||||
|
emitAll(clientEvent: .ping, data: [])
|
||||||
|
}
|
||||||
|
|
||||||
|
private func forAll(do: (SocketIOClient) throws -> ()) rethrows {
|
||||||
|
for (_, socket) in nsps {
|
||||||
|
try `do`(socket)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Called when the engine has a message that must be parsed.
|
||||||
|
///
|
||||||
|
/// - parameter msg: The message that needs parsing.
|
||||||
|
open func parseEngineMessage(_ msg: String) {
|
||||||
|
handleQueue.async {
|
||||||
|
self._parseEngineMessage(msg)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private func _parseEngineMessage(_ msg: String) {
|
||||||
|
guard let packet = parseSocketMessage(msg) else { return }
|
||||||
|
guard packet.type != .binaryAck && packet.type != .binaryEvent else {
|
||||||
|
waitingPackets.append(packet)
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
nsps[packet.nsp]?.handlePacket(packet)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Called when the engine receives binary data.
|
||||||
|
///
|
||||||
|
/// - parameter data: The data the engine received.
|
||||||
|
open func parseEngineBinaryData(_ data: Data) {
|
||||||
|
handleQueue.async {
|
||||||
|
self._parseEngineBinaryData(data)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private func _parseEngineBinaryData(_ data: Data) {
|
||||||
|
guard let packet = parseBinaryData(data) else { return }
|
||||||
|
|
||||||
|
nsps[packet.nsp]?.handlePacket(packet)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Tries to reconnect to the server.
|
||||||
|
///
|
||||||
|
/// This will cause a `disconnect` event to be emitted, as well as an `reconnectAttempt` event.
|
||||||
|
open func reconnect() {
|
||||||
|
guard !reconnecting else { return }
|
||||||
|
|
||||||
|
engine?.disconnect(reason: "manual reconnect")
|
||||||
|
}
|
||||||
|
|
||||||
|
private func tryReconnect(reason: String) {
|
||||||
|
guard reconnecting else { return }
|
||||||
|
|
||||||
|
DefaultSocketLogger.Logger.log("Starting reconnect", type: SocketManager.logType)
|
||||||
|
|
||||||
|
// Set status to connecting and emit reconnect for all sockets
|
||||||
|
forAll {socket in
|
||||||
|
guard socket.status == .connected else { return }
|
||||||
|
|
||||||
|
socket.setReconnecting(reason: reason)
|
||||||
|
}
|
||||||
|
|
||||||
|
_tryReconnect()
|
||||||
|
}
|
||||||
|
|
||||||
|
private func _tryReconnect() {
|
||||||
|
guard reconnects && reconnecting && status != .disconnected else { return }
|
||||||
|
|
||||||
|
if reconnectAttempts != -1 && currentReconnectAttempt + 1 > reconnectAttempts {
|
||||||
|
return didDisconnect(reason: "Reconnect Failed")
|
||||||
|
}
|
||||||
|
|
||||||
|
DefaultSocketLogger.Logger.log("Trying to reconnect", type: SocketManager.logType)
|
||||||
|
emitAll(clientEvent: .reconnectAttempt, data: [(reconnectAttempts - currentReconnectAttempt)])
|
||||||
|
|
||||||
|
currentReconnectAttempt += 1
|
||||||
|
connect()
|
||||||
|
|
||||||
|
handleQueue.asyncAfter(deadline: DispatchTime.now() + Double(reconnectWait), execute: _tryReconnect)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Sets manager specific configs.
|
||||||
|
///
|
||||||
|
/// parameter config: The configs that should be set.
|
||||||
|
open func setConfigs(_ config: SocketIOClientConfiguration) {
|
||||||
|
for option in config {
|
||||||
|
switch option {
|
||||||
|
case let .forceNew(new):
|
||||||
|
self.forceNew = new
|
||||||
|
case let .reconnects(reconnects):
|
||||||
|
self.reconnects = reconnects
|
||||||
|
case let .reconnectWait(wait):
|
||||||
|
reconnectWait = abs(wait)
|
||||||
|
case let .log(log):
|
||||||
|
DefaultSocketLogger.Logger.log = log
|
||||||
|
case let .logger(logger):
|
||||||
|
DefaultSocketLogger.Logger = logger
|
||||||
|
default:
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_config = config
|
||||||
|
_config.insert(.path("/socket.io/"), replacing: false)
|
||||||
|
|
||||||
|
// If `ConfigSettable` & `SocketEngineSpec`, update its configs.
|
||||||
|
if var settableEngine = engine as? ConfigSettable & SocketEngineSpec {
|
||||||
|
settableEngine.engineQueue.sync {
|
||||||
|
settableEngine.setConfigs(self._config)
|
||||||
|
}
|
||||||
|
|
||||||
|
engine = settableEngine
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns a `SocketIOClient` for the given namespace. This socket shares a transport with the manager.
|
||||||
|
///
|
||||||
|
/// Calling multiple times returns the same socket.
|
||||||
|
///
|
||||||
|
/// Sockets created from this method are retained by the manager.
|
||||||
|
/// Call one of the `disconnectSocket` methods on this class to remove the socket from manager control.
|
||||||
|
/// Or call `SocketIOClient.disconnect()` on the client.
|
||||||
|
///
|
||||||
|
/// - parameter forNamespace: The namespace for the socket.
|
||||||
|
/// - returns: A `SocketIOClient` for the given namespace.
|
||||||
|
open func socket(forNamespace nsp: String) -> SocketIOClient {
|
||||||
|
assert(nsp.hasPrefix("/"), "forNamespace must have a leading /")
|
||||||
|
|
||||||
|
if let socket = nsps[nsp] {
|
||||||
|
return socket
|
||||||
|
}
|
||||||
|
|
||||||
|
let client = SocketIOClient(manager: self, nsp: nsp)
|
||||||
|
|
||||||
|
nsps[nsp] = client
|
||||||
|
|
||||||
|
return client
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test properties
|
||||||
|
|
||||||
|
func setTestStatus(_ status: SocketIOStatus) {
|
||||||
|
self.status = status
|
||||||
|
}
|
||||||
|
}
|
||||||
128
Source/SocketIO/Manager/SocketManagerSpec.swift
Normal file
128
Source/SocketIO/Manager/SocketManagerSpec.swift
Normal file
@ -0,0 +1,128 @@
|
|||||||
|
//
|
||||||
|
// Created by Erik Little on 10/18/17.
|
||||||
|
//
|
||||||
|
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
// of this software and associated documentation files (the "Software"), to deal
|
||||||
|
// in the Software without restriction, including without limitation the rights
|
||||||
|
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
// copies of the Software, and to permit persons to whom the Software is
|
||||||
|
// furnished to do so, subject to the following conditions:
|
||||||
|
//
|
||||||
|
// The above copyright notice and this permission notice shall be included in
|
||||||
|
// all copies or substantial portions of the Software.
|
||||||
|
//
|
||||||
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
// THE SOFTWARE.
|
||||||
|
|
||||||
|
import Dispatch
|
||||||
|
import Foundation
|
||||||
|
|
||||||
|
// TODO Fix the types so that we aren't using concrete types
|
||||||
|
|
||||||
|
///
|
||||||
|
/// A manager for a socket.io connection.
|
||||||
|
///
|
||||||
|
/// A `SocketManagerSpec` is responsible for multiplexing multiple namespaces through a single `SocketEngineSpec`.
|
||||||
|
///
|
||||||
|
/// Example with `SocketManager`:
|
||||||
|
///
|
||||||
|
/// ```swift
|
||||||
|
/// let manager = SocketManager(socketURL: URL(string:"http://localhost:8080/")!)
|
||||||
|
/// let defaultNamespaceSocket = manager.defaultSocket
|
||||||
|
/// let swiftSocket = manager.socket(forNamespace: "/swift")
|
||||||
|
///
|
||||||
|
/// // defaultNamespaceSocket and swiftSocket both share a single connection to the server
|
||||||
|
/// ```
|
||||||
|
///
|
||||||
|
/// Sockets created through the manager are retained by the manager. So at the very least, a single strong reference
|
||||||
|
/// to the manager must be maintained to keep sockets alive.
|
||||||
|
///
|
||||||
|
/// To disconnect a socket and remove it from the manager, either call `SocketIOClient.disconnect()` on the socket,
|
||||||
|
/// or call one of the `disconnectSocket` methods on this class.
|
||||||
|
///
|
||||||
|
@objc
|
||||||
|
public protocol SocketManagerSpec : class, SocketEngineClient {
|
||||||
|
// MARK: Properties
|
||||||
|
|
||||||
|
/// Returns the socket associated with the default namespace ("/").
|
||||||
|
var defaultSocket: SocketIOClient { get }
|
||||||
|
|
||||||
|
/// The engine for this manager.
|
||||||
|
var engine: SocketEngineSpec? { get set }
|
||||||
|
|
||||||
|
/// If `true` then every time `connect` is called, a new engine will be created.
|
||||||
|
var forceNew: Bool { get set }
|
||||||
|
|
||||||
|
// TODO Per socket queues?
|
||||||
|
/// The queue that all interaction with the client should occur on. This is the queue that event handlers are
|
||||||
|
/// called on.
|
||||||
|
var handleQueue: DispatchQueue { get set }
|
||||||
|
|
||||||
|
/// 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.
|
||||||
|
var reconnectWait: Int { get set }
|
||||||
|
|
||||||
|
/// The URL of the socket.io server.
|
||||||
|
var socketURL: URL { get }
|
||||||
|
|
||||||
|
/// The status of this manager.
|
||||||
|
var status: SocketIOStatus { get }
|
||||||
|
|
||||||
|
// MARK: Methods
|
||||||
|
|
||||||
|
/// Connects the underlying transport.
|
||||||
|
func connect()
|
||||||
|
|
||||||
|
/// Connects a socket through this manager's engine.
|
||||||
|
///
|
||||||
|
/// - parameter socket: The socket who we should connect through this manager.
|
||||||
|
func connectSocket(_ socket: SocketIOClient)
|
||||||
|
|
||||||
|
/// Called when the manager has disconnected from socket.io.
|
||||||
|
///
|
||||||
|
/// - parameter reason: The reason for the disconnection.
|
||||||
|
func didDisconnect(reason: String)
|
||||||
|
|
||||||
|
/// Disconnects the manager and all associated sockets.
|
||||||
|
func disconnect()
|
||||||
|
|
||||||
|
/// Disconnects the given socket.
|
||||||
|
///
|
||||||
|
/// - parameter socket: The socket to disconnect.
|
||||||
|
func disconnectSocket(_ socket: SocketIOClient)
|
||||||
|
|
||||||
|
/// Disconnects the socket associated with `forNamespace`.
|
||||||
|
///
|
||||||
|
/// - parameter forNamespace: The namespace to disconnect from.
|
||||||
|
func disconnectSocket(forNamespace nsp: String)
|
||||||
|
|
||||||
|
/// Sends an event to the server on all namespaces in this manager.
|
||||||
|
///
|
||||||
|
/// - parameter event: The event to send.
|
||||||
|
/// - parameter withItems: The data to send with this event.
|
||||||
|
func emitAll(_ event: String, withItems items: [Any])
|
||||||
|
|
||||||
|
/// Tries to reconnect to the server.
|
||||||
|
///
|
||||||
|
/// This will cause a `disconnect` event to be emitted, as well as an `reconnectAttempt` event.
|
||||||
|
func reconnect()
|
||||||
|
|
||||||
|
/// Returns a `SocketIOClient` for the given namespace. This socket shares a transport with the manager.
|
||||||
|
///
|
||||||
|
/// Calling multiple times returns the same socket.
|
||||||
|
///
|
||||||
|
/// Sockets created from this method are retained by the manager.
|
||||||
|
/// Call one of the `disconnectSocket` methods on the implementing class to remove the socket from manager control.
|
||||||
|
/// Or call `SocketIOClient.disconnect()` on the client.
|
||||||
|
///
|
||||||
|
/// - parameter forNamespace: The namespace for the socket.
|
||||||
|
/// - returns: A `SocketIOClient` for the given namespace.
|
||||||
|
func socket(forNamespace nsp: String) -> SocketIOClient
|
||||||
|
}
|
||||||
@ -142,12 +142,8 @@ public struct SocketPacket : CustomStringConvertible {
|
|||||||
if dict["_placeholder"] as? Bool ?? false {
|
if dict["_placeholder"] as? Bool ?? false {
|
||||||
return binary[dict["num"] as! Int]
|
return binary[dict["num"] as! Int]
|
||||||
} else {
|
} else {
|
||||||
return dict.reduce(JSON(), {cur, keyValue in
|
return dict.reduce(into: JSON(), {cur, keyValue in
|
||||||
var cur = cur
|
|
||||||
|
|
||||||
cur[keyValue.0] = _fillInPlaceholders(keyValue.1)
|
cur[keyValue.0] = _fillInPlaceholders(keyValue.1)
|
||||||
|
|
||||||
return cur
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
case let arr as [Any]:
|
case let arr as [Any]:
|
||||||
@ -225,12 +221,8 @@ private extension SocketPacket {
|
|||||||
case let arr as [Any]:
|
case let arr as [Any]:
|
||||||
return arr.map({shred($0, binary: &binary)})
|
return arr.map({shred($0, binary: &binary)})
|
||||||
case let dict as JSON:
|
case let dict as JSON:
|
||||||
return dict.reduce(JSON(), {cur, keyValue in
|
return dict.reduce(into: JSON(), {cur, keyValue in
|
||||||
var mutCur = cur
|
cur[keyValue.0] = shred(keyValue.1, binary: &binary)
|
||||||
|
|
||||||
mutCur[keyValue.0] = shred(keyValue.1, binary: &binary)
|
|
||||||
|
|
||||||
return mutCur
|
|
||||||
})
|
})
|
||||||
default:
|
default:
|
||||||
return data
|
return data
|
||||||
|
|||||||
@ -26,14 +26,6 @@ import Foundation
|
|||||||
public protocol SocketParsable : class {
|
public protocol SocketParsable : class {
|
||||||
// MARK: Properties
|
// MARK: Properties
|
||||||
|
|
||||||
/// A list of packets that are waiting for binary data.
|
|
||||||
///
|
|
||||||
/// The way that socket.io works all data should be sent directly after each packet.
|
|
||||||
/// So this should ideally be an array of one packet waiting for data.
|
|
||||||
///
|
|
||||||
/// **This should not be modified directly.**
|
|
||||||
var waitingPackets: [SocketPacket] { get set }
|
|
||||||
|
|
||||||
// MARK: Methods
|
// MARK: Methods
|
||||||
|
|
||||||
/// Called when the engine has received some binary data that should be attached to a packet.
|
/// Called when the engine has received some binary data that should be attached to a packet.
|
||||||
@ -43,12 +35,13 @@ public protocol SocketParsable : class {
|
|||||||
/// into the correct placeholder.
|
/// into the correct placeholder.
|
||||||
///
|
///
|
||||||
/// - parameter data: The data that should be attached to a packet.
|
/// - parameter data: The data that should be attached to a packet.
|
||||||
func parseBinaryData(_ data: Data)
|
func parseBinaryData(_ data: Data) -> SocketPacket?
|
||||||
|
|
||||||
/// Called when the engine has received a string that should be parsed into a socket.io packet.
|
/// Called when the engine has received a string that should be parsed into a socket.io packet.
|
||||||
///
|
///
|
||||||
/// - parameter message: The string that needs parsing.
|
/// - parameter message: The string that needs parsing.
|
||||||
func parseSocketMessage(_ message: String)
|
/// - returns: A completed socket packet if there is no more data left to collect.
|
||||||
|
func parseSocketMessage(_ message: String) -> SocketPacket?
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Errors that can be thrown during parsing.
|
/// Errors that can be thrown during parsing.
|
||||||
@ -65,38 +58,18 @@ public enum SocketParsableError : Error {
|
|||||||
case invalidPacketType
|
case invalidPacketType
|
||||||
}
|
}
|
||||||
|
|
||||||
public extension SocketParsable where Self: SocketIOClientSpec {
|
/// Says that a type will be able to buffer binary data before all data for an event has come in.
|
||||||
private func isCorrectNamespace(_ nsp: String) -> Bool {
|
public protocol SocketDataBufferable : class {
|
||||||
return nsp == self.nsp
|
/// A list of packets that are waiting for binary data.
|
||||||
}
|
///
|
||||||
|
/// The way that socket.io works all data should be sent directly after each packet.
|
||||||
private func handleConnect(_ packetNamespace: String) {
|
/// So this should ideally be an array of one packet waiting for data.
|
||||||
guard packetNamespace == nsp else { return }
|
///
|
||||||
|
/// **This should not be modified directly.**
|
||||||
didConnect(toNamespace: packetNamespace)
|
var waitingPackets: [SocketPacket] { get set }
|
||||||
}
|
}
|
||||||
|
|
||||||
private func handlePacket(_ pack: SocketPacket) {
|
|
||||||
switch pack.type {
|
|
||||||
case .event where isCorrectNamespace(pack.nsp):
|
|
||||||
handleEvent(pack.event, data: pack.args, isInternalMessage: false, withAck: pack.id)
|
|
||||||
case .ack where isCorrectNamespace(pack.nsp):
|
|
||||||
handleAck(pack.id, data: pack.data)
|
|
||||||
case .binaryEvent where isCorrectNamespace(pack.nsp):
|
|
||||||
waitingPackets.append(pack)
|
|
||||||
case .binaryAck where isCorrectNamespace(pack.nsp):
|
|
||||||
waitingPackets.append(pack)
|
|
||||||
case .connect:
|
|
||||||
handleConnect(pack.nsp)
|
|
||||||
case .disconnect:
|
|
||||||
didDisconnect(reason: "Got Disconnect")
|
|
||||||
case .error:
|
|
||||||
handleEvent("error", data: pack.data, isInternalMessage: true, withAck: pack.id)
|
|
||||||
default:
|
|
||||||
DefaultSocketLogger.Logger.log("Got invalid packet: \(pack.description)", type: "SocketParser")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
public extension SocketParsable where Self: SocketManagerSpec & SocketDataBufferable {
|
||||||
/// Parses a message from the engine, returning a complete SocketPacket or throwing.
|
/// Parses a message from the engine, returning a complete SocketPacket or throwing.
|
||||||
///
|
///
|
||||||
/// - parameter message: The message to parse.
|
/// - parameter message: The message to parse.
|
||||||
@ -169,8 +142,9 @@ public extension SocketParsable where Self: SocketIOClientSpec {
|
|||||||
/// Called when the engine has received a string that should be parsed into a socket.io packet.
|
/// Called when the engine has received a string that should be parsed into a socket.io packet.
|
||||||
///
|
///
|
||||||
/// - parameter message: The string that needs parsing.
|
/// - parameter message: The string that needs parsing.
|
||||||
public func parseSocketMessage(_ message: String) {
|
/// - returns: A completed socket packet or nil if the packet is invalid.
|
||||||
guard !message.isEmpty else { return }
|
public func parseSocketMessage(_ message: String) -> SocketPacket? {
|
||||||
|
guard !message.isEmpty else { return nil }
|
||||||
|
|
||||||
DefaultSocketLogger.Logger.log("Parsing \(message)", type: "SocketParser")
|
DefaultSocketLogger.Logger.log("Parsing \(message)", type: "SocketParser")
|
||||||
|
|
||||||
@ -179,9 +153,11 @@ public extension SocketParsable where Self: SocketIOClientSpec {
|
|||||||
|
|
||||||
DefaultSocketLogger.Logger.log("Decoded packet as: \(packet.description)", type: "SocketParser")
|
DefaultSocketLogger.Logger.log("Decoded packet as: \(packet.description)", type: "SocketParser")
|
||||||
|
|
||||||
handlePacket(packet)
|
return packet
|
||||||
} catch {
|
} catch {
|
||||||
DefaultSocketLogger.Logger.error("\(error): \(message)", type: "SocketParser")
|
DefaultSocketLogger.Logger.error("\(error): \(message)", type: "SocketParser")
|
||||||
|
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -192,21 +168,17 @@ public extension SocketParsable where Self: SocketIOClientSpec {
|
|||||||
/// into the correct placeholder.
|
/// into the correct placeholder.
|
||||||
///
|
///
|
||||||
/// - parameter data: The data that should be attached to a packet.
|
/// - parameter data: The data that should be attached to a packet.
|
||||||
public func parseBinaryData(_ data: Data) {
|
/// - returns: A completed socket packet if there is no more data left to collect.
|
||||||
|
public func parseBinaryData(_ data: Data) -> SocketPacket? {
|
||||||
guard !waitingPackets.isEmpty else {
|
guard !waitingPackets.isEmpty else {
|
||||||
DefaultSocketLogger.Logger.error("Got data when not remaking packet", type: "SocketParser")
|
DefaultSocketLogger.Logger.error("Got data when not remaking packet", type: "SocketParser")
|
||||||
return
|
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Should execute event?
|
// Should execute event?
|
||||||
guard waitingPackets[waitingPackets.count - 1].addData(data) else { return }
|
guard waitingPackets[waitingPackets.count - 1].addData(data) else { return nil }
|
||||||
|
|
||||||
let packet = waitingPackets.removeLast()
|
return waitingPackets.removeLast()
|
||||||
|
|
||||||
if packet.type != .binaryAck {
|
|
||||||
handleEvent(packet.event, data: packet.args, isInternalMessage: false, withAck: packet.id)
|
|
||||||
} else {
|
|
||||||
handleAck(packet.id, data: packet.args)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,110 +0,0 @@
|
|||||||
//
|
|
||||||
// SocketClientManager.swift
|
|
||||||
// Socket.IO-Client-Swift
|
|
||||||
//
|
|
||||||
// Created by Erik Little on 6/11/16.
|
|
||||||
//
|
|
||||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
// of this software and associated documentation files (the "Software"), to deal
|
|
||||||
// in the Software without restriction, including without limitation the rights
|
|
||||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
// copies of the Software, and to permit persons to whom the Software is
|
|
||||||
// furnished to do so, subject to the following conditions:
|
|
||||||
//
|
|
||||||
// The above copyright notice and this permission notice shall be included in
|
|
||||||
// all copies or substantial portions of the Software.
|
|
||||||
//
|
|
||||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
||||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
||||||
// THE SOFTWARE.
|
|
||||||
|
|
||||||
import Foundation
|
|
||||||
|
|
||||||
/**
|
|
||||||
Experimental socket manager.
|
|
||||||
|
|
||||||
API subject to change.
|
|
||||||
|
|
||||||
Can be used to persist sockets across ViewControllers.
|
|
||||||
|
|
||||||
Sockets are strongly stored, so be sure to remove them once they are no
|
|
||||||
longer needed.
|
|
||||||
|
|
||||||
Example usage:
|
|
||||||
```
|
|
||||||
let manager = SocketClientManager.sharedManager
|
|
||||||
manager["room1"] = socket1
|
|
||||||
manager["room2"] = socket2
|
|
||||||
manager.removeSocket(socket: socket2)
|
|
||||||
manager["room1"]?.emit("hello")
|
|
||||||
```
|
|
||||||
*/
|
|
||||||
open class SocketClientManager : NSObject {
|
|
||||||
// MARK: Properties.
|
|
||||||
|
|
||||||
/// The shared manager.
|
|
||||||
@objc
|
|
||||||
open static let sharedManager = SocketClientManager()
|
|
||||||
|
|
||||||
private var sockets = [String: SocketIOClient]()
|
|
||||||
|
|
||||||
/// Gets a socket by its name.
|
|
||||||
///
|
|
||||||
/// - returns: The socket, if one had the given name.
|
|
||||||
open subscript(string: String) -> SocketIOClient? {
|
|
||||||
get {
|
|
||||||
return sockets[string]
|
|
||||||
}
|
|
||||||
|
|
||||||
set(socket) {
|
|
||||||
sockets[string] = socket
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// MARK: Methods.
|
|
||||||
|
|
||||||
/// Adds a socket.
|
|
||||||
///
|
|
||||||
/// - parameter socket: The socket to add.
|
|
||||||
/// - parameter labeledAs: The label for this socket.
|
|
||||||
@objc
|
|
||||||
open func addSocket(_ socket: SocketIOClient, labeledAs label: String) {
|
|
||||||
sockets[label] = socket
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Removes a socket by a given name.
|
|
||||||
///
|
|
||||||
/// - parameter withLabel: The label of the socket to remove.
|
|
||||||
/// - returns: The socket for the given label, if one was present.
|
|
||||||
@objc
|
|
||||||
@discardableResult
|
|
||||||
open func removeSocket(withLabel label: String) -> SocketIOClient? {
|
|
||||||
return sockets.removeValue(forKey: label)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Removes a socket.
|
|
||||||
///
|
|
||||||
/// - parameter socket: The socket to remove.
|
|
||||||
/// - returns: The socket if it was in the manager.
|
|
||||||
@objc
|
|
||||||
@discardableResult
|
|
||||||
open func removeSocket(_ socket: SocketIOClient) -> SocketIOClient? {
|
|
||||||
var returnSocket: SocketIOClient?
|
|
||||||
|
|
||||||
for (label, dictSocket) in sockets where dictSocket === socket {
|
|
||||||
returnSocket = sockets.removeValue(forKey: label)
|
|
||||||
}
|
|
||||||
|
|
||||||
return returnSocket
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Removes all the sockets in the manager.
|
|
||||||
@objc
|
|
||||||
open func removeSockets() {
|
|
||||||
sockets.removeAll()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -42,7 +42,7 @@ extension CharacterSet {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
extension NSDictionary {
|
extension Dictionary where Key == String, Value == Any {
|
||||||
private static func keyValueToSocketIOClientOption(key: String, value: Any) -> SocketIOClientOption? {
|
private static func keyValueToSocketIOClientOption(key: String, value: Any) -> SocketIOClientOption? {
|
||||||
switch (key, value) {
|
switch (key, value) {
|
||||||
case let ("connectParams", params as [String: Any]):
|
case let ("connectParams", params as [String: Any]):
|
||||||
@ -63,8 +63,6 @@ extension NSDictionary {
|
|||||||
return .log(log)
|
return .log(log)
|
||||||
case let ("logger", logger as SocketLogger):
|
case let ("logger", logger as SocketLogger):
|
||||||
return .logger(logger)
|
return .logger(logger)
|
||||||
case let ("nsp", nsp as String):
|
|
||||||
return .nsp(nsp)
|
|
||||||
case let ("path", path as String):
|
case let ("path", path as String):
|
||||||
return .path(path)
|
return .path(path)
|
||||||
case let ("reconnects", reconnects as Bool):
|
case let ("reconnects", reconnects as Bool):
|
||||||
@ -92,7 +90,7 @@ extension NSDictionary {
|
|||||||
var options = [] as SocketIOClientConfiguration
|
var options = [] as SocketIOClientConfiguration
|
||||||
|
|
||||||
for (rawKey, value) in self {
|
for (rawKey, value) in self {
|
||||||
if let key = rawKey as? String, let opt = NSDictionary.keyValueToSocketIOClientOption(key: key, value: value) {
|
if let opt = Dictionary.keyValueToSocketIOClientOption(key: rawKey, value: value) {
|
||||||
options.insert(opt)
|
options.insert(opt)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -150,10 +150,9 @@ class SocketBasicPacketTest: XCTestCase {
|
|||||||
|
|
||||||
func testBinaryStringPlaceholderInMessage() {
|
func testBinaryStringPlaceholderInMessage() {
|
||||||
let engineString = "52-[\"test\",\"~~0\",{\"num\":0,\"_placeholder\":true},{\"_placeholder\":true,\"num\":1}]"
|
let engineString = "52-[\"test\",\"~~0\",{\"num\":0,\"_placeholder\":true},{\"_placeholder\":true,\"num\":1}]"
|
||||||
let socket = SocketIOClient(socketURL: URL(string: "http://localhost/")!)
|
let manager = SocketManager(socketURL: URL(string: "http://localhost/")!)
|
||||||
socket.setTestable()
|
|
||||||
|
|
||||||
var packet = try! socket.parseString(engineString)
|
var packet = try! manager.parseString(engineString)
|
||||||
|
|
||||||
XCTAssertEqual(packet.event, "test")
|
XCTAssertEqual(packet.event, "test")
|
||||||
_ = packet.addData(data)
|
_ = packet.addData(data)
|
||||||
|
|||||||
@ -10,20 +10,9 @@ import XCTest
|
|||||||
@testable import SocketIO
|
@testable import SocketIO
|
||||||
|
|
||||||
class SocketEngineTest: XCTestCase {
|
class SocketEngineTest: XCTestCase {
|
||||||
var client: SocketIOClient!
|
|
||||||
var engine: SocketEngine!
|
|
||||||
|
|
||||||
override func setUp() {
|
|
||||||
super.setUp()
|
|
||||||
client = SocketIOClient(socketURL: URL(string: "http://localhost")!)
|
|
||||||
engine = SocketEngine(client: client, url: URL(string: "http://localhost")!, options: nil)
|
|
||||||
|
|
||||||
client.setTestable()
|
|
||||||
}
|
|
||||||
|
|
||||||
func testBasicPollingMessage() {
|
func testBasicPollingMessage() {
|
||||||
let expect = expectation(description: "Basic polling test")
|
let expect = expectation(description: "Basic polling test")
|
||||||
client.on("blankTest") {data, ack in
|
socket.on("blankTest") {data, ack in
|
||||||
expect.fulfill()
|
expect.fulfill()
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -35,11 +24,11 @@ class SocketEngineTest: XCTestCase {
|
|||||||
let finalExpectation = expectation(description: "Final packet in poll test")
|
let finalExpectation = expectation(description: "Final packet in poll test")
|
||||||
var gotBlank = false
|
var gotBlank = false
|
||||||
|
|
||||||
client.on("blankTest") {data, ack in
|
socket.on("blankTest") {data, ack in
|
||||||
gotBlank = true
|
gotBlank = true
|
||||||
}
|
}
|
||||||
|
|
||||||
client.on("stringTest") {data, ack in
|
socket.on("stringTest") {data, ack in
|
||||||
if let str = data[0] as? String, gotBlank {
|
if let str = data[0] as? String, gotBlank {
|
||||||
if str == "hello" {
|
if str == "hello" {
|
||||||
finalExpectation.fulfill()
|
finalExpectation.fulfill()
|
||||||
@ -54,7 +43,7 @@ class SocketEngineTest: XCTestCase {
|
|||||||
func testEngineDoesErrorOnUnknownTransport() {
|
func testEngineDoesErrorOnUnknownTransport() {
|
||||||
let finalExpectation = expectation(description: "Unknown Transport")
|
let finalExpectation = expectation(description: "Unknown Transport")
|
||||||
|
|
||||||
client.on("error") {data, ack in
|
socket.on("error") {data, ack in
|
||||||
if let error = data[0] as? String, error == "Unknown transport" {
|
if let error = data[0] as? String, error == "Unknown transport" {
|
||||||
finalExpectation.fulfill()
|
finalExpectation.fulfill()
|
||||||
}
|
}
|
||||||
@ -67,7 +56,7 @@ class SocketEngineTest: XCTestCase {
|
|||||||
func testEngineDoesErrorOnUnknownMessage() {
|
func testEngineDoesErrorOnUnknownMessage() {
|
||||||
let finalExpectation = expectation(description: "Engine Errors")
|
let finalExpectation = expectation(description: "Engine Errors")
|
||||||
|
|
||||||
client.on("error") {data, ack in
|
socket.on("error") {data, ack in
|
||||||
finalExpectation.fulfill()
|
finalExpectation.fulfill()
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -78,7 +67,7 @@ class SocketEngineTest: XCTestCase {
|
|||||||
func testEngineDecodesUTF8Properly() {
|
func testEngineDecodesUTF8Properly() {
|
||||||
let expect = expectation(description: "Engine Decodes utf8")
|
let expect = expectation(description: "Engine Decodes utf8")
|
||||||
|
|
||||||
client.on("stringTest") {data, ack in
|
socket.on("stringTest") {data, ack in
|
||||||
XCTAssertEqual(data[0] as? String, "lïne one\nlīne \rtwo𦅙𦅛", "Failed string test")
|
XCTAssertEqual(data[0] as? String, "lïne one\nlīne \rtwo𦅙𦅛", "Failed string test")
|
||||||
expect.fulfill()
|
expect.fulfill()
|
||||||
}
|
}
|
||||||
@ -110,7 +99,7 @@ class SocketEngineTest: XCTestCase {
|
|||||||
let b64String = "b4aGVsbG8NCg=="
|
let b64String = "b4aGVsbG8NCg=="
|
||||||
let packetString = "451-[\"test\",{\"test\":{\"_placeholder\":true,\"num\":0}}]"
|
let packetString = "451-[\"test\",{\"test\":{\"_placeholder\":true,\"num\":0}}]"
|
||||||
|
|
||||||
client.on("test") {data, ack in
|
socket.on("test") {data, ack in
|
||||||
if let data = data[0] as? Data, let string = String(data: data, encoding: .utf8) {
|
if let data = data[0] as? Data, let string = String(data: data, encoding: .utf8) {
|
||||||
XCTAssertEqual(string, "hello")
|
XCTAssertEqual(string, "hello")
|
||||||
}
|
}
|
||||||
@ -123,4 +112,97 @@ class SocketEngineTest: XCTestCase {
|
|||||||
|
|
||||||
waitForExpectations(timeout: 3, handler: nil)
|
waitForExpectations(timeout: 3, handler: nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func testSettingExtraHeadersBeforeConnectSetsEngineExtraHeaders() {
|
||||||
|
let newValue = ["hello": "world"]
|
||||||
|
|
||||||
|
manager.engine = engine
|
||||||
|
manager.setTestStatus(.notConnected)
|
||||||
|
manager.config = [.extraHeaders(["new": "value"])]
|
||||||
|
manager.config.insert(.extraHeaders(newValue), replacing: true)
|
||||||
|
|
||||||
|
XCTAssertEqual(2, manager.config.count)
|
||||||
|
XCTAssertEqual(manager.engine!.extraHeaders!, newValue)
|
||||||
|
|
||||||
|
for config in manager.config {
|
||||||
|
switch config {
|
||||||
|
case let .extraHeaders(headers):
|
||||||
|
XCTAssertTrue(headers.keys.contains("hello"), "It should contain hello header key")
|
||||||
|
XCTAssertFalse(headers.keys.contains("new"), "It should not contain old data")
|
||||||
|
case .path:
|
||||||
|
continue
|
||||||
|
default:
|
||||||
|
XCTFail("It should only have two configs")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func testSettingExtraHeadersAfterConnectDoesNotIgnoreChanges() {
|
||||||
|
let newValue = ["hello": "world"]
|
||||||
|
|
||||||
|
manager.engine = engine
|
||||||
|
manager.setTestStatus(.connected)
|
||||||
|
engine.setConnected(true)
|
||||||
|
manager.config = [.extraHeaders(["new": "value"])]
|
||||||
|
manager.config.insert(.extraHeaders(["hello": "world"]), replacing: true)
|
||||||
|
|
||||||
|
XCTAssertEqual(2, manager.config.count)
|
||||||
|
XCTAssertEqual(manager.engine!.extraHeaders!, newValue)
|
||||||
|
}
|
||||||
|
|
||||||
|
func testSettingPathAfterConnectDoesNotIgnoreChanges() {
|
||||||
|
let newValue = "/newpath/"
|
||||||
|
|
||||||
|
manager.engine = engine
|
||||||
|
manager.setTestStatus(.connected)
|
||||||
|
engine.setConnected(true)
|
||||||
|
manager.config.insert(.path(newValue))
|
||||||
|
|
||||||
|
XCTAssertEqual(1, manager.config.count)
|
||||||
|
XCTAssertEqual(manager.engine!.socketPath, newValue)
|
||||||
|
}
|
||||||
|
|
||||||
|
func testSettingCompressAfterConnectDoesNotIgnoreChanges() {
|
||||||
|
manager.engine = engine
|
||||||
|
manager.setTestStatus(.connected)
|
||||||
|
engine.setConnected(true)
|
||||||
|
manager.config.insert(.compress)
|
||||||
|
|
||||||
|
XCTAssertEqual(2, manager.config.count)
|
||||||
|
XCTAssertTrue(manager.engine!.compress)
|
||||||
|
}
|
||||||
|
|
||||||
|
func testSettingForcePollingAfterConnectDoesNotIgnoreChanges() {
|
||||||
|
manager.engine = engine
|
||||||
|
manager.setTestStatus(.connected)
|
||||||
|
engine.setConnected(true)
|
||||||
|
manager.config.insert(.forcePolling(true))
|
||||||
|
|
||||||
|
XCTAssertEqual(2, manager.config.count)
|
||||||
|
XCTAssertTrue(manager.engine!.forcePolling)
|
||||||
|
}
|
||||||
|
|
||||||
|
func testSettingForceWebSocketsAfterConnectDoesNotIgnoreChanges() {
|
||||||
|
manager.engine = engine
|
||||||
|
manager.setTestStatus(.connected)
|
||||||
|
engine.setConnected(true)
|
||||||
|
manager.config.insert(.forceWebsockets(true))
|
||||||
|
|
||||||
|
XCTAssertEqual(2, manager.config.count)
|
||||||
|
XCTAssertTrue(manager.engine!.forceWebsockets)
|
||||||
|
}
|
||||||
|
|
||||||
|
var manager: SocketManager!
|
||||||
|
var socket: SocketIOClient!
|
||||||
|
var engine: SocketEngine!
|
||||||
|
|
||||||
|
override func setUp() {
|
||||||
|
super.setUp()
|
||||||
|
|
||||||
|
manager = SocketManager(socketURL: URL(string: "http://localhost")!)
|
||||||
|
socket = manager.defaultSocket
|
||||||
|
engine = SocketEngine(client: manager, url: URL(string: "http://localhost")!, options: nil)
|
||||||
|
|
||||||
|
socket.setTestable()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -9,15 +9,7 @@
|
|||||||
import XCTest
|
import XCTest
|
||||||
import SocketIO
|
import SocketIO
|
||||||
|
|
||||||
class TestSocketIOClientConfiguration: XCTestCase {
|
class TestSocketIOClientConfiguration : XCTestCase {
|
||||||
var config = [] as SocketIOClientConfiguration
|
|
||||||
|
|
||||||
override func setUp() {
|
|
||||||
super.setUp()
|
|
||||||
|
|
||||||
config = [.log(false), .forceNew(true)]
|
|
||||||
}
|
|
||||||
|
|
||||||
func testReplaceSameOption() {
|
func testReplaceSameOption() {
|
||||||
config.insert(.log(true))
|
config.insert(.log(true))
|
||||||
|
|
||||||
@ -43,4 +35,12 @@ class TestSocketIOClientConfiguration: XCTestCase {
|
|||||||
XCTFail()
|
XCTFail()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var config = [] as SocketIOClientConfiguration
|
||||||
|
|
||||||
|
override func setUp() {
|
||||||
|
config = [.log(false), .forceNew(true)]
|
||||||
|
|
||||||
|
super.setUp()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
174
Tests/TestSocketIO/SocketMangerTest.swift
Normal file
174
Tests/TestSocketIO/SocketMangerTest.swift
Normal file
@ -0,0 +1,174 @@
|
|||||||
|
//
|
||||||
|
// Created by Erik Little on 10/21/17.
|
||||||
|
//
|
||||||
|
|
||||||
|
import Dispatch
|
||||||
|
import Foundation
|
||||||
|
@testable import SocketIO
|
||||||
|
import XCTest
|
||||||
|
|
||||||
|
class SocketMangerTest : XCTestCase {
|
||||||
|
func testManagerProperties() {
|
||||||
|
XCTAssertNotNil(manager.defaultSocket)
|
||||||
|
XCTAssertNil(manager.engine)
|
||||||
|
XCTAssertFalse(manager.forceNew)
|
||||||
|
XCTAssertEqual(manager.handleQueue, DispatchQueue.main)
|
||||||
|
XCTAssertTrue(manager.reconnects)
|
||||||
|
XCTAssertEqual(manager.reconnectWait, 10)
|
||||||
|
XCTAssertEqual(manager.status, .notConnected)
|
||||||
|
}
|
||||||
|
|
||||||
|
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")
|
||||||
|
|
||||||
|
socket.connect()
|
||||||
|
socket2.connect()
|
||||||
|
|
||||||
|
manager.fakeConnecting()
|
||||||
|
manager.fakeConnecting(toNamespace: "/swift")
|
||||||
|
|
||||||
|
waitForExpectations(timeout: 0.3)
|
||||||
|
}
|
||||||
|
|
||||||
|
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")
|
||||||
|
|
||||||
|
socket2.on(clientEvent: .connect) {data, ack in
|
||||||
|
self.manager.disconnect()
|
||||||
|
self.manager.fakeDisconnecting()
|
||||||
|
}
|
||||||
|
|
||||||
|
socket.connect()
|
||||||
|
socket2.connect()
|
||||||
|
|
||||||
|
manager.fakeConnecting()
|
||||||
|
manager.fakeConnecting(toNamespace: "/swift")
|
||||||
|
|
||||||
|
waitForExpectations(timeout: 0.3)
|
||||||
|
}
|
||||||
|
|
||||||
|
func testManagerEmitAll() {
|
||||||
|
setUpSockets()
|
||||||
|
|
||||||
|
socket.expectations[ManagerExpectation.emitAllEventCalled] = expectation(description: "The manager should emit an event to the default socket")
|
||||||
|
socket2.expectations[ManagerExpectation.emitAllEventCalled] = expectation(description: "The manager should emit an event to the socket")
|
||||||
|
|
||||||
|
socket2.on(clientEvent: .connect) {data, ack in
|
||||||
|
self.manager.emitAll("event", "testing")
|
||||||
|
}
|
||||||
|
|
||||||
|
socket.connect()
|
||||||
|
socket2.connect()
|
||||||
|
|
||||||
|
manager.fakeConnecting()
|
||||||
|
manager.fakeConnecting(toNamespace: "/swift")
|
||||||
|
|
||||||
|
waitForExpectations(timeout: 0.3)
|
||||||
|
}
|
||||||
|
|
||||||
|
private func setUpSockets() {
|
||||||
|
socket = manager.testSocket(forNamespace: "/")
|
||||||
|
socket2 = manager.testSocket(forNamespace: "/swift")
|
||||||
|
}
|
||||||
|
|
||||||
|
private var manager: TestManager!
|
||||||
|
private var socket: TestSocket!
|
||||||
|
private var socket2: TestSocket!
|
||||||
|
|
||||||
|
override func setUp() {
|
||||||
|
super.setUp()
|
||||||
|
|
||||||
|
manager = TestManager(socketURL: URL(string: "http://localhost/")!, config: [.log(false)])
|
||||||
|
socket = nil
|
||||||
|
socket2 = nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public enum ManagerExpectation : String {
|
||||||
|
case didConnectCalled
|
||||||
|
case didDisconnectCalled
|
||||||
|
case emitAllEventCalled
|
||||||
|
}
|
||||||
|
|
||||||
|
public class TestManager : SocketManager {
|
||||||
|
public override func disconnect() {
|
||||||
|
setTestStatus(.disconnected)
|
||||||
|
}
|
||||||
|
|
||||||
|
@objc
|
||||||
|
public func testSocket(forNamespace nsp: String) -> TestSocket {
|
||||||
|
return socket(forNamespace: nsp) as! TestSocket
|
||||||
|
}
|
||||||
|
|
||||||
|
@objc
|
||||||
|
public func fakeConnecting(toNamespace nsp: String) {
|
||||||
|
DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + 0.1) {
|
||||||
|
// Fake connecting
|
||||||
|
self.parseEngineMessage("0\(nsp)")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@objc
|
||||||
|
public func fakeDisconnecting() {
|
||||||
|
engineDidClose(reason: "")
|
||||||
|
}
|
||||||
|
|
||||||
|
@objc
|
||||||
|
public func fakeConnecting() {
|
||||||
|
engineDidOpen(reason: "")
|
||||||
|
}
|
||||||
|
|
||||||
|
public override func socket(forNamespace nsp: String) -> SocketIOClient {
|
||||||
|
// set socket to our test socket, the superclass method will get this from nsps
|
||||||
|
nsps[nsp] = TestSocket(manager: self, nsp: nsp)
|
||||||
|
|
||||||
|
return super.socket(forNamespace: nsp)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class TestSocket : SocketIOClient {
|
||||||
|
public var expectations = [ManagerExpectation: XCTestExpectation]()
|
||||||
|
|
||||||
|
@objc
|
||||||
|
public var expects = NSMutableDictionary()
|
||||||
|
|
||||||
|
public override func didConnect(toNamespace nsp: String) {
|
||||||
|
expectations[ManagerExpectation.didConnectCalled]?.fulfill()
|
||||||
|
expectations[ManagerExpectation.didConnectCalled] = nil
|
||||||
|
|
||||||
|
if let expect = expects[ManagerExpectation.didConnectCalled.rawValue] as? XCTestExpectation {
|
||||||
|
expect.fulfill()
|
||||||
|
expects[ManagerExpectation.didConnectCalled.rawValue] = nil
|
||||||
|
}
|
||||||
|
|
||||||
|
super.didConnect(toNamespace: nsp)
|
||||||
|
}
|
||||||
|
|
||||||
|
public override func didDisconnect(reason: String) {
|
||||||
|
expectations[ManagerExpectation.didDisconnectCalled]?.fulfill()
|
||||||
|
expectations[ManagerExpectation.didDisconnectCalled] = nil
|
||||||
|
|
||||||
|
if let expect = expects[ManagerExpectation.didDisconnectCalled.rawValue] as? XCTestExpectation {
|
||||||
|
expect.fulfill()
|
||||||
|
expects[ManagerExpectation.didDisconnectCalled.rawValue] = nil
|
||||||
|
}
|
||||||
|
|
||||||
|
super.didDisconnect(reason: reason)
|
||||||
|
}
|
||||||
|
|
||||||
|
public override func emit(_ event: String, with items: [Any]) {
|
||||||
|
expectations[ManagerExpectation.emitAllEventCalled]?.fulfill()
|
||||||
|
expectations[ManagerExpectation.emitAllEventCalled] = nil
|
||||||
|
|
||||||
|
if let expect = expects[ManagerExpectation.emitAllEventCalled.rawValue] as? XCTestExpectation {
|
||||||
|
expect.fulfill()
|
||||||
|
expects[ManagerExpectation.emitAllEventCalled.rawValue] = nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -10,26 +10,6 @@ import XCTest
|
|||||||
@testable import SocketIO
|
@testable import SocketIO
|
||||||
|
|
||||||
class SocketParserTest: XCTestCase {
|
class SocketParserTest: XCTestCase {
|
||||||
let testSocket = SocketIOClient(socketURL: URL(string: "http://localhost/")!)
|
|
||||||
|
|
||||||
//Format key: message; namespace-data-binary-id
|
|
||||||
static let packetTypes: [String: (String, [Any], [Data], Int)] = [
|
|
||||||
"0": ("/", [], [], -1), "1": ("/", [], [], -1),
|
|
||||||
"25[\"test\"]": ("/", ["test"], [], 5),
|
|
||||||
"2[\"test\",\"~~0\"]": ("/", ["test", "~~0"], [], -1),
|
|
||||||
"2/swift,[\"testArrayEmitReturn\",[\"test3\",\"test4\"]]": ("/swift", ["testArrayEmitReturn", ["test3", "test4"] as NSArray], [], -1),
|
|
||||||
"51-/swift,[\"testMultipleItemsWithBufferEmitReturn\",[1,2],{\"test\":\"bob\"},25,\"polo\",{\"_placeholder\":true,\"num\":0}]": ("/swift", ["testMultipleItemsWithBufferEmitReturn", [1, 2] as NSArray, ["test": "bob"] as NSDictionary, 25, "polo", ["_placeholder": true, "num": 0] as NSDictionary], [], -1),
|
|
||||||
"3/swift,0[[\"test3\",\"test4\"]]": ("/swift", [["test3", "test4"] as NSArray], [], 0),
|
|
||||||
"61-/swift,19[[1,2],{\"test\":\"bob\"},25,\"polo\",{\"_placeholder\":true,\"num\":0}]":
|
|
||||||
("/swift", [ [1, 2] as NSArray, ["test": "bob"] as NSDictionary, 25, "polo", ["_placeholder": true, "num": 0] as NSDictionary], [], 19),
|
|
||||||
"4/swift,": ("/swift", [], [], -1),
|
|
||||||
"0/swift": ("/swift", [], [], -1),
|
|
||||||
"1/swift": ("/swift", [], [], -1),
|
|
||||||
"4\"ERROR\"": ("/", ["ERROR"], [], -1),
|
|
||||||
"4{\"test\":2}": ("/", [["test": 2]], [], -1),
|
|
||||||
"41": ("/", [1], [], -1),
|
|
||||||
"4[1, \"hello\"]": ("/", [1, "hello"], [], -1)]
|
|
||||||
|
|
||||||
func testDisconnect() {
|
func testDisconnect() {
|
||||||
let message = "1"
|
let message = "1"
|
||||||
validateParseResult(message)
|
validateParseResult(message)
|
||||||
@ -108,7 +88,7 @@ class SocketParserTest: XCTestCase {
|
|||||||
func testInvalidInput() {
|
func testInvalidInput() {
|
||||||
let message = "8"
|
let message = "8"
|
||||||
do {
|
do {
|
||||||
let _ = try testSocket.parseString(message)
|
let _ = try testManager.parseString(message)
|
||||||
XCTFail()
|
XCTFail()
|
||||||
} catch {
|
} catch {
|
||||||
|
|
||||||
@ -125,8 +105,8 @@ class SocketParserTest: XCTestCase {
|
|||||||
|
|
||||||
func validateParseResult(_ message: String) {
|
func validateParseResult(_ message: String) {
|
||||||
let validValues = SocketParserTest.packetTypes[message]!
|
let validValues = SocketParserTest.packetTypes[message]!
|
||||||
let packet = try! testSocket.parseString(message)
|
let packet = try! testManager.parseString(message)
|
||||||
let type = String(message.characters.prefix(1))
|
let type = String(message.prefix(1))
|
||||||
|
|
||||||
XCTAssertEqual(packet.type, SocketPacket.PacketType(rawValue: Int(type) ?? -1)!)
|
XCTAssertEqual(packet.type, SocketPacket.PacketType(rawValue: Int(type) ?? -1)!)
|
||||||
XCTAssertEqual(packet.nsp, validValues.0)
|
XCTAssertEqual(packet.nsp, validValues.0)
|
||||||
@ -139,8 +119,29 @@ class SocketParserTest: XCTestCase {
|
|||||||
let keys = Array(SocketParserTest.packetTypes.keys)
|
let keys = Array(SocketParserTest.packetTypes.keys)
|
||||||
measure {
|
measure {
|
||||||
for item in keys.enumerated() {
|
for item in keys.enumerated() {
|
||||||
_ = try! self.testSocket.parseString(item.element)
|
_ = try! self.testManager.parseString(item.element)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let testManager = SocketManager(socketURL: URL(string: "http://localhost/")!)
|
||||||
|
|
||||||
|
//Format key: message; namespace-data-binary-id
|
||||||
|
static let packetTypes: [String: (String, [Any], [Data], Int)] = [
|
||||||
|
"0": ("/", [], [], -1), "1": ("/", [], [], -1),
|
||||||
|
"25[\"test\"]": ("/", ["test"], [], 5),
|
||||||
|
"2[\"test\",\"~~0\"]": ("/", ["test", "~~0"], [], -1),
|
||||||
|
"2/swift,[\"testArrayEmitReturn\",[\"test3\",\"test4\"]]": ("/swift", ["testArrayEmitReturn", ["test3", "test4"] as NSArray], [], -1),
|
||||||
|
"51-/swift,[\"testMultipleItemsWithBufferEmitReturn\",[1,2],{\"test\":\"bob\"},25,\"polo\",{\"_placeholder\":true,\"num\":0}]": ("/swift", ["testMultipleItemsWithBufferEmitReturn", [1, 2] as NSArray, ["test": "bob"] as NSDictionary, 25, "polo", ["_placeholder": true, "num": 0] as NSDictionary], [], -1),
|
||||||
|
"3/swift,0[[\"test3\",\"test4\"]]": ("/swift", [["test3", "test4"] as NSArray], [], 0),
|
||||||
|
"61-/swift,19[[1,2],{\"test\":\"bob\"},25,\"polo\",{\"_placeholder\":true,\"num\":0}]":
|
||||||
|
("/swift", [ [1, 2] as NSArray, ["test": "bob"] as NSDictionary, 25, "polo", ["_placeholder": true, "num": 0] as NSDictionary], [], 19),
|
||||||
|
"4/swift,": ("/swift", [], [], -1),
|
||||||
|
"0/swift": ("/swift", [], [], -1),
|
||||||
|
"1/swift": ("/swift", [], [], -1),
|
||||||
|
"4\"ERROR\"": ("/", ["ERROR"], [], -1),
|
||||||
|
"4{\"test\":2}": ("/", [["test": 2]], [], -1),
|
||||||
|
"41": ("/", [1], [], -1),
|
||||||
|
"4[1, \"hello\"]": ("/", [1, "hello"], [], -1)
|
||||||
|
]
|
||||||
}
|
}
|
||||||
|
|||||||
@ -34,7 +34,7 @@ class SocketSideEffectTest: XCTestCase {
|
|||||||
expect.fulfill()
|
expect.fulfill()
|
||||||
}
|
}
|
||||||
|
|
||||||
socket.parseSocketMessage("30[\"hello world\"]")
|
manager.parseEngineMessage("30[\"hello world\"]")
|
||||||
waitForExpectations(timeout: 3, handler: nil)
|
waitForExpectations(timeout: 3, handler: nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -45,8 +45,8 @@ class SocketSideEffectTest: XCTestCase {
|
|||||||
expect.fulfill()
|
expect.fulfill()
|
||||||
}
|
}
|
||||||
|
|
||||||
socket.parseSocketMessage("61-0[{\"_placeholder\":true,\"num\":0},{\"test\":true}]")
|
manager.parseEngineMessage("61-0[{\"_placeholder\":true,\"num\":0},{\"test\":true}]")
|
||||||
socket.parseBinaryData(Data())
|
manager.parseEngineBinaryData(Data())
|
||||||
waitForExpectations(timeout: 3, handler: nil)
|
waitForExpectations(timeout: 3, handler: nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -57,7 +57,7 @@ class SocketSideEffectTest: XCTestCase {
|
|||||||
expect.fulfill()
|
expect.fulfill()
|
||||||
}
|
}
|
||||||
|
|
||||||
socket.parseSocketMessage("2[\"test\",\"hello world\"]")
|
manager.parseEngineMessage("2[\"test\",\"hello world\"]")
|
||||||
waitForExpectations(timeout: 3, handler: nil)
|
waitForExpectations(timeout: 3, handler: nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -68,7 +68,7 @@ class SocketSideEffectTest: XCTestCase {
|
|||||||
expect.fulfill()
|
expect.fulfill()
|
||||||
}
|
}
|
||||||
|
|
||||||
socket.parseSocketMessage("2[\"test\",\"\\\"hello world\\\"\"]")
|
manager.parseEngineMessage("2[\"test\",\"\\\"hello world\\\"\"]")
|
||||||
waitForExpectations(timeout: 3, handler: nil)
|
waitForExpectations(timeout: 3, handler: nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -80,7 +80,7 @@ class SocketSideEffectTest: XCTestCase {
|
|||||||
expect.fulfill()
|
expect.fulfill()
|
||||||
}
|
}
|
||||||
|
|
||||||
socket.parseSocketMessage("2[\"test\",\"hello world\"]")
|
manager.parseEngineMessage("2[\"test\",\"hello world\"]")
|
||||||
waitForExpectations(timeout: 3, handler: nil)
|
waitForExpectations(timeout: 3, handler: nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -96,7 +96,7 @@ class SocketSideEffectTest: XCTestCase {
|
|||||||
|
|
||||||
DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + 0.1) {
|
DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + 0.1) {
|
||||||
// Fake connecting
|
// Fake connecting
|
||||||
self.socket.parseEngineMessage("0/")
|
self.manager.parseEngineMessage("0/")
|
||||||
}
|
}
|
||||||
|
|
||||||
waitForExpectations(timeout: 3, handler: nil)
|
waitForExpectations(timeout: 3, handler: nil)
|
||||||
@ -136,59 +136,47 @@ class SocketSideEffectTest: XCTestCase {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
socket.parseSocketMessage("4\"test error\"")
|
manager.parseEngineMessage("4\"test error\"")
|
||||||
waitForExpectations(timeout: 3, handler: nil)
|
waitForExpectations(timeout: 3, handler: nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
func testHandleBinaryEvent() {
|
func testHandleBinaryEvent() {
|
||||||
let expect = expectation(description: "handled binary event")
|
let expect = expectation(description: "handled binary event")
|
||||||
socket.on("test") {data, ack in
|
socket.on("test") {data, ack in
|
||||||
if let dict = data[0] as? NSDictionary, let data = dict["test"] as? NSData {
|
if let dict = data[0] as? [String: Any], let data = dict["test"] as? Data {
|
||||||
XCTAssertEqual(data as Data, self.data)
|
XCTAssertEqual(data as Data, self.data)
|
||||||
expect.fulfill()
|
expect.fulfill()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
socket.parseSocketMessage("51-[\"test\",{\"test\":{\"_placeholder\":true,\"num\":0}}]")
|
manager.parseEngineMessage("51-[\"test\",{\"test\":{\"_placeholder\":true,\"num\":0}}]")
|
||||||
socket.parseBinaryData(data)
|
manager.parseEngineBinaryData(data)
|
||||||
waitForExpectations(timeout: 3, handler: nil)
|
waitForExpectations(timeout: 3, handler: nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
func testHandleMultipleBinaryEvent() {
|
func testHandleMultipleBinaryEvent() {
|
||||||
let expect = expectation(description: "handled multiple binary event")
|
let expect = expectation(description: "handled multiple binary event")
|
||||||
socket.on("test") {data, ack in
|
socket.on("test") {data, ack in
|
||||||
if let dict = data[0] as? NSDictionary, let data = dict["test"] as? NSData,
|
if let dict = data[0] as? [String: Any], let data = dict["test"] as? Data,
|
||||||
let data2 = dict["test2"] as? NSData {
|
let data2 = dict["test2"] as? Data {
|
||||||
XCTAssertEqual(data as Data, self.data)
|
XCTAssertEqual(data as Data, self.data)
|
||||||
XCTAssertEqual(data2 as Data, self.data2)
|
XCTAssertEqual(data2 as Data, self.data2)
|
||||||
expect.fulfill()
|
expect.fulfill()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
socket.parseSocketMessage("52-[\"test\",{\"test\":{\"_placeholder\":true,\"num\":0},\"test2\":{\"_placeholder\":true,\"num\":1}}]")
|
manager.parseEngineMessage("52-[\"test\",{\"test\":{\"_placeholder\":true,\"num\":0},\"test2\":{\"_placeholder\":true,\"num\":1}}]")
|
||||||
socket.parseBinaryData(data)
|
manager.parseEngineBinaryData(data)
|
||||||
socket.parseBinaryData(data2)
|
manager.parseEngineBinaryData(data2)
|
||||||
waitForExpectations(timeout: 3, handler: nil)
|
waitForExpectations(timeout: 3, handler: nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
func testSocketManager() {
|
|
||||||
let manager = SocketClientManager.sharedManager
|
|
||||||
manager["test"] = socket
|
|
||||||
|
|
||||||
XCTAssert(manager["test"] === socket, "failed to get socket")
|
|
||||||
|
|
||||||
manager["test"] = nil
|
|
||||||
|
|
||||||
XCTAssert(manager["test"] == nil, "socket not removed")
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
func testChangingStatusCallsStatusChangeHandler() {
|
func testChangingStatusCallsStatusChangeHandler() {
|
||||||
let expect = expectation(description: "The client should announce when the status changes")
|
let expect = expectation(description: "The client should announce when the status changes")
|
||||||
let statusChange = SocketIOClientStatus.connecting
|
let statusChange = SocketIOStatus.connecting
|
||||||
|
|
||||||
socket.on("statusChange") {data, ack in
|
socket.on("statusChange") {data, ack in
|
||||||
guard let status = data[0] as? SocketIOClientStatus else {
|
guard let status = data[0] as? SocketIOStatus else {
|
||||||
XCTFail("Status should be one of the defined statuses")
|
XCTFail("Status should be one of the defined statuses")
|
||||||
|
|
||||||
return
|
return
|
||||||
@ -251,9 +239,9 @@ class SocketSideEffectTest: XCTestCase {
|
|||||||
func testConnectTimesOutIfNotConnected() {
|
func testConnectTimesOutIfNotConnected() {
|
||||||
let expect = expectation(description: "The client should call the timeout function")
|
let expect = expectation(description: "The client should call the timeout function")
|
||||||
|
|
||||||
|
socket = manager.socket(forNamespace: "/someNamespace")
|
||||||
socket.setTestStatus(.notConnected)
|
socket.setTestStatus(.notConnected)
|
||||||
socket.nsp = "/someNamespace"
|
manager.engine = TestEngine(client: manager, url: manager.socketURL, options: nil)
|
||||||
socket.engine = TestEngine(client: socket, url: socket.socketURL, options: nil)
|
|
||||||
|
|
||||||
socket.connect(timeoutAfter: 0.5, withHandler: {
|
socket.connect(timeoutAfter: 0.5, withHandler: {
|
||||||
expect.fulfill()
|
expect.fulfill()
|
||||||
@ -266,7 +254,7 @@ class SocketSideEffectTest: XCTestCase {
|
|||||||
let expect = expectation(description: "The client should not call the timeout function")
|
let expect = expectation(description: "The client should not call the timeout function")
|
||||||
|
|
||||||
socket.setTestStatus(.notConnected)
|
socket.setTestStatus(.notConnected)
|
||||||
socket.engine = TestEngine(client: socket, url: socket.socketURL, options: nil)
|
manager.engine = TestEngine(client: manager, url: manager.socketURL, options: nil)
|
||||||
|
|
||||||
socket.on(clientEvent: .connect) {data, ack in
|
socket.on(clientEvent: .connect) {data, ack in
|
||||||
expect.fulfill()
|
expect.fulfill()
|
||||||
@ -278,7 +266,7 @@ class SocketSideEffectTest: XCTestCase {
|
|||||||
|
|
||||||
DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + 0.1) {
|
DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + 0.1) {
|
||||||
// Fake connecting
|
// Fake connecting
|
||||||
self.socket.parseEngineMessage("0/")
|
self.manager.parseEngineMessage("0/")
|
||||||
}
|
}
|
||||||
|
|
||||||
waitForExpectations(timeout: 2)
|
waitForExpectations(timeout: 2)
|
||||||
@ -288,7 +276,7 @@ class SocketSideEffectTest: XCTestCase {
|
|||||||
let expect = expectation(description: "The client call the connect handler")
|
let expect = expectation(description: "The client call the connect handler")
|
||||||
|
|
||||||
socket.setTestStatus(.notConnected)
|
socket.setTestStatus(.notConnected)
|
||||||
socket.engine = TestEngine(client: socket, url: socket.socketURL, options: nil)
|
manager.engine = TestEngine(client: manager, url: manager.socketURL, options: nil)
|
||||||
|
|
||||||
socket.on(clientEvent: .connect) {data, ack in
|
socket.on(clientEvent: .connect) {data, ack in
|
||||||
expect.fulfill()
|
expect.fulfill()
|
||||||
@ -305,9 +293,9 @@ class SocketSideEffectTest: XCTestCase {
|
|||||||
let expect = expectation(description: "The client should not call the timeout function")
|
let expect = expectation(description: "The client should not call the timeout function")
|
||||||
let nspString = "/swift"
|
let nspString = "/swift"
|
||||||
|
|
||||||
|
socket = manager.socket(forNamespace: "/swift")
|
||||||
socket.setTestStatus(.notConnected)
|
socket.setTestStatus(.notConnected)
|
||||||
socket.nsp = nspString
|
manager.engine = TestEngine(client: manager, url: manager.socketURL, options: nil)
|
||||||
socket.engine = TestEngine(client: socket, url: socket.socketURL, options: nil)
|
|
||||||
|
|
||||||
socket.on(clientEvent: .connect) {data, ack in
|
socket.on(clientEvent: .connect) {data, ack in
|
||||||
guard let nsp = data[0] as? String else {
|
guard let nsp = data[0] as? String else {
|
||||||
@ -327,7 +315,7 @@ class SocketSideEffectTest: XCTestCase {
|
|||||||
|
|
||||||
DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + 0.1) {
|
DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + 0.1) {
|
||||||
// Fake connecting
|
// Fake connecting
|
||||||
self.socket.parseEngineMessage("0/swift")
|
self.manager.parseEngineMessage("0/swift")
|
||||||
}
|
}
|
||||||
|
|
||||||
waitForExpectations(timeout: 2)
|
waitForExpectations(timeout: 2)
|
||||||
@ -377,48 +365,68 @@ class SocketSideEffectTest: XCTestCase {
|
|||||||
|
|
||||||
func testSettingConfigAfterInit() {
|
func testSettingConfigAfterInit() {
|
||||||
socket.setTestStatus(.notConnected)
|
socket.setTestStatus(.notConnected)
|
||||||
socket.config.insert(.log(true))
|
manager.config.insert(.log(true))
|
||||||
|
|
||||||
XCTAssertTrue(DefaultSocketLogger.Logger.log, "It should set logging to true after creation")
|
XCTAssertTrue(DefaultSocketLogger.Logger.log, "It should set logging to true after creation")
|
||||||
|
|
||||||
socket.config = [.log(false), .nsp("/test")]
|
manager.config = [.log(false)]
|
||||||
|
|
||||||
XCTAssertFalse(DefaultSocketLogger.Logger.log, "It should set logging to false after creation")
|
XCTAssertFalse(DefaultSocketLogger.Logger.log, "It should set logging to false after creation")
|
||||||
XCTAssertEqual(socket.nsp, "/test", "It should set the namespace after creation")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func testSettingExtraHeadersAfterInit() {
|
func testSettingConfigAfterDisconnect() {
|
||||||
socket.setTestStatus(.notConnected)
|
socket.setTestStatus(.disconnected)
|
||||||
socket.config = [.extraHeaders(["new": "value"])]
|
manager.config.insert(.log(true))
|
||||||
socket.config.insert(.extraHeaders(["hello": "world"]), replacing: true)
|
|
||||||
|
|
||||||
for config in socket.config {
|
XCTAssertTrue(DefaultSocketLogger.Logger.log, "It should set logging to true after creation")
|
||||||
switch config {
|
|
||||||
case let .extraHeaders(headers):
|
manager.config = [.log(false)]
|
||||||
XCTAssertTrue(headers.keys.contains("hello"), "It should contain hello header key")
|
|
||||||
XCTAssertFalse(headers.keys.contains("new"), "It should not contain old data")
|
XCTAssertFalse(DefaultSocketLogger.Logger.log, "It should set logging to false after creation")
|
||||||
case .path:
|
}
|
||||||
continue
|
|
||||||
default:
|
func testSettingConfigAfterInitWhenConnectedDoesNotIgnoreChanges() {
|
||||||
XCTFail("It should only have two configs")
|
manager.setTestStatus(.connected)
|
||||||
}
|
manager.config = [.log(true)]
|
||||||
|
|
||||||
|
XCTAssertTrue(DefaultSocketLogger.Logger.log, "It should set logging to false after creation")
|
||||||
|
}
|
||||||
|
|
||||||
|
func testClientCallsSentPingHandler() {
|
||||||
|
let expect = expectation(description: "The client should emit a ping event")
|
||||||
|
|
||||||
|
socket.on(clientEvent: .ping) {data, ack in
|
||||||
|
expect.fulfill()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
manager.engineDidSendPing()
|
||||||
|
|
||||||
|
waitForExpectations(timeout: 0.2)
|
||||||
}
|
}
|
||||||
|
|
||||||
func testSettingConfigAfterInitWhenConnectedIgnoresChanges() {
|
func testClientCallsGotPongHandler() {
|
||||||
socket.config = [.log(true), .nsp("/test")]
|
let expect = expectation(description: "The client should emit a pong event")
|
||||||
|
|
||||||
XCTAssertFalse(DefaultSocketLogger.Logger.log, "It should set logging to false after creation")
|
socket.on(clientEvent: .pong) {data, ack in
|
||||||
XCTAssertEqual(socket.nsp, "/", "It should set the namespace after creation")
|
expect.fulfill()
|
||||||
|
}
|
||||||
|
|
||||||
|
manager.engineDidReceivePong()
|
||||||
|
|
||||||
|
waitForExpectations(timeout: 0.2)
|
||||||
}
|
}
|
||||||
|
|
||||||
let data = "test".data(using: String.Encoding.utf8)!
|
let data = "test".data(using: String.Encoding.utf8)!
|
||||||
let data2 = "test2".data(using: String.Encoding.utf8)!
|
let data2 = "test2".data(using: String.Encoding.utf8)!
|
||||||
|
|
||||||
|
private var manager: SocketManager!
|
||||||
private var socket: SocketIOClient!
|
private var socket: SocketIOClient!
|
||||||
|
|
||||||
override func setUp() {
|
override func setUp() {
|
||||||
super.setUp()
|
super.setUp()
|
||||||
socket = SocketIOClient(socketURL: URL(string: "http://localhost/")!)
|
|
||||||
|
manager = SocketManager(socketURL: URL(string: "http://localhost/")!, config: [.log(false)])
|
||||||
|
socket = manager.defaultSocket
|
||||||
socket.setTestable()
|
socket.setTestable()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -437,6 +445,7 @@ struct ThrowingData : SocketData {
|
|||||||
class TestEngine : SocketEngineSpec {
|
class TestEngine : SocketEngineSpec {
|
||||||
weak var client: SocketEngineClient?
|
weak var client: SocketEngineClient?
|
||||||
private(set) var closed = false
|
private(set) var closed = false
|
||||||
|
private(set) var compress = false
|
||||||
private(set) var connected = false
|
private(set) var connected = false
|
||||||
var connectParams: [String: Any]? = nil
|
var connectParams: [String: Any]? = nil
|
||||||
private(set) var cookies: [HTTPCookie]? = nil
|
private(set) var cookies: [HTTPCookie]? = nil
|
||||||
@ -454,7 +463,7 @@ class TestEngine : SocketEngineSpec {
|
|||||||
private(set) var websocket = false
|
private(set) var websocket = false
|
||||||
private(set) var ws: WebSocket? = nil
|
private(set) var ws: WebSocket? = nil
|
||||||
|
|
||||||
required init(client: SocketEngineClient, url: URL, options: NSDictionary?) {
|
required init(client: SocketEngineClient, url: URL, options: [String: Any]?) {
|
||||||
self.client = client
|
self.client = client
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
16
Tests/TestSocketIOObjc/ManagerObjectiveCTest.h
Normal file
16
Tests/TestSocketIOObjc/ManagerObjectiveCTest.h
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
//
|
||||||
|
// Created by Erik Little on 10/21/17.
|
||||||
|
//
|
||||||
|
|
||||||
|
#import "SocketIO_Tests-Swift.h"
|
||||||
|
|
||||||
|
@import XCTest;
|
||||||
|
@import SocketIO;
|
||||||
|
|
||||||
|
@interface ManagerObjectiveCTest : XCTestCase
|
||||||
|
|
||||||
|
@property TestSocket* socket;
|
||||||
|
@property TestSocket* socket2;
|
||||||
|
@property TestManager* manager;
|
||||||
|
|
||||||
|
@end
|
||||||
122
Tests/TestSocketIOObjc/ManagerObjectiveCTest.m
Normal file
122
Tests/TestSocketIOObjc/ManagerObjectiveCTest.m
Normal file
@ -0,0 +1,122 @@
|
|||||||
|
//
|
||||||
|
// Created by Erik Little on 10/21/17.
|
||||||
|
//
|
||||||
|
|
||||||
|
#import "ManagerObjectiveCTest.h"
|
||||||
|
|
||||||
|
@import Dispatch;
|
||||||
|
@import Foundation;
|
||||||
|
@import XCTest;
|
||||||
|
@import SocketIO;
|
||||||
|
|
||||||
|
@implementation ManagerObjectiveCTest
|
||||||
|
|
||||||
|
- (void)testSettingConfig {
|
||||||
|
NSURL* url = [[NSURL alloc] initWithString:@"http://localhost"];
|
||||||
|
self.manager = [[TestManager alloc] initWithSocketURL:url config:@{@"forceNew": @YES}];
|
||||||
|
|
||||||
|
XCTAssertTrue(self.manager.forceNew);
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)testManagerProperties {
|
||||||
|
XCTAssertNotNil(self.manager.defaultSocket);
|
||||||
|
XCTAssertNil(self.manager.engine);
|
||||||
|
XCTAssertFalse(self.manager.forceNew);
|
||||||
|
XCTAssertEqual(self.manager.handleQueue, dispatch_get_main_queue());
|
||||||
|
XCTAssertTrue(self.manager.reconnects);
|
||||||
|
XCTAssertEqual(self.manager.reconnectWait, 10);
|
||||||
|
XCTAssertEqual(self.manager.status, SocketIOStatusNotConnected);
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)testConnectSocketSyntax {
|
||||||
|
[self setUpSockets];
|
||||||
|
[self.manager connectSocket:self.socket];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)testDisconnectSocketSyntax {
|
||||||
|
[self setUpSockets];
|
||||||
|
[self.manager disconnectSocket:self.socket];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)testSocketForNamespaceSyntax {
|
||||||
|
SocketIOClient* client = [self.manager socketForNamespace:@"/swift"];
|
||||||
|
client = nil;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)testManagerCallsConnect {
|
||||||
|
[self setUpSockets];
|
||||||
|
|
||||||
|
XCTestExpectation* expect = [self expectationWithDescription:@"The manager should call connect on the default socket"];
|
||||||
|
XCTestExpectation* expect2 = [self expectationWithDescription:@"The manager should call connect on the socket"];
|
||||||
|
|
||||||
|
self.socket.expects[@"didConnectCalled"] = expect;
|
||||||
|
self.socket2.expects[@"didConnectCalled"] = expect2;
|
||||||
|
|
||||||
|
[self.socket connect];
|
||||||
|
[self.socket2 connect];
|
||||||
|
|
||||||
|
[self.manager fakeConnecting];
|
||||||
|
[self.manager fakeConnectingToNamespace:@"/swift"];
|
||||||
|
|
||||||
|
[self waitForExpectationsWithTimeout:0.3 handler:nil];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)testManagerCallsDisconnect {
|
||||||
|
[self setUpSockets];
|
||||||
|
|
||||||
|
XCTestExpectation* expect = [self expectationWithDescription:@"The manager should call disconnect on the default socket"];
|
||||||
|
XCTestExpectation* expect2 = [self expectationWithDescription:@"The manager should call disconnect on the socket"];
|
||||||
|
|
||||||
|
self.socket.expects[@"didDisconnectCalled"] = expect;
|
||||||
|
self.socket2.expects[@"didDisconnectCalled"] = expect2;
|
||||||
|
|
||||||
|
[self.socket2 on:@"connect" callback:^(NSArray* data, SocketAckEmitter* ack) {
|
||||||
|
[self.manager disconnect];
|
||||||
|
[self.manager fakeDisconnecting];
|
||||||
|
}];
|
||||||
|
|
||||||
|
[self.socket connect];
|
||||||
|
[self.socket2 connect];
|
||||||
|
|
||||||
|
[self.manager fakeConnecting];
|
||||||
|
[self.manager fakeConnectingToNamespace:@"/swift"];
|
||||||
|
|
||||||
|
[self waitForExpectationsWithTimeout:0.3 handler:nil];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)testManagerEmitAll {
|
||||||
|
[self setUpSockets];
|
||||||
|
|
||||||
|
XCTestExpectation* expect = [self expectationWithDescription:@"The manager should emit an event to the default socket"];
|
||||||
|
XCTestExpectation* expect2 = [self expectationWithDescription:@"The manager should emit an event to the socket"];
|
||||||
|
|
||||||
|
self.socket.expects[@"emitAllEventCalled"] = expect;
|
||||||
|
self.socket2.expects[@"emitAllEventCalled"] = expect2;
|
||||||
|
|
||||||
|
[self.socket2 on:@"connect" callback:^(NSArray* data, SocketAckEmitter* ack) {
|
||||||
|
[self.manager emitAll:@"event" withItems:@[@"testing"]];
|
||||||
|
}];
|
||||||
|
|
||||||
|
[self.socket connect];
|
||||||
|
[self.socket2 connect];
|
||||||
|
|
||||||
|
[self.manager fakeConnecting];
|
||||||
|
[self.manager fakeConnectingToNamespace:@"/swift"];
|
||||||
|
|
||||||
|
[self waitForExpectationsWithTimeout:0.3 handler:nil];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)setUpSockets {
|
||||||
|
self.socket = [self.manager testSocketForNamespace:@"/"];
|
||||||
|
self.socket2 = [self.manager testSocketForNamespace:@"/swift"];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)setUp {
|
||||||
|
[super setUp];
|
||||||
|
NSURL* url = [[NSURL alloc] initWithString:@"http://localhost"];
|
||||||
|
self.manager = [[TestManager alloc] initWithSocketURL:url config:@{@"log": @NO}];
|
||||||
|
self.socket = nil;
|
||||||
|
self.socket2 = nil;
|
||||||
|
}
|
||||||
|
|
||||||
|
@end
|
||||||
16
Tests/TestSocketIOObjc/SocketObjectiveCTest.h
Normal file
16
Tests/TestSocketIOObjc/SocketObjectiveCTest.h
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
//
|
||||||
|
// Created by Erik Little on 10/21/17.
|
||||||
|
//
|
||||||
|
|
||||||
|
|
||||||
|
@import Dispatch;
|
||||||
|
@import Foundation;
|
||||||
|
@import XCTest;
|
||||||
|
@import SocketIO;
|
||||||
|
|
||||||
|
@interface SocketObjectiveCTest : XCTestCase
|
||||||
|
|
||||||
|
@property SocketIOClient* socket;
|
||||||
|
@property SocketManager* manager;
|
||||||
|
|
||||||
|
@end
|
||||||
@ -7,36 +7,20 @@
|
|||||||
// Merely tests whether the Objective-C api breaks
|
// Merely tests whether the Objective-C api breaks
|
||||||
//
|
//
|
||||||
|
|
||||||
|
#import "SocketObjectiveCTest.h"
|
||||||
|
|
||||||
@import Dispatch;
|
@import Dispatch;
|
||||||
@import Foundation;
|
@import Foundation;
|
||||||
@import XCTest;
|
@import XCTest;
|
||||||
@import SocketIO;
|
@import SocketIO;
|
||||||
|
|
||||||
@interface SocketObjectiveCTest : XCTestCase
|
// TODO Manager interface tests
|
||||||
|
|
||||||
@property SocketIOClient* socket;
|
|
||||||
|
|
||||||
@end
|
|
||||||
|
|
||||||
@implementation SocketObjectiveCTest
|
@implementation SocketObjectiveCTest
|
||||||
|
|
||||||
- (void)setUp {
|
|
||||||
[super setUp];
|
|
||||||
NSURL* url = [[NSURL alloc] initWithString:@"http://localhost"];
|
|
||||||
self.socket = [[SocketIOClient alloc] initWithSocketURL:url config:@{@"log": @NO, @"forcePolling": @YES}];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)testProperties {
|
- (void)testProperties {
|
||||||
NSURL* url = nil;
|
XCTAssertTrue([self.socket.nsp isEqualToString:@"/"]);
|
||||||
|
XCTAssertEqual(self.socket.status, SocketIOStatusNotConnected);
|
||||||
url = self.socket.socketURL;
|
|
||||||
self.socket.forceNew = false;
|
|
||||||
self.socket.handleQueue = dispatch_get_main_queue();
|
|
||||||
self.socket.nsp = @"/objective-c";
|
|
||||||
self.socket.reconnects = false;
|
|
||||||
self.socket.reconnectWait = 1;
|
|
||||||
if (self.socket.status == SocketIOClientStatusConnected) { }
|
|
||||||
if (self.socket.engine == NULL) { }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)testOnSyntax {
|
- (void)testOnSyntax {
|
||||||
@ -62,7 +46,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
- (void)testJoinNamespaceSyntax {
|
- (void)testJoinNamespaceSyntax {
|
||||||
[self.socket joinNamespace:@"/objective-c"];
|
[self.socket joinNamespace];
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)testOnAnySyntax {
|
- (void)testOnAnySyntax {
|
||||||
@ -74,10 +58,6 @@
|
|||||||
}];
|
}];
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)testReconnectSyntax {
|
|
||||||
[self.socket reconnect];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)testRemoveAllHandlersSyntax {
|
- (void)testRemoveAllHandlersSyntax {
|
||||||
[self.socket removeAllHandlers];
|
[self.socket removeAllHandlers];
|
||||||
}
|
}
|
||||||
@ -94,15 +74,16 @@
|
|||||||
[self.socket off:@"test"];
|
[self.socket off:@"test"];
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)testSocketManager {
|
|
||||||
SocketClientManager* manager = [SocketClientManager sharedManager];
|
|
||||||
[manager addSocket:self.socket labeledAs:@"test"];
|
|
||||||
[manager removeSocketWithLabel:@"test"];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)testSSLSecurity {
|
- (void)testSSLSecurity {
|
||||||
SSLSecurity* sec = [[SSLSecurity alloc] initWithUsePublicKeys:0];
|
SSLSecurity* sec = [[SSLSecurity alloc] initWithUsePublicKeys:0];
|
||||||
sec = nil;
|
sec = nil;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
- (void)setUp {
|
||||||
|
[super setUp];
|
||||||
|
NSURL* url = [[NSURL alloc] initWithString:@"http://localhost"];
|
||||||
|
self.manager = [[SocketManager alloc] initWithSocketURL:url config:@{@"log": @NO}];
|
||||||
|
self.socket = [self.manager defaultSocket];
|
||||||
|
}
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
|||||||
109
Usage Docs/12to13.md
Normal file
109
Usage Docs/12to13.md
Normal file
@ -0,0 +1,109 @@
|
|||||||
|
# Upgrading from v12
|
||||||
|
|
||||||
|
This guide will help you navigate the changes that were introduced in v13.
|
||||||
|
|
||||||
|
## What are the big changes?
|
||||||
|
|
||||||
|
The biggest change is how to create and manage clients. Much like the native JS client and server,
|
||||||
|
the swift client now only uses one engine per connection. Previously in order to use namespaces it was required
|
||||||
|
to create multiple clients, and each client had its own engine.
|
||||||
|
|
||||||
|
Some v12 code might've looked like this:
|
||||||
|
|
||||||
|
```swift
|
||||||
|
let defaultSocket = SocketIOClient(socketURL: myURL)
|
||||||
|
let namespaceSocket = SocketIOClient(socketURL: myURL, config: [.nsp("/swift")])
|
||||||
|
|
||||||
|
// add handlers for sockets and connect
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
In v12 this would have opened two connections to the socket.io.
|
||||||
|
|
||||||
|
|
||||||
|
In v13 the same code would look like this:
|
||||||
|
|
||||||
|
```swift
|
||||||
|
let manager = SocketManager(socketURL: myURL)
|
||||||
|
let defaultSocket = manager.defaultSocket
|
||||||
|
let namespaceSocket = manager.socket(forNamespace: "/swift")
|
||||||
|
|
||||||
|
// add handlers for sockets and connect
|
||||||
|
```
|
||||||
|
|
||||||
|
In v13 `defaultSocket` and `namespaceSocket` will share a single transport. This means one less connection to the server
|
||||||
|
needs to be opened.
|
||||||
|
|
||||||
|
## What might I have to change?
|
||||||
|
|
||||||
|
- The most obvious thing you will need to change is that instead of creating `SocketIOClient`s directly, you will create a
|
||||||
|
`SocketManager` and either use the `defaultSocket` property if you don't need namespaces, or call the
|
||||||
|
`socket(forNamespace:)` method on the manager.
|
||||||
|
|
||||||
|
- `SocketIOClient` is no longer a client to an engine. So if you were overriding the engine methods, these have been moved
|
||||||
|
to the manager.
|
||||||
|
|
||||||
|
- The library is now a single target. So you might have to change some of your Xcode project settings.
|
||||||
|
|
||||||
|
- `SocketIOClient`s no longer take a configuration, they are shared from the manager.
|
||||||
|
|
||||||
|
- The `joinNamespace()` and `leaveNamespace()` methods on `SocketIOClient` no longer take any arguments, and in most cases
|
||||||
|
no longer need to be called. Namespace joining/leaving can be managed by calling `connect()`/`disconnect()` on the socket
|
||||||
|
associated with that namespace.
|
||||||
|
|
||||||
|
----------
|
||||||
|
|
||||||
|
# What things should I know?
|
||||||
|
|
||||||
|
How sockets are stored
|
||||||
|
---
|
||||||
|
|
||||||
|
You should know that `SocketIOClient`s no longer need to be held around in properties, but the `SocketManager` should.
|
||||||
|
|
||||||
|
One of the most common mistakes people made is not maintaining a strong reference to the client.
|
||||||
|
|
||||||
|
```swift
|
||||||
|
class Manager {
|
||||||
|
func addHandlers() {
|
||||||
|
let socket = SocketIOClient(socketURL: myURL, config: [.nsp("/swift")])
|
||||||
|
|
||||||
|
// Add handlers
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
This would have resulted in the client being released and no handlers being called.
|
||||||
|
|
||||||
|
A *correct* equivalent would be:
|
||||||
|
|
||||||
|
```swift
|
||||||
|
class Manager {
|
||||||
|
let socketManager = SocketManager(socketURL: someURL)
|
||||||
|
|
||||||
|
func addHandlers() {
|
||||||
|
let socket = socketManager.socket(forNamespace: "/swift")
|
||||||
|
|
||||||
|
// Add handlers
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
This code is fine because the `SocketManager` will maintain a strong reference to the socket.
|
||||||
|
|
||||||
|
It's also worth noting that subsequent calls to `socket(forNamespace:)` will return the *same* socket instance as the
|
||||||
|
first call. So you don't need to hold onto the socket directly just to access it again, just call `socket(forNamespace:)`
|
||||||
|
on the manager to get it. **This does mean that if you need multiple sockets on the same namespace, you will have to use
|
||||||
|
multiple managers.**
|
||||||
|
|
||||||
|
What to call connect on
|
||||||
|
---
|
||||||
|
|
||||||
|
Connect can either be called on the manager directly, or on one of the sockets made from it. In either case, if the manager
|
||||||
|
was not already connected to the server, a connection will be made. Also in both cases the default socket (namespace "/")
|
||||||
|
will fire a `connect` event.
|
||||||
|
|
||||||
|
The difference is that if `connect()` is just called on the manager, then any sockets for that manager that are not the default
|
||||||
|
socket will not automatically connect. `connect()` will need to be called individually for each socket. However, if `connect()`
|
||||||
|
is called on a client, then in addition to opening the connection if needed, the client will connect to the its namespace,
|
||||||
|
and a `connect` event fired.
|
||||||
|
|
||||||
@ -13,11 +13,11 @@ One of the most common reasons your event might not be called is if the client i
|
|||||||
Take this code for example:
|
Take this code for example:
|
||||||
|
|
||||||
```swift
|
```swift
|
||||||
class SocketManager {
|
class Manager {
|
||||||
func addHandlers() {
|
func addHandlers() {
|
||||||
let socket = SocketIOClient(socketURL: URL(string: "http://somesocketioserver.com")!)
|
let manager = SocketManager(socketURL: URL(string: "http://somesocketioserver.com")!)
|
||||||
|
|
||||||
socket.on("myEvent") {data, ack in
|
manager.defaultSocket.on("myEvent") {data, ack in
|
||||||
print(data)
|
print(data)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -25,30 +25,20 @@ class SocketManager {
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
This code is **incorrect**, and the event handler will never be called. Because as soon as this method is called `socket`
|
This code is **incorrect**, and the event handler will never be called. Because as soon as this method is called `manager`
|
||||||
will be released and its memory reclaimed.
|
will be released, along with the socket, and its memory reclaimed.
|
||||||
|
|
||||||
A correct way would be:
|
A correct way would be:
|
||||||
|
|
||||||
```swift
|
```swift
|
||||||
class SocketManager {
|
class Manager {
|
||||||
let socket = SocketIOClient(socketURL: URL(string: "http://somesocketioserver.com")!)
|
let manager = SocketManager(socketURL: URL(string: "http://somesocketioserver.com")!)
|
||||||
|
|
||||||
func addHandlers() {
|
func addHandlers() {
|
||||||
socket.on("myEvent") {data, ack in
|
manager.defaultSocket.on("myEvent") {data, ack in
|
||||||
print(data)
|
print(data)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|
||||||
------
|
|
||||||
|
|
||||||
Another case where this might happen is if you use namespaces in your socket.io application.
|
|
||||||
|
|
||||||
In the JavaScript client a url that looks like `http://somesocketioserver.com/client` would be done with the `nsp` config.
|
|
||||||
|
|
||||||
```swift
|
|
||||||
let socket = SocketIOClient(socketURL: URL(string: "http://somesocketioserver.com")!, config: [.nsp("/client")])
|
|
||||||
```
|
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user