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.

146 lines
5.0 KiB

2 years ago
  1. //
  2. // SocketAckEmitter.swift
  3. // Socket.IO-Client-Swift
  4. //
  5. // Created by Erik Little on 9/16/15.
  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. /// A class that represents a waiting ack call.
  27. ///
  28. /// **NOTE**: You should not store this beyond the life of the event handler.
  29. public final class SocketAckEmitter : NSObject {
  30. private unowned let socket: SocketIOClient
  31. private let ackNum: Int
  32. /// A view into this emitter where emits do not check for binary data.
  33. ///
  34. /// Usage:
  35. ///
  36. /// ```swift
  37. /// ack.rawEmitView.with(myObject)
  38. /// ```
  39. ///
  40. /// **NOTE**: It is not safe to hold on to this view beyond the life of the socket.
  41. @objc
  42. public private(set) lazy var rawEmitView = SocketRawAckView(socket: socket, ackNum: ackNum)
  43. // MARK: Properties
  44. /// If true, this handler is expecting to be acked. Call `with(_: SocketData...)` to ack.
  45. public var expected: Bool {
  46. return ackNum != -1
  47. }
  48. // MARK: Initializers
  49. /// Creates a new `SocketAckEmitter`.
  50. ///
  51. /// - parameter socket: The socket for this emitter.
  52. /// - parameter ackNum: The ack number for this emitter.
  53. public init(socket: SocketIOClient, ackNum: Int) {
  54. self.socket = socket
  55. self.ackNum = ackNum
  56. }
  57. // MARK: Methods
  58. /// Call to ack receiving this event.
  59. ///
  60. /// If an error occurs trying to transform `items` into their socket representation, a `SocketClientEvent.error`
  61. /// will be emitted. The structure of the error data is `[ackNum, items, theError]`
  62. ///
  63. /// - parameter items: A variable number of items to send when acking.
  64. public func with(_ items: SocketData...) {
  65. guard ackNum != -1 else { return }
  66. do {
  67. socket.emitAck(ackNum, with: try items.map({ try $0.socketRepresentation() }))
  68. } catch {
  69. socket.handleClientEvent(.error, data: [ackNum, items, error])
  70. }
  71. }
  72. /// Call to ack receiving this event.
  73. ///
  74. /// - parameter items: An array of items to send when acking. Use `[]` to send nothing.
  75. @objc
  76. public func with(_ items: [Any]) {
  77. guard ackNum != -1 else { return }
  78. socket.emitAck(ackNum, with: items)
  79. }
  80. }
  81. /// A class that represents an emit that will request an ack that has not yet been sent.
  82. /// Call `timingOut(after:callback:)` to complete the emit
  83. /// Example:
  84. ///
  85. /// ```swift
  86. /// socket.emitWithAck("myEvent").timingOut(after: 1) {data in
  87. /// ...
  88. /// }
  89. /// ```
  90. public final class OnAckCallback : NSObject {
  91. private let ackNumber: Int
  92. private let binary: Bool
  93. private let items: [Any]
  94. private weak var socket: SocketIOClient?
  95. init(ackNumber: Int, items: [Any], socket: SocketIOClient, binary: Bool = true) {
  96. self.ackNumber = ackNumber
  97. self.items = items
  98. self.socket = socket
  99. self.binary = binary
  100. }
  101. /// :nodoc:
  102. deinit {
  103. DefaultSocketLogger.Logger.log("OnAckCallback for \(ackNumber) being released", type: "OnAckCallback")
  104. }
  105. // MARK: Methods
  106. /// Completes an emitWithAck. If this isn't called, the emit never happens.
  107. ///
  108. /// - parameter seconds: The number of seconds before this emit times out if an ack hasn't been received.
  109. /// - parameter callback: The callback called when an ack is received, or when a timeout happens.
  110. /// To check for timeout, use `SocketAckStatus`'s `noAck` case.
  111. @objc
  112. public func timingOut(after seconds: Double, callback: @escaping AckCallback) {
  113. guard let socket = self.socket, ackNumber != -1 else { return }
  114. socket.ackHandlers.addAck(ackNumber, callback: callback)
  115. socket.emit(items, ack: ackNumber, binary: binary)
  116. guard seconds != 0 else { return }
  117. socket.manager?.handleQueue.asyncAfter(deadline: DispatchTime.now() + seconds) {[weak socket] in
  118. guard let socket = socket else { return }
  119. socket.ackHandlers.timeoutAck(self.ackNumber)
  120. }
  121. }
  122. }