You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

392 lines
15 KiB

2 years ago
  1. //
  2. // SocketIOClientSpec.swift
  3. // Socket.IO-Client-Swift
  4. //
  5. // Created by Erik Little on 1/3/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. import Dispatch
  25. import Foundation
  26. /// Defines the interface for a SocketIOClient.
  27. public protocol SocketIOClientSpec : AnyObject {
  28. // MARK: Properties
  29. /// A handler that will be called on any event.
  30. var anyHandler: ((SocketAnyEvent) -> ())? { get }
  31. /// The array of handlers for this socket.
  32. var handlers: [SocketEventHandler] { get }
  33. /// The manager for this socket.
  34. var manager: SocketManagerSpec? { get }
  35. /// The namespace that this socket is currently connected to.
  36. ///
  37. /// **Must** start with a `/`.
  38. var nsp: String { get }
  39. /// A view into this socket where emits do not check for binary data.
  40. ///
  41. /// Usage:
  42. ///
  43. /// ```swift
  44. /// socket.rawEmitView.emit("myEvent", myObject)
  45. /// ```
  46. ///
  47. /// **NOTE**: It is not safe to hold on to this view beyond the life of the socket.
  48. var rawEmitView: SocketRawView { get }
  49. /// The id of this socket.io connect. This is different from the sid of the engine.io connection.
  50. var sid: String? { get }
  51. /// The status of this client.
  52. var status: SocketIOStatus { get }
  53. // MARK: Methods
  54. /// Connect to the server. The same as calling `connect(timeoutAfter:withHandler:)` with a timeout of 0.
  55. ///
  56. /// Only call after adding your event listeners, unless you know what you're doing.
  57. ///
  58. /// - parameter payload: An optional payload sent on connect
  59. func connect(withPayload payload: [String: Any]?)
  60. /// Connect to the server. If we aren't connected after `timeoutAfter` seconds, then `withHandler` is called.
  61. ///
  62. /// Only call after adding your event listeners, unless you know what you're doing.
  63. ///
  64. /// - parameter withPayload: An optional payload sent on connect
  65. /// - parameter timeoutAfter: The number of seconds after which if we are not connected we assume the connection
  66. /// has failed. Pass 0 to never timeout.
  67. /// - parameter handler: The handler to call when the client fails to connect.
  68. func connect(withPayload payload: [String: Any]?, timeoutAfter: Double, withHandler handler: (() -> ())?)
  69. /// Called when the client connects to a namespace. If the client was created with a namespace upfront,
  70. /// then this is only called when the client connects to that namespace.
  71. ///
  72. /// - parameter toNamespace: The namespace that was connected to.
  73. func didConnect(toNamespace namespace: String, payload: [String: Any]?)
  74. /// Called when the client has disconnected from socket.io.
  75. ///
  76. /// - parameter reason: The reason for the disconnection.
  77. func didDisconnect(reason: String)
  78. /// Called when the client encounters an error.
  79. ///
  80. /// - parameter reason: The reason for the disconnection.
  81. func didError(reason: String)
  82. /// Disconnects the socket.
  83. func disconnect()
  84. /// Send an event to the server, with optional data items and optional write completion handler.
  85. ///
  86. /// If an error occurs trying to transform `items` into their socket representation, a `SocketClientEvent.error`
  87. /// will be emitted. The structure of the error data is `[eventName, items, theError]`
  88. ///
  89. /// - parameter event: The event to send.
  90. /// - parameter items: The items to send with this event. May be left out.
  91. /// - parameter completion: Callback called on transport write completion.
  92. func emit(_ event: String, _ items: SocketData..., completion: (() -> ())?)
  93. /// Send an event to the server, with optional data items and optional write completion handler.
  94. ///
  95. /// If an error occurs trying to transform `items` into their socket representation, a `SocketClientEvent.error`
  96. /// will be emitted. The structure of the error data is `[eventName, items, theError]`
  97. ///
  98. /// - parameter event: The event to send.
  99. /// - parameter items: The items to send with this event. May be left out.
  100. /// - parameter completion: Callback called on transport write completion.
  101. func emit(_ event: String, with items: [SocketData], completion: (() -> ())?)
  102. /// Call when you wish to tell the server that you've received the event for `ack`.
  103. ///
  104. /// - parameter ack: The ack number.
  105. /// - parameter with: The data for this ack.
  106. func emitAck(_ ack: Int, with items: [Any])
  107. /// Sends a message to the server, requesting an ack.
  108. ///
  109. /// **NOTE**: It is up to the server send an ack back, just calling this method does not mean the server will ack.
  110. /// Check that your server's api will ack the event being sent.
  111. ///
  112. /// If an error occurs trying to transform `items` into their socket representation, a `SocketClientEvent.error`
  113. /// will be emitted. The structure of the error data is `[eventName, items, theError]`
  114. ///
  115. /// Example:
  116. ///
  117. /// ```swift
  118. /// socket.emitWithAck("myEvent", 1).timingOut(after: 1) {data in
  119. /// ...
  120. /// }
  121. /// ```
  122. ///
  123. /// - parameter event: The event to send.
  124. /// - parameter items: The items to send with this event. May be left out.
  125. /// - returns: An `OnAckCallback`. You must call the `timingOut(after:)` method before the event will be sent.
  126. func emitWithAck(_ event: String, _ items: SocketData...) -> OnAckCallback
  127. /// Sends a message to the server, requesting an ack.
  128. ///
  129. /// **NOTE**: It is up to the server send an ack back, just calling this method does not mean the server will ack.
  130. /// Check that your server's api will ack the event being sent.
  131. ///
  132. /// If an error occurs trying to transform `items` into their socket representation, a `SocketClientEvent.error`
  133. /// will be emitted. The structure of the error data is `[eventName, items, theError]`
  134. ///
  135. /// Example:
  136. ///
  137. /// ```swift
  138. /// socket.emitWithAck("myEvent", 1).timingOut(after: 1) {data in
  139. /// ...
  140. /// }
  141. /// ```
  142. ///
  143. /// - parameter event: The event to send.
  144. /// - parameter items: The items to send with this event. May be left out.
  145. /// - returns: An `OnAckCallback`. You must call the `timingOut(after:)` method before the event will be sent.
  146. func emitWithAck(_ event: String, with items: [SocketData]) -> OnAckCallback
  147. /// Called when socket.io has acked one of our emits. Causes the corresponding ack callback to be called.
  148. ///
  149. /// - parameter ack: The number for this ack.
  150. /// - parameter data: The data sent back with this ack.
  151. func handleAck(_ ack: Int, data: [Any])
  152. /// Called on socket.io specific events.
  153. ///
  154. /// - parameter event: The `SocketClientEvent`.
  155. /// - parameter data: The data for this event.
  156. func handleClientEvent(_ event: SocketClientEvent, data: [Any])
  157. /// Called when we get an event from socket.io.
  158. ///
  159. /// - parameter event: The name of the event.
  160. /// - parameter data: The data that was sent with this event.
  161. /// - parameter isInternalMessage: Whether this event was sent internally. If `true` it is always sent to handlers.
  162. /// - parameter ack: If > 0 then this event expects to get an ack back from the client.
  163. func handleEvent(_ event: String, data: [Any], isInternalMessage: Bool, withAck ack: Int)
  164. /// Causes a client to handle a socket.io packet. The namespace for the packet must match the namespace of the
  165. /// socket.
  166. ///
  167. /// - parameter packet: The packet to handle.
  168. func handlePacket(_ packet: SocketPacket)
  169. /// Call when you wish to leave a namespace and disconnect this socket.
  170. func leaveNamespace()
  171. /// Joins `nsp`. You shouldn't need to call this directly, instead call `connect`.
  172. ///
  173. /// - Parameter withPayload: The payload to connect when joining this namespace
  174. func joinNamespace(withPayload payload: [String: Any]?)
  175. /// Removes handler(s) for a client event.
  176. ///
  177. /// If you wish to remove a client event handler, call the `off(id:)` with the UUID received from its `on` call.
  178. ///
  179. /// - parameter clientEvent: The event to remove handlers for.
  180. func off(clientEvent event: SocketClientEvent)
  181. /// Removes handler(s) based on an event name.
  182. ///
  183. /// If you wish to remove a specific event, call the `off(id:)` with the UUID received from its `on` call.
  184. ///
  185. /// - parameter event: The event to remove handlers for.
  186. func off(_ event: String)
  187. /// Removes a handler with the specified UUID gotten from an `on` or `once`
  188. ///
  189. /// If you want to remove all events for an event, call the off `off(_:)` method with the event name.
  190. ///
  191. /// - parameter id: The UUID of the handler you wish to remove.
  192. func off(id: UUID)
  193. /// Adds a handler for an event.
  194. ///
  195. /// - parameter event: The event name for this handler.
  196. /// - parameter callback: The callback that will execute when this event is received.
  197. /// - returns: A unique id for the handler that can be used to remove it.
  198. func on(_ event: String, callback: @escaping NormalCallback) -> UUID
  199. /// Adds a handler for a client event.
  200. ///
  201. /// Example:
  202. ///
  203. /// ```swift
  204. /// socket.on(clientEvent: .connect) {data, ack in
  205. /// ...
  206. /// }
  207. /// ```
  208. ///
  209. /// - parameter event: The event for this handler.
  210. /// - parameter callback: The callback that will execute when this event is received.
  211. /// - returns: A unique id for the handler that can be used to remove it.
  212. func on(clientEvent event: SocketClientEvent, callback: @escaping NormalCallback) -> UUID
  213. /// Adds a single-use handler for a client event.
  214. ///
  215. /// - parameter clientEvent: The event for this handler.
  216. /// - parameter callback: The callback that will execute when this event is received.
  217. /// - returns: A unique id for the handler that can be used to remove it.
  218. func once(clientEvent event: SocketClientEvent, callback: @escaping NormalCallback) -> UUID
  219. /// Adds a single-use handler for an event.
  220. ///
  221. /// - parameter event: The event name for this handler.
  222. /// - parameter callback: The callback that will execute when this event is received.
  223. /// - returns: A unique id for the handler that can be used to remove it.
  224. func once(_ event: String, callback: @escaping NormalCallback) -> UUID
  225. /// Adds a handler that will be called on every event.
  226. ///
  227. /// - parameter handler: The callback that will execute whenever an event is received.
  228. func onAny(_ handler: @escaping (SocketAnyEvent) -> ())
  229. /// Removes all handlers.
  230. ///
  231. /// Can be used after disconnecting to break any potential remaining retain cycles.
  232. func removeAllHandlers()
  233. /// Puts the socket back into the connecting state.
  234. /// Called when the manager detects a broken connection, or when a manual reconnect is triggered.
  235. ///
  236. /// parameter reason: The reason this socket is going reconnecting.
  237. func setReconnecting(reason: String)
  238. }
  239. public extension SocketIOClientSpec {
  240. /// Default implementation.
  241. func didError(reason: String) {
  242. DefaultSocketLogger.Logger.error("\(reason)", type: "SocketIOClient")
  243. handleClientEvent(.error, data: [reason])
  244. }
  245. }
  246. /// The set of events that are generated by the client.
  247. public enum SocketClientEvent : String {
  248. // MARK: Cases
  249. /// Emitted when the client connects. This is also called on a successful reconnection. A connect event gets one
  250. /// data item: the namespace that was connected to.
  251. ///
  252. /// ```swift
  253. /// socket.on(clientEvent: .connect) {data, ack in
  254. /// guard let nsp = data[0] as? String else { return }
  255. /// // Some logic using the nsp
  256. /// }
  257. /// ```
  258. case connect
  259. /// Emitted when the socket has disconnected and will not attempt to try to reconnect.
  260. ///
  261. /// Usage:
  262. ///
  263. /// ```swift
  264. /// socket.on(clientEvent: .disconnect) {data, ack in
  265. /// // Some cleanup logic
  266. /// }
  267. /// ```
  268. case disconnect
  269. /// Emitted when an error occurs.
  270. ///
  271. /// Usage:
  272. ///
  273. /// ```swift
  274. /// socket.on(clientEvent: .error) {data, ack in
  275. /// // Some logging
  276. /// }
  277. /// ```
  278. case error
  279. /// Emitted whenever the engine sends a ping.
  280. ///
  281. /// Usage:
  282. ///
  283. /// ```swift
  284. /// socket.on(clientEvent: .ping) {_, _ in
  285. /// // Maybe keep track of latency?
  286. /// }
  287. /// ```
  288. case ping
  289. /// Emitted whenever the engine gets a pong.
  290. ///
  291. /// Usage:
  292. ///
  293. /// ```swift
  294. /// socket.on(clientEvent: .pong) {_, _ in
  295. /// // Maybe keep track of latency?
  296. /// }
  297. /// ```
  298. case pong
  299. /// Emitted when the client begins the reconnection process.
  300. ///
  301. /// Usage:
  302. ///
  303. /// ```swift
  304. /// socket.on(clientEvent: .reconnect) {data, ack in
  305. /// // Some reconnect event logic
  306. /// }
  307. /// ```
  308. case reconnect
  309. /// Emitted each time the client tries to reconnect to the server.
  310. ///
  311. /// Usage:
  312. ///
  313. /// ```swift
  314. /// socket.on(clientEvent: .reconnectAttempt) {data, ack in
  315. /// // Some reconnect attempt logging
  316. /// }
  317. /// ```
  318. case reconnectAttempt
  319. /// Emitted every time there is a change in the client's status.
  320. ///
  321. /// The payload for data is [SocketIOClientStatus, Int]. Where the second item is the raw value. Use the second one
  322. /// if you are working in Objective-C.
  323. ///
  324. /// Usage:
  325. ///
  326. /// ```swift
  327. /// socket.on(clientEvent: .statusChange) {data, ack in
  328. /// // Some status changing logging
  329. /// }
  330. /// ```
  331. case statusChange
  332. /// Emitted when when upgrading the http connection to a websocket connection.
  333. ///
  334. /// Usage:
  335. ///
  336. /// ```swift
  337. /// socket.on(clientEvent: .websocketUpgrade) {data, ack in
  338. /// let headers = (data as [Any])[0]
  339. /// // Some header logic
  340. /// }
  341. /// ```
  342. case websocketUpgrade
  343. }