websocket and user center
This commit is contained in:
@@ -0,0 +1,48 @@
|
||||
//
|
||||
// SocketAnyEvent.swift
|
||||
// Socket.IO-Client-Swift
|
||||
//
|
||||
// Created by Erik Little on 3/28/15.
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
// THE SOFTWARE.
|
||||
|
||||
import Foundation
|
||||
|
||||
/// Represents some event that was received.
|
||||
public final class SocketAnyEvent : NSObject {
|
||||
// MARK: Properties
|
||||
|
||||
/// The event name.
|
||||
@objc
|
||||
public let event: String
|
||||
|
||||
/// The data items for this event.
|
||||
@objc
|
||||
public let items: [Any]?
|
||||
|
||||
/// The description of this event.
|
||||
override public var description: String {
|
||||
return "SocketAnyEvent: Event: \(event) items: \(String(describing: items))"
|
||||
}
|
||||
|
||||
init(event: String, items: [Any]?) {
|
||||
self.event = event
|
||||
self.items = items
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
//
|
||||
// EventHandler.swift
|
||||
// Socket.IO-Client-Swift
|
||||
//
|
||||
// Created by Erik Little on 1/18/15.
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
// THE SOFTWARE.
|
||||
|
||||
import Foundation
|
||||
|
||||
/// A wrapper around a handler.
|
||||
public struct SocketEventHandler {
|
||||
// MARK: Properties
|
||||
|
||||
/// The event for this handler.
|
||||
public let event: String
|
||||
|
||||
/// A unique identifier for this handler.
|
||||
public let id: UUID
|
||||
|
||||
/// The actual handler function.
|
||||
public let callback: NormalCallback
|
||||
|
||||
// MARK: Methods
|
||||
|
||||
/// Causes this handler to be executed.
|
||||
///
|
||||
/// - parameter with: The data that this handler should be called with.
|
||||
/// - parameter withAck: The ack number that this event expects. Pass -1 to say this event doesn't expect an ack.
|
||||
/// - parameter withSocket: The socket that is calling this event.
|
||||
public func executeCallback(with items: [Any], withAck ack: Int, withSocket socket: SocketIOClient) {
|
||||
callback(items, SocketAckEmitter(socket: socket, ackNum: ack))
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,535 @@
|
||||
//
|
||||
// SocketIOClient.swift
|
||||
// Socket.IO-Client-Swift
|
||||
//
|
||||
// Created by Erik Little on 11/23/14.
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
// THE SOFTWARE.
|
||||
|
||||
import Dispatch
|
||||
import Foundation
|
||||
|
||||
/// Represents a socket.io-client.
|
||||
///
|
||||
/// Clients are created through a `SocketManager`, which owns the `SocketEngineSpec` that controls the connection to the server.
|
||||
///
|
||||
/// For example:
|
||||
///
|
||||
/// ```swift
|
||||
/// // Create a socket for the /swift namespace
|
||||
/// let socket = manager.socket(forNamespace: "/swift")
|
||||
///
|
||||
/// // Add some handlers and connect
|
||||
/// ```
|
||||
///
|
||||
/// **NOTE**: The client is not thread/queue safe, all interaction with the socket should be done on the `manager.handleQueue`
|
||||
///
|
||||
open class SocketIOClient : NSObject, SocketIOClientSpec {
|
||||
// MARK: Properties
|
||||
|
||||
/// The namespace that this socket is currently connected to.
|
||||
///
|
||||
/// **Must** start with a `/`.
|
||||
@objc
|
||||
public let nsp: String
|
||||
|
||||
/// The session id of this client.
|
||||
@objc
|
||||
public var sid: String {
|
||||
guard let engine = manager?.engine else { return "" }
|
||||
|
||||
return nsp == "/" ? engine.sid : "\(nsp)#\(engine.sid)"
|
||||
}
|
||||
|
||||
/// A handler that will be called on any event.
|
||||
public private(set) var anyHandler: ((SocketAnyEvent) -> ())?
|
||||
|
||||
/// The array of handlers for this socket.
|
||||
public private(set) var handlers = [SocketEventHandler]()
|
||||
|
||||
/// The manager for this socket.
|
||||
@objc
|
||||
public private(set) weak var manager: SocketManagerSpec?
|
||||
|
||||
/// A view into this socket where emits do not check for binary data.
|
||||
///
|
||||
/// Usage:
|
||||
///
|
||||
/// ```swift
|
||||
/// socket.rawEmitView.emit("myEvent", myObject)
|
||||
/// ```
|
||||
///
|
||||
/// **NOTE**: It is not safe to hold on to this view beyond the life of the socket.
|
||||
@objc
|
||||
public private(set) lazy var rawEmitView = SocketRawView(socket: self)
|
||||
|
||||
/// The status of this client.
|
||||
@objc
|
||||
public private(set) var status = SocketIOStatus.notConnected {
|
||||
didSet {
|
||||
handleClientEvent(.statusChange, data: [status])
|
||||
}
|
||||
}
|
||||
|
||||
let ackHandlers = SocketAckManager()
|
||||
|
||||
private(set) var currentAck = -1
|
||||
|
||||
private lazy var logType = "SocketIOClient{\(nsp)}"
|
||||
|
||||
// MARK: Initializers
|
||||
|
||||
/// Type safe way to create a new SocketIOClient. `opts` can be omitted.
|
||||
///
|
||||
/// - parameter manager: The manager for this socket.
|
||||
/// - parameter nsp: The namespace of the socket.
|
||||
@objc
|
||||
public init(manager: SocketManagerSpec, nsp: String) {
|
||||
self.manager = manager
|
||||
self.nsp = nsp
|
||||
|
||||
super.init()
|
||||
}
|
||||
|
||||
deinit {
|
||||
DefaultSocketLogger.Logger.log("Client is being released", type: logType)
|
||||
}
|
||||
|
||||
// MARK: Methods
|
||||
|
||||
/// Connect to the server. The same as calling `connect(timeoutAfter:withHandler:)` with a timeout of 0.
|
||||
///
|
||||
/// Only call after adding your event listeners, unless you know what you're doing.
|
||||
@objc
|
||||
open func connect() {
|
||||
connect(timeoutAfter: 0, withHandler: nil)
|
||||
}
|
||||
|
||||
/// Connect to the server. If we aren't connected after `timeoutAfter` seconds, then `withHandler` is called.
|
||||
///
|
||||
/// Only call after adding your event listeners, unless you know what you're doing.
|
||||
///
|
||||
/// - parameter timeoutAfter: The number of seconds after which if we are not connected we assume the connection
|
||||
/// has failed. Pass 0 to never timeout.
|
||||
/// - parameter handler: The handler to call when the client fails to connect.
|
||||
@objc
|
||||
open func connect(timeoutAfter: Double, withHandler handler: (() -> ())?) {
|
||||
assert(timeoutAfter >= 0, "Invalid timeout: \(timeoutAfter)")
|
||||
|
||||
guard let manager = self.manager, status != .connected else {
|
||||
DefaultSocketLogger.Logger.log("Tried connecting on an already connected socket", type: logType)
|
||||
return
|
||||
}
|
||||
|
||||
status = .connecting
|
||||
|
||||
joinNamespace()
|
||||
|
||||
if manager.status == .connected && nsp == "/" {
|
||||
// We might not get a connect event for the default nsp, fire immediately
|
||||
didConnect(toNamespace: nsp)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
guard timeoutAfter != 0 else { return }
|
||||
|
||||
manager.handleQueue.asyncAfter(deadline: DispatchTime.now() + timeoutAfter) {[weak self] in
|
||||
guard let this = self, this.status == .connecting || this.status == .notConnected else { return }
|
||||
|
||||
this.status = .disconnected
|
||||
this.leaveNamespace()
|
||||
|
||||
handler?()
|
||||
}
|
||||
}
|
||||
|
||||
func createOnAck(_ items: [Any], binary: Bool = true) -> OnAckCallback {
|
||||
currentAck += 1
|
||||
|
||||
return OnAckCallback(ackNumber: currentAck, items: items, socket: self)
|
||||
}
|
||||
|
||||
/// Called when the client connects to a namespace. If the client was created with a namespace upfront,
|
||||
/// then this is only called when the client connects to that namespace.
|
||||
///
|
||||
/// - parameter toNamespace: The namespace that was connected to.
|
||||
open func didConnect(toNamespace namespace: String) {
|
||||
guard status != .connected else { return }
|
||||
|
||||
DefaultSocketLogger.Logger.log("Socket connected", type: logType)
|
||||
|
||||
status = .connected
|
||||
|
||||
handleClientEvent(.connect, data: [namespace])
|
||||
}
|
||||
|
||||
/// Called when the client has disconnected from socket.io.
|
||||
///
|
||||
/// - parameter reason: The reason for the disconnection.
|
||||
open func didDisconnect(reason: String) {
|
||||
guard status != .disconnected else { return }
|
||||
|
||||
DefaultSocketLogger.Logger.log("Disconnected: \(reason)", type: logType)
|
||||
|
||||
status = .disconnected
|
||||
|
||||
handleClientEvent(.disconnect, data: [reason])
|
||||
}
|
||||
|
||||
/// Disconnects the socket.
|
||||
///
|
||||
/// This will cause the socket to leave the namespace it is associated to, as well as remove itself from the
|
||||
/// `manager`.
|
||||
@objc
|
||||
open func disconnect() {
|
||||
DefaultSocketLogger.Logger.log("Closing socket", type: logType)
|
||||
|
||||
leaveNamespace()
|
||||
}
|
||||
|
||||
/// 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 {
|
||||
try emit(event, with: items.map({ try $0.socketRepresentation() }))
|
||||
} catch {
|
||||
DefaultSocketLogger.Logger.error("Error creating socketRepresentation for emit: \(event), \(items)",
|
||||
type: logType)
|
||||
|
||||
handleClientEvent(.error, data: [event, items, error])
|
||||
}
|
||||
}
|
||||
|
||||
/// Same as emit, but meant for Objective-C
|
||||
///
|
||||
/// - parameter event: The event to send.
|
||||
/// - parameter items: The items to send with this event. Send an empty array to send no data.
|
||||
@objc
|
||||
open func emit(_ event: String, with items: [Any]) {
|
||||
emit([event] + items)
|
||||
}
|
||||
|
||||
/// Sends a message to the server, requesting an ack.
|
||||
///
|
||||
/// **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
|
||||
/// socket.emitWithAck("myEvent", 1).timingOut(after: 1) {data in
|
||||
/// ...
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// - parameter event: The event to send.
|
||||
/// - parameter items: The items to send with this event. May be left out.
|
||||
/// - returns: An `OnAckCallback`. You must call the `timingOut(after:)` method before the event will be sent.
|
||||
open func emitWithAck(_ event: String, _ items: SocketData...) -> OnAckCallback {
|
||||
do {
|
||||
return emitWithAck(event, with: try items.map({ try $0.socketRepresentation() }))
|
||||
} catch {
|
||||
DefaultSocketLogger.Logger.error("Error creating socketRepresentation for emit: \(event), \(items)",
|
||||
type: logType)
|
||||
|
||||
handleClientEvent(.error, data: [event, items, error])
|
||||
|
||||
return OnAckCallback(ackNumber: -1, items: [], socket: self)
|
||||
}
|
||||
}
|
||||
|
||||
/// Same as emitWithAck, but for Objective-C
|
||||
///
|
||||
/// **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.
|
||||
///
|
||||
/// Example:
|
||||
///
|
||||
/// ```swift
|
||||
/// socket.emitWithAck("myEvent", with: [1]).timingOut(after: 1) {data in
|
||||
/// ...
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// - parameter event: The event to send.
|
||||
/// - parameter items: The items to send with this event. Use `[]` to send nothing.
|
||||
/// - returns: An `OnAckCallback`. You must call the `timingOut(after:)` method before the event will be sent.
|
||||
@objc
|
||||
open func emitWithAck(_ event: String, with items: [Any]) -> OnAckCallback {
|
||||
return createOnAck([event] + items)
|
||||
}
|
||||
|
||||
func emit(_ data: [Any], ack: Int? = nil, binary: Bool = true, isAck: Bool = false) {
|
||||
guard status == .connected else {
|
||||
handleClientEvent(.error, data: ["Tried emitting when not connected"])
|
||||
return
|
||||
}
|
||||
|
||||
let packet = SocketPacket.packetFromEmit(data, id: ack ?? -1, nsp: nsp, ack: isAck, checkForBinary: binary)
|
||||
let str = packet.packetString
|
||||
|
||||
DefaultSocketLogger.Logger.log("Emitting: \(str), Ack: \(isAck)", type: logType)
|
||||
|
||||
manager?.engine?.send(str, withData: packet.binary)
|
||||
}
|
||||
|
||||
/// Call when you wish to tell the server that you've received the event for `ack`.
|
||||
///
|
||||
/// **You shouldn't need to call this directly.** Instead use an `SocketAckEmitter` that comes in an event callback.
|
||||
///
|
||||
/// - parameter ack: The ack number.
|
||||
/// - parameter with: The data for this ack.
|
||||
open func emitAck(_ ack: Int, with items: [Any]) {
|
||||
emit(items, ack: ack, binary: true, isAck: true)
|
||||
}
|
||||
|
||||
/// Called when socket.io has acked one of our emits. Causes the corresponding ack callback to be called.
|
||||
///
|
||||
/// - parameter ack: The number for this ack.
|
||||
/// - parameter data: The data sent back with this ack.
|
||||
@objc
|
||||
open func handleAck(_ ack: Int, data: [Any]) {
|
||||
guard status == .connected else { return }
|
||||
|
||||
DefaultSocketLogger.Logger.log("Handling ack: \(ack) with data: \(data)", type: logType)
|
||||
|
||||
ackHandlers.executeAck(ack, with: data)
|
||||
}
|
||||
|
||||
/// Called on socket.io specific events.
|
||||
///
|
||||
/// - parameter event: The `SocketClientEvent`.
|
||||
/// - parameter data: The data for this event.
|
||||
open func handleClientEvent(_ event: SocketClientEvent, data: [Any]) {
|
||||
handleEvent(event.rawValue, data: data, isInternalMessage: true)
|
||||
}
|
||||
|
||||
/// Called when we get an event from socket.io.
|
||||
///
|
||||
/// - parameter event: The name of the event.
|
||||
/// - parameter data: The data that was sent with this event.
|
||||
/// - parameter isInternalMessage: Whether this event was sent internally. If `true` it is always sent to handlers.
|
||||
/// - parameter ack: If > 0 then this event expects to get an ack back from the client.
|
||||
@objc
|
||||
open func handleEvent(_ event: String, data: [Any], isInternalMessage: Bool, withAck ack: Int = -1) {
|
||||
guard status == .connected || isInternalMessage else { return }
|
||||
|
||||
DefaultSocketLogger.Logger.log("Handling event: \(event) with data: \(data)", type: logType)
|
||||
|
||||
anyHandler?(SocketAnyEvent(event: event, items: data))
|
||||
|
||||
for handler in handlers where handler.event == event {
|
||||
handler.executeCallback(with: data, withAck: ack, withSocket: self)
|
||||
}
|
||||
}
|
||||
|
||||
/// Causes a client to handle a socket.io packet. The namespace for the packet must match the namespace of the
|
||||
/// socket.
|
||||
///
|
||||
/// - parameter packet: The packet to handle.
|
||||
open func handlePacket(_ packet: SocketPacket) {
|
||||
guard packet.nsp == nsp else { return }
|
||||
|
||||
switch packet.type {
|
||||
case .event, .binaryEvent:
|
||||
handleEvent(packet.event, data: packet.args, isInternalMessage: false, withAck: packet.id)
|
||||
case .ack, .binaryAck:
|
||||
handleAck(packet.id, data: packet.data)
|
||||
case .connect:
|
||||
didConnect(toNamespace: nsp)
|
||||
case .disconnect:
|
||||
didDisconnect(reason: "Got Disconnect")
|
||||
case .error:
|
||||
handleEvent("error", data: packet.data, isInternalMessage: true, withAck: packet.id)
|
||||
}
|
||||
}
|
||||
|
||||
/// Call when you wish to leave a namespace and disconnect this socket.
|
||||
@objc
|
||||
open func leaveNamespace() {
|
||||
manager?.disconnectSocket(self)
|
||||
}
|
||||
|
||||
/// Joins `nsp`.
|
||||
@objc
|
||||
open func joinNamespace() {
|
||||
DefaultSocketLogger.Logger.log("Joining namespace \(nsp)", type: logType)
|
||||
|
||||
manager?.connectSocket(self)
|
||||
}
|
||||
|
||||
/// Removes handler(s) for a client event.
|
||||
///
|
||||
/// If you wish to remove a client event handler, call the `off(id:)` with the UUID received from its `on` call.
|
||||
///
|
||||
/// - parameter clientEvent: The event to remove handlers for.
|
||||
open func off(clientEvent event: SocketClientEvent) {
|
||||
off(event.rawValue)
|
||||
}
|
||||
|
||||
/// Removes handler(s) based on an event name.
|
||||
///
|
||||
/// If you wish to remove a specific event, call the `off(id:)` with the UUID received from its `on` call.
|
||||
///
|
||||
/// - parameter event: The event to remove handlers for.
|
||||
@objc
|
||||
open func off(_ event: String) {
|
||||
DefaultSocketLogger.Logger.log("Removing handler for event: \(event)", type: logType)
|
||||
|
||||
handlers = handlers.filter({ $0.event != event })
|
||||
}
|
||||
|
||||
/// Removes a handler with the specified UUID gotten from an `on` or `once`
|
||||
///
|
||||
/// If you want to remove all events for an event, call the off `off(_:)` method with the event name.
|
||||
///
|
||||
/// - parameter id: The UUID of the handler you wish to remove.
|
||||
@objc
|
||||
open func off(id: UUID) {
|
||||
DefaultSocketLogger.Logger.log("Removing handler with id: \(id)", type: logType)
|
||||
|
||||
handlers = handlers.filter({ $0.id != id })
|
||||
}
|
||||
|
||||
/// Adds a handler for an event.
|
||||
///
|
||||
/// - parameter event: The event name for this handler.
|
||||
/// - parameter callback: The callback that will execute when this event is received.
|
||||
/// - returns: A unique id for the handler that can be used to remove it.
|
||||
@objc
|
||||
@discardableResult
|
||||
open func on(_ event: String, callback: @escaping NormalCallback) -> UUID {
|
||||
DefaultSocketLogger.Logger.log("Adding handler for event: \(event)", type: logType)
|
||||
|
||||
let handler = SocketEventHandler(event: event, id: UUID(), callback: callback)
|
||||
handlers.append(handler)
|
||||
|
||||
return handler.id
|
||||
}
|
||||
|
||||
/// Adds a handler for a client event.
|
||||
///
|
||||
/// Example:
|
||||
///
|
||||
/// ```swift
|
||||
/// socket.on(clientEvent: .connect) {data, ack in
|
||||
/// ...
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// - parameter event: The event for this handler.
|
||||
/// - parameter callback: The callback that will execute when this event is received.
|
||||
/// - returns: A unique id for the handler that can be used to remove it.
|
||||
@discardableResult
|
||||
open func on(clientEvent event: SocketClientEvent, callback: @escaping NormalCallback) -> UUID {
|
||||
return on(event.rawValue, callback: callback)
|
||||
}
|
||||
|
||||
/// Adds a single-use handler for a client event.
|
||||
///
|
||||
/// - parameter clientEvent: The event for this handler.
|
||||
/// - parameter callback: The callback that will execute when this event is received.
|
||||
/// - returns: A unique id for the handler that can be used to remove it.
|
||||
@discardableResult
|
||||
open func once(clientEvent event: SocketClientEvent, callback: @escaping NormalCallback) -> UUID {
|
||||
return once(event.rawValue, callback: callback)
|
||||
}
|
||||
|
||||
/// Adds a single-use handler for an event.
|
||||
///
|
||||
/// - parameter event: The event name for this handler.
|
||||
/// - parameter callback: The callback that will execute when this event is received.
|
||||
/// - returns: A unique id for the handler that can be used to remove it.
|
||||
@objc
|
||||
@discardableResult
|
||||
open func once(_ event: String, callback: @escaping NormalCallback) -> UUID {
|
||||
DefaultSocketLogger.Logger.log("Adding once handler for event: \(event)", type: logType)
|
||||
|
||||
let id = UUID()
|
||||
|
||||
let handler = SocketEventHandler(event: event, id: id) {[weak self] data, ack in
|
||||
guard let this = self else { return }
|
||||
this.off(id: id)
|
||||
callback(data, ack)
|
||||
}
|
||||
|
||||
handlers.append(handler)
|
||||
|
||||
return handler.id
|
||||
}
|
||||
|
||||
/// Adds a handler that will be called on every event.
|
||||
///
|
||||
/// - parameter handler: The callback that will execute whenever an event is received.
|
||||
@objc
|
||||
open func onAny(_ handler: @escaping (SocketAnyEvent) -> ()) {
|
||||
anyHandler = handler
|
||||
}
|
||||
|
||||
/// Tries to reconnect to the server.
|
||||
@objc
|
||||
@available(*, unavailable, message: "Call the manager's reconnect method")
|
||||
open func reconnect() { }
|
||||
|
||||
/// Removes all handlers.
|
||||
///
|
||||
/// Can be used after disconnecting to break any potential remaining retain cycles.
|
||||
@objc
|
||||
open func removeAllHandlers() {
|
||||
handlers.removeAll(keepingCapacity: false)
|
||||
}
|
||||
|
||||
/// Puts the socket back into the connecting state.
|
||||
/// Called when the manager detects a broken connection, or when a manual reconnect is triggered.
|
||||
///
|
||||
/// - parameter reason: The reason this socket is reconnecting.
|
||||
@objc
|
||||
open func setReconnecting(reason: String) {
|
||||
status = .connecting
|
||||
|
||||
handleClientEvent(.reconnect, data: [reason])
|
||||
}
|
||||
|
||||
// Test properties
|
||||
|
||||
var testHandlers: [SocketEventHandler] {
|
||||
return handlers
|
||||
}
|
||||
|
||||
func setTestable() {
|
||||
status = .connected
|
||||
}
|
||||
|
||||
func setTestStatus(_ status: SocketIOStatus) {
|
||||
self.status = status
|
||||
}
|
||||
|
||||
func emitTest(event: String, _ data: Any...) {
|
||||
emit([event] + data)
|
||||
}
|
||||
}
|
||||
+138
@@ -0,0 +1,138 @@
|
||||
//
|
||||
// SocketIOClientConfiguration.swift
|
||||
// Socket.IO-Client-Swift
|
||||
//
|
||||
// Created by Erik Little on 8/13/16.
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
// THE SOFTWARE.
|
||||
|
||||
/// An array-like type that holds `SocketIOClientOption`s
|
||||
public struct SocketIOClientConfiguration : ExpressibleByArrayLiteral, Collection, MutableCollection {
|
||||
// MARK: Typealiases
|
||||
|
||||
/// Type of element stored.
|
||||
public typealias Element = SocketIOClientOption
|
||||
|
||||
/// Index type.
|
||||
public typealias Index = Array<SocketIOClientOption>.Index
|
||||
|
||||
/// Iterator type.
|
||||
public typealias Iterator = Array<SocketIOClientOption>.Iterator
|
||||
|
||||
/// SubSequence type.
|
||||
public typealias SubSequence = Array<SocketIOClientOption>.SubSequence
|
||||
|
||||
// MARK: Properties
|
||||
|
||||
private var backingArray = [SocketIOClientOption]()
|
||||
|
||||
/// The start index of this collection.
|
||||
public var startIndex: Index {
|
||||
return backingArray.startIndex
|
||||
}
|
||||
|
||||
/// The end index of this collection.
|
||||
public var endIndex: Index {
|
||||
return backingArray.endIndex
|
||||
}
|
||||
|
||||
/// Whether this collection is empty.
|
||||
public var isEmpty: Bool {
|
||||
return backingArray.isEmpty
|
||||
}
|
||||
|
||||
/// The number of elements stored in this collection.
|
||||
public var count: Index.Stride {
|
||||
return backingArray.count
|
||||
}
|
||||
|
||||
/// The first element in this collection.
|
||||
public var first: Element? {
|
||||
return backingArray.first
|
||||
}
|
||||
|
||||
public subscript(position: Index) -> Element {
|
||||
get {
|
||||
return backingArray[position]
|
||||
}
|
||||
|
||||
set {
|
||||
backingArray[position] = newValue
|
||||
}
|
||||
}
|
||||
|
||||
public subscript(bounds: Range<Index>) -> SubSequence {
|
||||
get {
|
||||
return backingArray[bounds]
|
||||
}
|
||||
|
||||
set {
|
||||
backingArray[bounds] = newValue
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: Initializers
|
||||
|
||||
/// Creates a new `SocketIOClientConfiguration` from an array literal.
|
||||
///
|
||||
/// - parameter arrayLiteral: The elements.
|
||||
public init(arrayLiteral elements: Element...) {
|
||||
backingArray = elements
|
||||
}
|
||||
|
||||
// MARK: Methods
|
||||
|
||||
/// Creates an iterator for this collection.
|
||||
///
|
||||
/// - returns: An iterator over this collection.
|
||||
public func makeIterator() -> Iterator {
|
||||
return backingArray.makeIterator()
|
||||
}
|
||||
|
||||
/// - returns: The index after index.
|
||||
public func index(after i: Index) -> Index {
|
||||
return backingArray.index(after: i)
|
||||
}
|
||||
|
||||
/// Special method that inserts `element` into the collection, replacing any other instances of `element`.
|
||||
///
|
||||
/// - parameter element: The element to insert.
|
||||
/// - parameter replacing: Whether to replace any occurrences of element to the new item. Default is `true`.
|
||||
public mutating func insert(_ element: Element, replacing replace: Bool = true) {
|
||||
for i in 0..<backingArray.count where backingArray[i] == element {
|
||||
guard replace else { return }
|
||||
|
||||
backingArray[i] = element
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
backingArray.append(element)
|
||||
}
|
||||
}
|
||||
|
||||
/// Declares that a type can set configs from a `SocketIOClientConfiguration`.
|
||||
public protocol ConfigSettable {
|
||||
// MARK: Methods
|
||||
|
||||
/// Called when an `ConfigSettable` should set/update its configs from a given configuration.
|
||||
///
|
||||
/// - parameter config: The `SocketIOClientConfiguration` that should be used to set/update configs.
|
||||
mutating func setConfigs(_ config: SocketIOClientConfiguration)
|
||||
}
|
||||
+197
@@ -0,0 +1,197 @@
|
||||
//
|
||||
// SocketIOClientOption .swift
|
||||
// Socket.IO-Client-Swift
|
||||
//
|
||||
// Created by Erik Little on 10/17/15.
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
// THE SOFTWARE.
|
||||
|
||||
import Foundation
|
||||
import Starscream
|
||||
|
||||
protocol ClientOption : CustomStringConvertible, Equatable {
|
||||
func getSocketIOOptionValue() -> Any
|
||||
}
|
||||
|
||||
/// The options for a client.
|
||||
public enum SocketIOClientOption : ClientOption {
|
||||
/// If given, the WebSocket transport will attempt to use compression.
|
||||
case compress
|
||||
|
||||
/// A dictionary of GET parameters that will be included in the connect url.
|
||||
case connectParams([String: Any])
|
||||
|
||||
/// An array of cookies that will be sent during the initial connection.
|
||||
case cookies([HTTPCookie])
|
||||
|
||||
/// Any extra HTTP headers that should be sent during the initial connection.
|
||||
case extraHeaders([String: String])
|
||||
|
||||
/// If passed `true`, will cause the client to always create a new engine. Useful for debugging,
|
||||
/// or when you want to be sure no state from previous engines is being carried over.
|
||||
case forceNew(Bool)
|
||||
|
||||
/// If passed `true`, the only transport that will be used will be HTTP long-polling.
|
||||
case forcePolling(Bool)
|
||||
|
||||
/// If passed `true`, the only transport that will be used will be WebSockets.
|
||||
case forceWebsockets(Bool)
|
||||
|
||||
/// The queue that all interaction with the client should occur on. This is the queue that event handlers are
|
||||
/// called on.
|
||||
///
|
||||
/// **This should be a serial queue! Concurrent queues are not supported and might cause crashes and races**.
|
||||
case handleQueue(DispatchQueue)
|
||||
|
||||
/// If passed `true`, the client will log debug information. This should be turned off in production code.
|
||||
case log(Bool)
|
||||
|
||||
/// Used to pass in a custom logger.
|
||||
case logger(SocketLogger)
|
||||
|
||||
/// A custom path to socket.io. Only use this if the socket.io server is configured to look for this path.
|
||||
case path(String)
|
||||
|
||||
/// If passed `false`, the client will not reconnect when it loses connection. Useful if you want full control
|
||||
/// over when reconnects happen.
|
||||
case reconnects(Bool)
|
||||
|
||||
/// The number of times to try and reconnect before giving up. Pass `-1` to [never give up](https://www.youtube.com/watch?v=dQw4w9WgXcQ).
|
||||
case reconnectAttempts(Int)
|
||||
|
||||
/// The number of seconds to wait before reconnect attempts.
|
||||
case reconnectWait(Int)
|
||||
|
||||
/// Set `true` if your server is using secure transports.
|
||||
case secure(Bool)
|
||||
|
||||
/// Allows you to set which certs are valid. Useful for SSL pinning.
|
||||
case security(SSLSecurity)
|
||||
|
||||
/// If you're using a self-signed set. Only use for development.
|
||||
case selfSigned(Bool)
|
||||
|
||||
/// Sets an NSURLSessionDelegate for the underlying engine. Useful if you need to handle self-signed certs.
|
||||
case sessionDelegate(URLSessionDelegate)
|
||||
|
||||
// MARK: Properties
|
||||
|
||||
/// The description of this option.
|
||||
public var description: String {
|
||||
let description: String
|
||||
|
||||
switch self {
|
||||
case .compress:
|
||||
description = "compress"
|
||||
case .connectParams:
|
||||
description = "connectParams"
|
||||
case .cookies:
|
||||
description = "cookies"
|
||||
case .extraHeaders:
|
||||
description = "extraHeaders"
|
||||
case .forceNew:
|
||||
description = "forceNew"
|
||||
case .forcePolling:
|
||||
description = "forcePolling"
|
||||
case .forceWebsockets:
|
||||
description = "forceWebsockets"
|
||||
case .handleQueue:
|
||||
description = "handleQueue"
|
||||
case .log:
|
||||
description = "log"
|
||||
case .logger:
|
||||
description = "logger"
|
||||
case .path:
|
||||
description = "path"
|
||||
case .reconnects:
|
||||
description = "reconnects"
|
||||
case .reconnectAttempts:
|
||||
description = "reconnectAttempts"
|
||||
case .reconnectWait:
|
||||
description = "reconnectWait"
|
||||
case .secure:
|
||||
description = "secure"
|
||||
case .selfSigned:
|
||||
description = "selfSigned"
|
||||
case .security:
|
||||
description = "security"
|
||||
case .sessionDelegate:
|
||||
description = "sessionDelegate"
|
||||
}
|
||||
|
||||
return description
|
||||
}
|
||||
|
||||
func getSocketIOOptionValue() -> Any {
|
||||
let value: Any
|
||||
|
||||
switch self {
|
||||
case .compress:
|
||||
value = true
|
||||
case let .connectParams(params):
|
||||
value = params
|
||||
case let .cookies(cookies):
|
||||
value = cookies
|
||||
case let .extraHeaders(headers):
|
||||
value = headers
|
||||
case let .forceNew(force):
|
||||
value = force
|
||||
case let .forcePolling(force):
|
||||
value = force
|
||||
case let .forceWebsockets(force):
|
||||
value = force
|
||||
case let .handleQueue(queue):
|
||||
value = queue
|
||||
case let .log(log):
|
||||
value = log
|
||||
case let .logger(logger):
|
||||
value = logger
|
||||
case let .path(path):
|
||||
value = path
|
||||
case let .reconnects(reconnects):
|
||||
value = reconnects
|
||||
case let .reconnectAttempts(attempts):
|
||||
value = attempts
|
||||
case let .reconnectWait(wait):
|
||||
value = wait
|
||||
case let .secure(secure):
|
||||
value = secure
|
||||
case let .security(security):
|
||||
value = security
|
||||
case let .selfSigned(signed):
|
||||
value = signed
|
||||
case let .sessionDelegate(delegate):
|
||||
value = delegate
|
||||
}
|
||||
|
||||
return value
|
||||
}
|
||||
|
||||
// MARK: Operators
|
||||
|
||||
/// Compares whether two options are the same.
|
||||
///
|
||||
/// - parameter lhs: Left operand to compare.
|
||||
/// - parameter rhs: Right operand to compare.
|
||||
/// - returns: `true` if the two are the same option.
|
||||
public static func ==(lhs: SocketIOClientOption, rhs: SocketIOClientOption) -> Bool {
|
||||
return lhs.description == rhs.description
|
||||
}
|
||||
|
||||
}
|
||||
+337
@@ -0,0 +1,337 @@
|
||||
//
|
||||
// SocketIOClientSpec.swift
|
||||
// Socket.IO-Client-Swift
|
||||
//
|
||||
// Created by Erik Little on 1/3/16.
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
// THE SOFTWARE.
|
||||
|
||||
import Dispatch
|
||||
import Foundation
|
||||
|
||||
/// Defines the interface for a SocketIOClient.
|
||||
public protocol SocketIOClientSpec : AnyObject {
|
||||
// MARK: Properties
|
||||
|
||||
/// A handler that will be called on any event.
|
||||
var anyHandler: ((SocketAnyEvent) -> ())? { get }
|
||||
|
||||
/// The array of handlers for this socket.
|
||||
var handlers: [SocketEventHandler] { get }
|
||||
|
||||
/// The manager for this socket.
|
||||
var manager: SocketManagerSpec? { get }
|
||||
|
||||
/// The namespace that this socket is currently connected to.
|
||||
///
|
||||
/// **Must** start with a `/`.
|
||||
var nsp: String { get }
|
||||
|
||||
/// A view into this socket where emits do not check for binary data.
|
||||
///
|
||||
/// Usage:
|
||||
///
|
||||
/// ```swift
|
||||
/// socket.rawEmitView.emit("myEvent", myObject)
|
||||
/// ```
|
||||
///
|
||||
/// **NOTE**: It is not safe to hold on to this view beyond the life of the socket.
|
||||
var rawEmitView: SocketRawView { get }
|
||||
|
||||
/// The status of this client.
|
||||
var status: SocketIOStatus { get }
|
||||
|
||||
// MARK: Methods
|
||||
|
||||
/// Connect to the server. The same as calling `connect(timeoutAfter:withHandler:)` with a timeout of 0.
|
||||
///
|
||||
/// Only call after adding your event listeners, unless you know what you're doing.
|
||||
func connect()
|
||||
|
||||
/// Connect to the server. If we aren't connected after `timeoutAfter` seconds, then `withHandler` is called.
|
||||
///
|
||||
/// Only call after adding your event listeners, unless you know what you're doing.
|
||||
///
|
||||
/// - parameter timeoutAfter: The number of seconds after which if we are not connected we assume the connection
|
||||
/// has failed. Pass 0 to never timeout.
|
||||
/// - parameter handler: The handler to call when the client fails to connect.
|
||||
func connect(timeoutAfter: Double, withHandler handler: (() -> ())?)
|
||||
|
||||
/// Called when the client connects to a namespace. If the client was created with a namespace upfront,
|
||||
/// then this is only called when the client connects to that namespace.
|
||||
///
|
||||
/// - parameter toNamespace: The namespace that was connected to.
|
||||
func didConnect(toNamespace namespace: String)
|
||||
|
||||
/// Called when the client has disconnected from socket.io.
|
||||
///
|
||||
/// - parameter reason: The reason for the disconnection.
|
||||
func didDisconnect(reason: String)
|
||||
|
||||
/// Called when the client encounters an error.
|
||||
///
|
||||
/// - parameter reason: The reason for the disconnection.
|
||||
func didError(reason: String)
|
||||
|
||||
/// Disconnects the socket.
|
||||
func disconnect()
|
||||
|
||||
/// 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.
|
||||
func emit(_ event: String, _ items: SocketData...)
|
||||
|
||||
/// Call when you wish to tell the server that you've received the event for `ack`.
|
||||
///
|
||||
/// - parameter ack: The ack number.
|
||||
/// - parameter with: The data for this ack.
|
||||
func emitAck(_ ack: Int, with items: [Any])
|
||||
|
||||
/// Sends a message to the server, requesting an ack.
|
||||
///
|
||||
/// **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
|
||||
/// socket.emitWithAck("myEvent", 1).timingOut(after: 1) {data in
|
||||
/// ...
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// - parameter event: The event to send.
|
||||
/// - parameter items: The items to send with this event. May be left out.
|
||||
/// - returns: An `OnAckCallback`. You must call the `timingOut(after:)` method before the event will be sent.
|
||||
func emitWithAck(_ event: String, _ items: SocketData...) -> OnAckCallback
|
||||
|
||||
/// Called when socket.io has acked one of our emits. Causes the corresponding ack callback to be called.
|
||||
///
|
||||
/// - parameter ack: The number for this ack.
|
||||
/// - parameter data: The data sent back with this ack.
|
||||
func handleAck(_ ack: Int, data: [Any])
|
||||
|
||||
/// Called on socket.io specific events.
|
||||
///
|
||||
/// - parameter event: The `SocketClientEvent`.
|
||||
/// - parameter data: The data for this event.
|
||||
func handleClientEvent(_ event: SocketClientEvent, data: [Any])
|
||||
|
||||
/// Called when we get an event from socket.io.
|
||||
///
|
||||
/// - parameter event: The name of the event.
|
||||
/// - parameter data: The data that was sent with this event.
|
||||
/// - parameter isInternalMessage: Whether this event was sent internally. If `true` it is always sent to handlers.
|
||||
/// - parameter ack: If > 0 then this event expects to get an ack back from the client.
|
||||
func handleEvent(_ event: String, data: [Any], isInternalMessage: Bool, withAck ack: Int)
|
||||
|
||||
/// Causes a client to handle a socket.io packet. The namespace for the packet must match the namespace of the
|
||||
/// socket.
|
||||
///
|
||||
/// - parameter packet: The packet to handle.
|
||||
func handlePacket(_ packet: SocketPacket)
|
||||
|
||||
/// Call when you wish to leave a namespace and disconnect this socket.
|
||||
func leaveNamespace()
|
||||
|
||||
/// Joins `nsp`.
|
||||
func joinNamespace()
|
||||
|
||||
/// Removes handler(s) for a client event.
|
||||
///
|
||||
/// If you wish to remove a client event handler, call the `off(id:)` with the UUID received from its `on` call.
|
||||
///
|
||||
/// - parameter clientEvent: The event to remove handlers for.
|
||||
func off(clientEvent event: SocketClientEvent)
|
||||
|
||||
/// Removes handler(s) based on an event name.
|
||||
///
|
||||
/// If you wish to remove a specific event, call the `off(id:)` with the UUID received from its `on` call.
|
||||
///
|
||||
/// - parameter event: The event to remove handlers for.
|
||||
func off(_ event: String)
|
||||
|
||||
/// Removes a handler with the specified UUID gotten from an `on` or `once`
|
||||
///
|
||||
/// If you want to remove all events for an event, call the off `off(_:)` method with the event name.
|
||||
///
|
||||
/// - parameter id: The UUID of the handler you wish to remove.
|
||||
func off(id: UUID)
|
||||
|
||||
/// Adds a handler for an event.
|
||||
///
|
||||
/// - parameter event: The event name for this handler.
|
||||
/// - parameter callback: The callback that will execute when this event is received.
|
||||
/// - returns: A unique id for the handler that can be used to remove it.
|
||||
func on(_ event: String, callback: @escaping NormalCallback) -> UUID
|
||||
|
||||
/// Adds a handler for a client event.
|
||||
///
|
||||
/// Example:
|
||||
///
|
||||
/// ```swift
|
||||
/// socket.on(clientEvent: .connect) {data, ack in
|
||||
/// ...
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// - parameter event: The event for this handler.
|
||||
/// - parameter callback: The callback that will execute when this event is received.
|
||||
/// - returns: A unique id for the handler that can be used to remove it.
|
||||
func on(clientEvent event: SocketClientEvent, callback: @escaping NormalCallback) -> UUID
|
||||
|
||||
/// Adds a single-use handler for a client event.
|
||||
///
|
||||
/// - parameter clientEvent: The event for this handler.
|
||||
/// - parameter callback: The callback that will execute when this event is received.
|
||||
/// - returns: A unique id for the handler that can be used to remove it.
|
||||
func once(clientEvent event: SocketClientEvent, callback: @escaping NormalCallback) -> UUID
|
||||
|
||||
/// Adds a single-use handler for an event.
|
||||
///
|
||||
/// - parameter event: The event name for this handler.
|
||||
/// - parameter callback: The callback that will execute when this event is received.
|
||||
/// - returns: A unique id for the handler that can be used to remove it.
|
||||
func once(_ event: String, callback: @escaping NormalCallback) -> UUID
|
||||
|
||||
/// Adds a handler that will be called on every event.
|
||||
///
|
||||
/// - parameter handler: The callback that will execute whenever an event is received.
|
||||
func onAny(_ handler: @escaping (SocketAnyEvent) -> ())
|
||||
|
||||
/// Removes all handlers.
|
||||
///
|
||||
/// Can be used after disconnecting to break any potential remaining retain cycles.
|
||||
func removeAllHandlers()
|
||||
|
||||
/// Puts the socket back into the connecting state.
|
||||
/// Called when the manager detects a broken connection, or when a manual reconnect is triggered.
|
||||
///
|
||||
/// parameter reason: The reason this socket is going reconnecting.
|
||||
func setReconnecting(reason: String)
|
||||
}
|
||||
|
||||
public extension SocketIOClientSpec {
|
||||
/// Default implementation.
|
||||
public func didError(reason: String) {
|
||||
DefaultSocketLogger.Logger.error("\(reason)", type: "SocketIOClient")
|
||||
|
||||
handleClientEvent(.error, data: [reason])
|
||||
}
|
||||
}
|
||||
|
||||
/// The set of events that are generated by the client.
|
||||
public enum SocketClientEvent : String {
|
||||
// MARK: Cases
|
||||
|
||||
/// Emitted when the client connects. This is also called on a successful reconnection. A connect event gets one
|
||||
/// data item: the namespace that was connected to.
|
||||
///
|
||||
/// ```swift
|
||||
/// socket.on(clientEvent: .connect) {data, ack in
|
||||
/// guard let nsp = data[0] as? String else { return }
|
||||
/// // Some logic using the nsp
|
||||
/// }
|
||||
/// ```
|
||||
case connect
|
||||
|
||||
/// Emitted when the socket has disconnected and will not attempt to try to reconnect.
|
||||
///
|
||||
/// Usage:
|
||||
///
|
||||
/// ```swift
|
||||
/// socket.on(clientEvent: .disconnect) {data, ack in
|
||||
/// // Some cleanup logic
|
||||
/// }
|
||||
/// ```
|
||||
case disconnect
|
||||
|
||||
/// Emitted when an error occurs.
|
||||
///
|
||||
/// Usage:
|
||||
///
|
||||
/// ```swift
|
||||
/// socket.on(clientEvent: .error) {data, ack in
|
||||
/// // Some logging
|
||||
/// }
|
||||
/// ```
|
||||
case error
|
||||
|
||||
/// Emitted whenever the engine sends a ping.
|
||||
///
|
||||
/// Usage:
|
||||
///
|
||||
/// ```swift
|
||||
/// socket.on(clientEvent: .ping) {_, _ in
|
||||
/// // Maybe keep track of latency?
|
||||
/// }
|
||||
/// ```
|
||||
case ping
|
||||
|
||||
/// Emitted whenever the engine gets a pong.
|
||||
///
|
||||
/// Usage:
|
||||
///
|
||||
/// ```swift
|
||||
/// socket.on(clientEvent: .pong) {_, _ in
|
||||
/// // Maybe keep track of latency?
|
||||
/// }
|
||||
/// ```
|
||||
case pong
|
||||
|
||||
/// Emitted when the client begins the reconnection process.
|
||||
///
|
||||
/// Usage:
|
||||
///
|
||||
/// ```swift
|
||||
/// socket.on(clientEvent: .reconnect) {data, ack in
|
||||
/// // Some reconnect event logic
|
||||
/// }
|
||||
/// ```
|
||||
case reconnect
|
||||
|
||||
/// Emitted each time the client tries to reconnect to the server.
|
||||
///
|
||||
/// Usage:
|
||||
///
|
||||
/// ```swift
|
||||
/// socket.on(clientEvent: .reconnectAttempt) {data, ack in
|
||||
/// // Some reconnect attempt logging
|
||||
/// }
|
||||
/// ```
|
||||
case reconnectAttempt
|
||||
|
||||
/// Emitted every time there is a change in the client's status.
|
||||
///
|
||||
/// Usage:
|
||||
///
|
||||
/// ```swift
|
||||
/// socket.on(clientEvent: .statusChange) {data, ack in
|
||||
/// // Some status changing logging
|
||||
/// }
|
||||
/// ```
|
||||
case statusChange
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
//
|
||||
// SocketIOStatus.swift
|
||||
// Socket.IO-Client-Swift
|
||||
//
|
||||
// Created by Erik Little on 8/14/15.
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
// THE SOFTWARE.
|
||||
|
||||
import Foundation
|
||||
|
||||
/// Represents state of a manager or client.
|
||||
@objc
|
||||
public enum SocketIOStatus : Int, CustomStringConvertible {
|
||||
// MARK: Cases
|
||||
|
||||
/// The client/manager has never been connected. Or the client has been reset.
|
||||
case notConnected
|
||||
|
||||
/// The client/manager was once connected, but not anymore.
|
||||
case disconnected
|
||||
|
||||
/// The client/manager is in the process of connecting.
|
||||
case connecting
|
||||
|
||||
/// The client/manager is currently connected.
|
||||
case connected
|
||||
|
||||
// MARK: Properties
|
||||
|
||||
/// - returns: True if this client/manager is connected/connecting to a server.
|
||||
public var active: Bool {
|
||||
return self == .connected || self == .connecting
|
||||
}
|
||||
|
||||
public var description: String {
|
||||
switch self {
|
||||
case .connected: return "connected"
|
||||
case .connecting: return "connecting"
|
||||
case .disconnected: return "disconnected"
|
||||
case .notConnected: return "notConnected"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,163 @@
|
||||
//
|
||||
// SocketRawView.swift
|
||||
// Socket.IO-Client-Swift
|
||||
//
|
||||
// Created by Erik Little on 3/30/18.
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
// THE SOFTWARE.
|
||||
|
||||
import Foundation
|
||||
|
||||
/// Class that gives a backwards compatible way to cause an emit not to recursively check for Data objects.
|
||||
///
|
||||
/// Usage:
|
||||
///
|
||||
/// ```swift
|
||||
/// socket.rawEmitView.emit("myEvent", myObject)
|
||||
/// ```
|
||||
public final class SocketRawView : NSObject {
|
||||
private unowned let socket: SocketIOClient
|
||||
|
||||
init(socket: SocketIOClient) {
|
||||
self.socket = socket
|
||||
}
|
||||
|
||||
/// 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.
|
||||
public func emit(_ event: String, _ items: SocketData...) {
|
||||
do {
|
||||
try emit(event, with: items.map({ try $0.socketRepresentation() }))
|
||||
} catch {
|
||||
DefaultSocketLogger.Logger.error("Error creating socketRepresentation for emit: \(event), \(items)",
|
||||
type: "SocketIOClient")
|
||||
|
||||
socket.handleClientEvent(.error, data: [event, items, error])
|
||||
}
|
||||
}
|
||||
|
||||
/// Same as emit, but meant for Objective-C
|
||||
///
|
||||
/// - parameter event: The event to send.
|
||||
/// - parameter items: The items to send with this event. Send an empty array to send no data.
|
||||
@objc
|
||||
public func emit(_ event: String, with items: [Any]) {
|
||||
socket.emit([event] + items, binary: false)
|
||||
}
|
||||
|
||||
/// Sends a message to the server, requesting an ack.
|
||||
///
|
||||
/// **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
|
||||
/// socket.emitWithAck("myEvent", 1).timingOut(after: 1) {data in
|
||||
/// ...
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// - parameter event: The event to send.
|
||||
/// - parameter items: The items to send with this event. May be left out.
|
||||
/// - returns: An `OnAckCallback`. You must call the `timingOut(after:)` method before the event will be sent.
|
||||
public func emitWithAck(_ event: String, _ items: SocketData...) -> OnAckCallback {
|
||||
do {
|
||||
return emitWithAck(event, with: try items.map({ try $0.socketRepresentation() }))
|
||||
} catch {
|
||||
DefaultSocketLogger.Logger.error("Error creating socketRepresentation for emit: \(event), \(items)",
|
||||
type: "SocketIOClient")
|
||||
|
||||
socket.handleClientEvent(.error, data: [event, items, error])
|
||||
|
||||
return OnAckCallback(ackNumber: -1, items: [], socket: socket)
|
||||
}
|
||||
}
|
||||
|
||||
/// Same as emitWithAck, but for Objective-C
|
||||
///
|
||||
/// **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.
|
||||
///
|
||||
/// Example:
|
||||
///
|
||||
/// ```swift
|
||||
/// socket.emitWithAck("myEvent", with: [1]).timingOut(after: 1) {data in
|
||||
/// ...
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// - parameter event: The event to send.
|
||||
/// - parameter items: The items to send with this event. Use `[]` to send nothing.
|
||||
/// - returns: An `OnAckCallback`. You must call the `timingOut(after:)` method before the event will be sent.
|
||||
@objc
|
||||
public func emitWithAck(_ event: String, with items: [Any]) -> OnAckCallback {
|
||||
return socket.createOnAck([event] + items, binary: false)
|
||||
}
|
||||
}
|
||||
|
||||
/// Class that gives a backwards compatible way to cause an emit not to recursively check for Data objects.
|
||||
///
|
||||
/// Usage:
|
||||
///
|
||||
/// ```swift
|
||||
/// ack.rawEmitView.with(myObject)
|
||||
/// ```
|
||||
public final class SocketRawAckView : NSObject {
|
||||
private unowned let socket: SocketIOClient
|
||||
private let ackNum: Int
|
||||
|
||||
init(socket: SocketIOClient, ackNum: Int) {
|
||||
self.socket = socket
|
||||
self.ackNum = ackNum
|
||||
}
|
||||
|
||||
/// Call to ack receiving this event.
|
||||
///
|
||||
/// 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 `[ackNum, items, theError]`
|
||||
///
|
||||
/// - parameter items: A variable number of items to send when acking.
|
||||
public func with(_ items: SocketData...) {
|
||||
guard ackNum != -1 else { return }
|
||||
|
||||
do {
|
||||
socket.emit(try items.map({ try $0.socketRepresentation() }), ack: ackNum, binary: false, isAck: true)
|
||||
} catch {
|
||||
socket.handleClientEvent(.error, data: [ackNum, items, error])
|
||||
}
|
||||
}
|
||||
|
||||
/// Call to ack receiving this event.
|
||||
///
|
||||
/// - parameter items: An array of items to send when acking. Use `[]` to send nothing.
|
||||
@objc
|
||||
public func with(_ items: [Any]) {
|
||||
guard ackNum != -1 else { return }
|
||||
|
||||
socket.emit(items, ack: ackNum, binary: false, isAck: true)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user