Skip to content

Commit 5ced8a5

Browse files
authored
Merge pull request #399 from socketio/development
Development
2 parents 4fa3225 + 6fd3294 commit 5ced8a5

15 files changed

+475
-393
lines changed

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -165,6 +165,7 @@ case ReconnectAttempts(Int) // How many times to reconnect. Default is `-1` (inf
165165
case ReconnectWait(Int) // Amount of time to wait between reconnects. Default is `10`
166166
case SessionDelegate(NSURLSessionDelegate) // Sets an NSURLSessionDelegate for the underlying engine. Useful if you need to handle self-signed certs. Default is nil.
167167
case Secure(Bool) // If the connection should use TLS. Default is false.
168+
case Security(SSLSecurity) // Allows you to set which certs are valid. Useful for SSL pinning.
168169
case SelfSigned(Bool) // Sets WebSocket.selfSignedSSL (Don't do this, iOS will yell at you)
169170
case VoipEnabled(Bool) // Only use this option if you're using the client with VoIP services. Changes the way the WebSocket is created. Default is false
170171
```

Socket.IO-Client-Swift.xcodeproj/project.pbxproj

Lines changed: 33 additions & 31 deletions
Large diffs are not rendered by default.

SocketIO-MacTests/SocketObjectiveCTest.m

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,4 +44,10 @@ - (void)testOffSyntax {
4444
[self.socket off:@"test"];
4545
}
4646

47+
- (void)testSocketManager {
48+
SocketClientManager* manager = [SocketClientManager sharedManager];
49+
[manager addSocket:self.socket labeledAs:@"test"];
50+
[manager removeSocketWithLabel:@"test"];
51+
}
52+
4753
@end

SocketIO-MacTests/SocketParserTest.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ class SocketParserTest: XCTestCase {
1313
let testSocket = SocketIOClient(socketURL: NSURL())
1414

1515
//Format key: message; namespace-data-binary-id
16-
static let packetTypes: Dictionary<String, (String, [AnyObject], [NSData], Int)> = [
16+
static let packetTypes: [String: (String, [AnyObject], [NSData], Int)] = [
1717
"0": ("/", [], [], -1), "1": ("/", [], [], -1),
1818
"25[\"test\"]": ("/", ["test"], [], 5),
1919
"2[\"test\",\"~~0\"]": ("/", ["test", "~~0"], [], -1),

SocketIO-MacTests/SocketSideEffectTest.swift

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -153,4 +153,16 @@ class SocketSideEffectTest: XCTestCase {
153153
socket.parseBinaryData(data2)
154154
waitForExpectationsWithTimeout(3, handler: nil)
155155
}
156+
157+
func testSocketManager() {
158+
let manager = SocketClientManager.sharedManager
159+
manager["test"] = socket
160+
161+
XCTAssert(manager["test"] === socket, "failed to get socket")
162+
163+
manager["test"] = nil
164+
165+
XCTAssert(manager["test"] == nil, "socket not removed")
166+
167+
}
156168
}

SocketIO-iOSTests/Info.plist

Lines changed: 0 additions & 24 deletions
This file was deleted.

Source/SocketClientManager.swift

Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
//
2+
// SocketClientManager.swift
3+
// Socket.IO-Client-Swift
4+
//
5+
// Created by Erik Little on 6/11/16.
6+
//
7+
// Permission is hereby granted, free of charge, to any person obtaining a copy
8+
// of this software and associated documentation files (the "Software"), to deal
9+
// in the Software without restriction, including without limitation the rights
10+
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11+
// copies of the Software, and to permit persons to whom the Software is
12+
// furnished to do so, subject to the following conditions:
13+
//
14+
// The above copyright notice and this permission notice shall be included in
15+
// all copies or substantial portions of the Software.
16+
//
17+
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18+
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19+
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20+
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21+
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22+
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
23+
// THE SOFTWARE.
24+
25+
import Foundation
26+
27+
/**
28+
Experimental socket manager.
29+
30+
API subject to change.
31+
32+
Can be used to persist sockets across ViewControllers.
33+
34+
Sockets are strongly stored, so be sure to remove them once they are no
35+
longer needed.
36+
37+
Example usage:
38+
```
39+
let manager = SocketClientManager.sharedManager
40+
manager["room1"] = socket1
41+
manager["room2"] = socket2
42+
manager.removeSocket(socket: socket2)
43+
manager["room1"]?.emit("hello")
44+
```
45+
*/
46+
public final class SocketClientManager : NSObject {
47+
public static let sharedManager = SocketClientManager()
48+
49+
private var sockets = [String: SocketIOClient]()
50+
51+
public subscript(string: String) -> SocketIOClient? {
52+
get {
53+
return sockets[string]
54+
}
55+
56+
set(socket) {
57+
sockets[string] = socket
58+
}
59+
}
60+
61+
public func addSocket(socket: SocketIOClient, labeledAs label: String) {
62+
sockets[label] = socket
63+
}
64+
65+
public func removeSocket(withLabel label: String) -> SocketIOClient? {
66+
return sockets.removeValueForKey(label)
67+
}
68+
69+
public func removeSocket(socket socket: SocketIOClient) -> SocketIOClient? {
70+
var returnSocket: SocketIOClient?
71+
72+
for (label, dictSocket) in sockets where dictSocket === socket {
73+
returnSocket = sockets.removeValueForKey(label)
74+
}
75+
76+
return returnSocket
77+
}
78+
79+
public func removeSockets() {
80+
sockets.removeAll()
81+
}
82+
}

Source/SocketEngine.swift

Lines changed: 36 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,7 @@ public final class SocketEngine : NSObject, SocketEnginePollable, SocketEngineWe
7676
private var pongsMissedMax = 0
7777
private var probeWait = ProbeWaitQueue()
7878
private var secure = false
79+
private var security: SSLSecurity?
7980
private var selfSigned = false
8081
private var voipEnabled = false
8182

@@ -105,6 +106,8 @@ public final class SocketEngine : NSObject, SocketEnginePollable, SocketEngineWe
105106
voipEnabled = enable
106107
case let .Secure(secure):
107108
self.secure = secure
109+
case let .Security(security):
110+
self.security = security
108111
case let .SelfSigned(selfSigned):
109112
self.selfSigned = selfSigned
110113
default:
@@ -153,9 +156,8 @@ public final class SocketEngine : NSObject, SocketEnginePollable, SocketEngineWe
153156
// binary in base64 string
154157
let noPrefix = message[message.startIndex.advancedBy(2)..<message.endIndex]
155158

156-
if let data = NSData(base64EncodedString: noPrefix,
157-
options: .IgnoreUnknownCharacters) {
158-
client?.parseEngineBinaryData(data)
159+
if let data = NSData(base64EncodedString: noPrefix, options: .IgnoreUnknownCharacters) {
160+
client?.parseEngineBinaryData(data)
159161
}
160162

161163
return true
@@ -164,14 +166,24 @@ public final class SocketEngine : NSObject, SocketEnginePollable, SocketEngineWe
164166
}
165167
}
166168

169+
private func closeOutEngine() {
170+
sid = ""
171+
closed = true
172+
invalidated = true
173+
connected = false
174+
175+
ws?.disconnect()
176+
stopPolling()
177+
}
178+
167179
/// Starts the connection to the server
168180
public func connect() {
169181
if connected {
170182
DefaultSocketLogger.Logger.error("Engine tried opening while connected. Assuming this was a reconnect", type: logType)
171183
disconnect("reconnect")
172184
}
173185

174-
DefaultSocketLogger.Logger.log("Starting engine", type: logType)
186+
DefaultSocketLogger.Logger.log("Starting engine. Server: %@", type: logType, args: url)
175187
DefaultSocketLogger.Logger.log("Handshaking", type: logType)
176188

177189
resetEngine()
@@ -256,6 +268,7 @@ public final class SocketEngine : NSObject, SocketEnginePollable, SocketEngineWe
256268
ws?.voipEnabled = voipEnabled
257269
ws?.delegate = self
258270
ws?.selfSignedSSL = selfSigned
271+
ws?.security = security
259272

260273
ws?.connect()
261274
}
@@ -267,35 +280,32 @@ public final class SocketEngine : NSObject, SocketEnginePollable, SocketEngineWe
267280
}
268281

269282
public func disconnect(reason: String) {
270-
func postSendClose(data: NSData?, _ res: NSURLResponse?, _ err: NSError?) {
271-
sid = ""
272-
closed = true
273-
invalidated = true
274-
connected = false
275-
276-
ws?.disconnect()
277-
stopPolling()
278-
client?.engineDidClose(reason)
279-
}
283+
guard connected else { return closeOutEngine() }
280284

281285
DefaultSocketLogger.Logger.log("Engine is being closed.", type: logType)
282286

283287
if closed {
288+
closeOutEngine()
284289
client?.engineDidClose(reason)
285290
return
286291
}
287292

288293
if websocket {
289294
sendWebSocketMessage("", withType: .Close, withData: [])
290-
postSendClose(nil, nil, nil)
295+
closeOutEngine()
291296
} else {
292-
// We need to take special care when we're polling that we send it ASAP
293-
// Also make sure we're on the emitQueue since we're touching postWait
294-
dispatch_sync(emitQueue) {
295-
self.postWait.append(String(SocketEnginePacketType.Close.rawValue))
296-
let req = self.createRequestForPostWithPostWait()
297-
self.doRequest(req, withCallback: postSendClose)
298-
}
297+
disconnectPolling()
298+
}
299+
}
300+
301+
// We need to take special care when we're polling that we send it ASAP
302+
// Also make sure we're on the emitQueue since we're touching postWait
303+
private func disconnectPolling() {
304+
dispatch_sync(emitQueue) {
305+
self.postWait.append(String(SocketEnginePacketType.Close.rawValue))
306+
let req = self.createRequestForPostWithPostWait()
307+
self.doRequest(req) {_, _, _ in }
308+
self.closeOutEngine()
299309
}
300310
}
301311

@@ -335,7 +345,7 @@ public final class SocketEngine : NSObject, SocketEnginePollable, SocketEngineWe
335345
guard let ws = self.ws else { return }
336346

337347
for msg in postWait {
338-
ws.writeString(fixDoubleUTF8(msg))
348+
ws.writeString(msg)
339349
}
340350

341351
postWait.removeAll(keepCapacity: true)
@@ -535,13 +545,11 @@ public final class SocketEngine : NSObject, SocketEnginePollable, SocketEngineWe
535545
connected = false
536546
websocket = false
537547

538-
let reason = error?.localizedDescription ?? "Socket Disconnected"
539-
540-
if error != nil {
548+
if let reason = error?.localizedDescription {
541549
didError(reason)
550+
} else {
551+
client?.engineDidClose("Socket Disconnected")
542552
}
543-
544-
client?.engineDidClose(reason)
545553
} else {
546554
flushProbeWait()
547555
}

Source/SocketEnginePollable.swift

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -123,7 +123,7 @@ extension SocketEnginePollable {
123123

124124
return
125125
}
126-
126+
127127
DefaultSocketLogger.Logger.log("Got polling response", type: "SocketEnginePolling")
128128

129129
if let str = String(data: data!, encoding: NSUTF8StringEncoding) {
@@ -213,9 +213,7 @@ extension SocketEnginePollable {
213213
fixedMessage = message
214214
}
215215

216-
let strMsg = "\(type.rawValue)\(fixedMessage)"
217-
218-
postWait.append(strMsg)
216+
postWait.append(String(type.rawValue) + fixedMessage)
219217

220218
for data in datas {
221219
if case let .Right(bin) = createBinaryDataForSend(data) {

Source/SocketIOClient.swift

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -197,8 +197,7 @@ public final class SocketIOClient : NSObject, SocketEngineClient, SocketParsable
197197
handleEvent("disconnect", data: [reason], isInternalMessage: true)
198198
}
199199

200-
/// Disconnects the socket. Only reconnect the same socket if you know what you're doing.
201-
/// Will turn off automatic reconnects.
200+
/// Disconnects the socket.
202201
public func disconnect() {
203202
assert(status != .NotConnected, "Tried closing a NotConnected client")
204203

0 commit comments

Comments
 (0)