From dd2167a09f8b2e1dc125ad9f6a07bbe7c578c588 Mon Sep 17 00:00:00 2001 From: Erik Date: Sun, 7 May 2017 14:40:39 -0400 Subject: [PATCH] Report errors in SocketData to users. Resolves #677 --- SocketIO-MacTests/SocketSideEffectTest.swift | 53 ++++++++++++++++++++ Source/SocketAckEmitter.swift | 2 +- Source/SocketIOClient.swift | 22 ++++++-- 3 files changed, 72 insertions(+), 5 deletions(-) diff --git a/SocketIO-MacTests/SocketSideEffectTest.swift b/SocketIO-MacTests/SocketSideEffectTest.swift index 8969a4a..d1edcb4 100644 --- a/SocketIO-MacTests/SocketSideEffectTest.swift +++ b/SocketIO-MacTests/SocketSideEffectTest.swift @@ -254,6 +254,48 @@ class SocketSideEffectTest: XCTestCase { waitForExpectations(timeout: 2) } + func testErrorInCustomSocketDataCallsErrorHandler() { + let expect = expectation(description: "The client should call the error handler for emit errors because of " + + "custom data") + + socket.on(clientEvent: .error) {data, ack in + guard data.count == 3, data[0] as? String == "myEvent", + data[2] is ThrowingData.ThrowingError else { + XCTFail("Incorrect error call") + + return + } + + expect.fulfill() + } + + socket.emit("myEvent", ThrowingData()) + + waitForExpectations(timeout: 0.2) + } + + func testErrorInCustomSocketDataCallsErrorHandler_ack() { + let expect = expectation(description: "The client should call the error handler for emit errors because of " + + "custom data") + + socket.on(clientEvent: .error) {data, ack in + guard data.count == 3, data[0] as? String == "myEvent", + data[2] is ThrowingData.ThrowingError else { + XCTFail("Incorrect error call") + + return + } + + expect.fulfill() + } + + socket.emitWithAck("myEvent", ThrowingData()).timingOut(after: 1, callback: {_ in + XCTFail("Ack callback should not be called") + }) + + waitForExpectations(timeout: 0.2) + } + let data = "test".data(using: String.Encoding.utf8)! let data2 = "test2".data(using: String.Encoding.utf8)! private var socket: SocketIOClient! @@ -264,3 +306,14 @@ class SocketSideEffectTest: XCTestCase { socket.setTestable() } } + +struct ThrowingData : SocketData { + enum ThrowingError : Error { + case error + } + + func socketRepresentation() throws -> SocketData { + throw ThrowingError.error + } + +} diff --git a/Source/SocketAckEmitter.swift b/Source/SocketAckEmitter.swift index 6c5fe15..6ec5e5f 100644 --- a/Source/SocketAckEmitter.swift +++ b/Source/SocketAckEmitter.swift @@ -98,7 +98,7 @@ public final class OnAckCallback : NSObject { /// - parameter callback: The callback called when an ack is received, or when a timeout happens. /// To check for timeout, use `SocketAckStatus`'s `noAck` case. public func timingOut(after seconds: Int, callback: @escaping AckCallback) { - guard let socket = self.socket else { return } + guard let socket = self.socket, ackNumber != -1 else { return } socket.ackHandlers.addAck(ackNumber, callback: callback) socket._emit(items, ack: ackNumber) diff --git a/Source/SocketIOClient.swift b/Source/SocketIOClient.swift index 3896ba7..8cb0bde 100644 --- a/Source/SocketIOClient.swift +++ b/Source/SocketIOClient.swift @@ -230,13 +230,19 @@ open class SocketIOClient : NSObject, SocketIOClientSpec, SocketEngineClient, So /// Send an event to the server, with optional data items. /// + /// If an error occurs trying to transform `items` into their socket representation, a `SocketClientEvent.error` + /// will be emitted. The structure of the error data is `[eventName, items, theError]` + /// /// - parameter event: The event to send. /// - parameter items: The items to send with this event. May be left out. open func emit(_ event: String, _ items: SocketData...) { do { emit(event, with: try items.map({ try $0.socketRepresentation() })) - } catch { - fatalError("Error creating socketRepresentation for emit: \(event), \(items)") + } catch let err { + DefaultSocketLogger.Logger.error("Error creating socketRepresentation for emit: \(event), \(items)", + type: logType) + + handleClientEvent(.error, data: [event, items, err]) } } @@ -258,6 +264,9 @@ open class SocketIOClient : NSObject, SocketIOClientSpec, SocketEngineClient, So /// **NOTE**: It is up to the server send an ack back, just calling this method does not mean the server will ack. /// Check that your server's api will ack the event being sent. /// + /// If an error occurs trying to transform `items` into their socket representation, a `SocketClientEvent.error` + /// will be emitted. The structure of the error data is `[eventName, items, theError]` + /// /// Example: /// /// ```swift @@ -272,8 +281,13 @@ open class SocketIOClient : NSObject, SocketIOClientSpec, SocketEngineClient, So open func emitWithAck(_ event: String, _ items: SocketData...) -> OnAckCallback { do { return emitWithAck(event, with: try items.map({ try $0.socketRepresentation() })) - } catch { - fatalError("Error creating socketRepresentation for emit: \(event), \(items)") + } catch let err { + DefaultSocketLogger.Logger.error("Error creating socketRepresentation for emit: \(event), \(items)", + type: logType) + + handleClientEvent(.error, data: [event, items, err]) + + return OnAckCallback(ackNumber: -1, items: [], socket: self) } }