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.

353 lines
13 KiB

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