Repository: nuclearace/Socket.IO-Client-Swift Branch: master Commit: af5ce97b755d Files: 98 Total size: 1.7 MB Directory structure: gitextract_8c5nirf9/ ├── .gitignore ├── .travis.yml ├── CHANGELOG.md ├── Cartfile ├── Cartfile.resolved ├── LICENSE ├── Package.resolved ├── Package.swift ├── README.md ├── Socket.IO-Client-Swift.podspec ├── SocketIO/ │ ├── Info.plist │ └── SocketIO.h ├── Source/ │ └── SocketIO/ │ ├── Ack/ │ │ ├── SocketAckEmitter.swift │ │ └── SocketAckManager.swift │ ├── Client/ │ │ ├── SocketAnyEvent.swift │ │ ├── SocketEventHandler.swift │ │ ├── SocketIOClient.swift │ │ ├── SocketIOClientConfiguration.swift │ │ ├── SocketIOClientOption.swift │ │ ├── SocketIOClientSpec.swift │ │ ├── SocketIOStatus.swift │ │ └── SocketRawView.swift │ ├── Engine/ │ │ ├── SocketEngine.swift │ │ ├── SocketEngineClient.swift │ │ ├── SocketEnginePacketType.swift │ │ ├── SocketEnginePollable.swift │ │ ├── SocketEngineSpec.swift │ │ └── SocketEngineWebsocket.swift │ ├── Manager/ │ │ ├── SocketManager.swift │ │ └── SocketManagerSpec.swift │ ├── Parse/ │ │ ├── SocketPacket.swift │ │ └── SocketParsable.swift │ └── Util/ │ ├── SocketExtensions.swift │ ├── SocketLogger.swift │ ├── SocketStringReader.swift │ └── SocketTypes.swift ├── Tests/ │ └── TestSocketIO/ │ ├── SocketAckManagerTest.swift │ ├── SocketBasicPacketTest.swift │ ├── SocketEngineTest.swift │ ├── SocketIOClientConfigurationTest.swift │ ├── SocketMangerTest.swift │ ├── SocketNamespacePacketTest.swift │ ├── SocketParserTest.swift │ ├── SocketSideEffectTest.swift │ └── utils.swift ├── Usage Docs/ │ ├── 12to13.md │ ├── 15to16.md │ └── FAQ.md └── docs/ ├── 12to13.html ├── 15to16.html ├── Classes/ │ ├── OnAckCallback.html │ ├── SSLSecurity.html │ ├── SocketAckEmitter.html │ ├── SocketAnyEvent.html │ ├── SocketClientManager.html │ ├── SocketEngine.html │ ├── SocketIOClient.html │ ├── SocketManager.html │ ├── SocketRawAckView.html │ └── SocketRawView.html ├── Classes.html ├── Enums/ │ ├── SocketAckStatus.html │ ├── SocketClientEvent.html │ ├── SocketEnginePacketType.html │ ├── SocketIOClientOption.html │ ├── SocketIOClientStatus.html │ ├── SocketIOStatus.html │ ├── SocketIOVersion.html │ └── SocketParsableError.html ├── Enums.html ├── Extensions.html ├── Guides.html ├── Protocols/ │ ├── ConfigSettable.html │ ├── SocketData.html │ ├── SocketDataBufferable.html │ ├── SocketEngineClient.html │ ├── SocketEnginePollable.html │ ├── SocketEngineSpec.html │ ├── SocketEngineWebsocket.html │ ├── SocketIOClientSpec.html │ ├── SocketLogger.html │ ├── SocketManagerSpec.html │ └── SocketParsable.html ├── Protocols.html ├── Structs/ │ ├── SocketEventHandler.html │ ├── SocketIOClientConfiguration.html │ ├── SocketPacket/ │ │ └── PacketType.html │ └── SocketPacket.html ├── Structs.html ├── Typealiases.html ├── css/ │ ├── highlight.css │ └── jazzy.css ├── faq.html ├── index.html ├── js/ │ ├── jazzy.js │ ├── jazzy.search.js │ └── typeahead.jquery.js └── search.json ================================================ FILE CONTENTS ================================================ ================================================ FILE: .gitignore ================================================ .DS_Store .AppleDouble .LSOverride *.xcodeproj .build/* Packages/* # Icon must end with two \r Icon # Thumbnails ._* # Files that might appear in the root of a volume .DocumentRevisions-V100 .fseventsd .Spotlight-V100 .TemporaryItems .Trashes .VolumeIcon.icns # Directories potentially created on remote AFP share .AppleDB .AppleDesktop Network Trash Folder Temporary Items .apdisk # Xcode build/ *.pbxuser !default.pbxuser *.mode1v3 !default.mode1v3 *.mode2v3 !default.mode2v3 *.perspectivev3 !default.perspectivev3 xcuserdata *.xccheckout *.moved-aside DerivedData *.hmap *.ipa *.xcuserstate Socket.IO-Test-Server/node_modules/* .idea/ docs/docsets/ docs/undocumented.json ================================================ FILE: .travis.yml ================================================ language: objective-c xcode_project: Socket.IO-Client-Swift.xcodeproj # path to your xcodeproj folder xcode_scheme: SocketIO-Mac osx_image: xcode12.2 branches: only: - master - development before_install: # - brew update # - brew outdated xctool || brew upgrade xctool # - brew outdated carthage || brew upgrade carthage - carthage update --platform macosx script: - xcodebuild -project Socket.IO-Client-Swift.xcodeproj -scheme SocketIO build test -quiet # - xcodebuild -project Socket.IO-Client-Swift.xcodeproj -scheme SocketIO build-for-testing -quiet # - xctool -project Socket.IO-Client-Swift.xcodeproj -scheme SocketIO run-tests --parallelize - swift test ================================================ FILE: CHANGELOG.md ================================================ # v16.0.0 - Removed Objective-C support. It's time for you to embrace Swift. - Socket.io 3 support. # v15.3.0 - Add `==` operators for `SocketAckStatus` and `String` # v15.2.0 - Small fixes. # v15.1.0 - Add ability to enable websockets SOCKS proxy. - Fix emit completion callback not firing on websockets [#1178](https://github.com/socketio/socket.io-client-swift/issues/1178) # v15.0.0 - Swift 5 # v14.0.0 - Minimum version of the client is now Swift 4.2. - Add exponential backoff for reconnects, with `reconnectWaitMax` and `randomizationFactor` options [#1149](https://github.com/socketio/socket.io-client-swift/pull/1149) - `statusChange` event's data format adds a second value, the raw value of the status. This is for use in Objective-C. [#1147](https://github.com/socketio/socket.io-client-swift/issues/1147) # v13.4.0 - Add emits with write completion handlers. [#1096](https://github.com/socketio/socket.io-client-swift/issues/1096) - Add ability to listen for when a websocket upgrade happens # v13.3.1 - Fixes various bugs. [#857](https://github.com/socketio/socket.io-client-swift/issues/857), [#1078](https://github.com/socketio/socket.io-client-swift/issues/1078) # v13.3.0 - Copy cookies from polling to WebSockets ([#1057](https://github.com/socketio/socket.io-client-swift/issues/1057), [#1058](https://github.com/socketio/socket.io-client-swift/issues/1058)) # v13.2.1 - Fix packets getting lost when WebSocket upgrade fails. [#1033](https://github.com/socketio/socket.io-client-swift/issues/1033) - Fix bad unit tests. [#794](https://github.com/socketio/socket.io-client-swift/issues/794) # v13.2.0 - Add ability to bypass Data inspection in emits. [#992]((https://github.com/socketio/socket.io-client-swift/issues/992)) - Allow `SocketEngine` to be subclassed # v13.1.3 - Fix setting reconnectAttempts [#989]((https://github.com/socketio/socket.io-client-swift/issues/989)) # v13.1.2 - Fix [#950](https://github.com/socketio/socket.io-client-swift/issues/950) - Conforming to `SocketEngineWebsocket` no longer requires conforming to `WebsocketDelegate` # v13.1.1 - Fix [#923](https://github.com/socketio/socket.io-client-swift/issues/923) - Fix [#894](https://github.com/socketio/socket.io-client-swift/issues/894) # v13.1.0 - Allow setting `SocketEngineSpec.extraHeaders` after init. - Deprecate `SocketEngineSpec.websocket` in favor of just using the `SocketEngineSpec.polling` property. - Enable bitcode for most platforms. - Fix [#882](https://github.com/socketio/socket.io-client-swift/issues/882). This adds a new method `SocketManger.removeSocket(_:)` that should be called if when you no longer wish to use a socket again. This will cause the engine to no longer keep a strong reference to the socket and no longer track it. # v13.0.1 - Fix not setting handleQueue on `SocketManager` # v13.0.0 Checkout out the migration guide in Usage Docs for a more detailed guide on how to migrate to this version. What's new: --- - Adds a new `SocketManager` class that multiplexes multiple namespaces through a single engine. - Adds `.sentPing` and `.gotPong` client events for tracking ping/pongs. - watchOS support. Important API changes --- - Many properties that were previously on `SocketIOClient` have been moved to the `SocketManager`. - `SocketIOClientOption.nsp` has been removed. Use `SocketManager.socket(forNamespace:)` to create/get a socket attached to a specific namespace. - Adds `.sentPing` and `.gotPong` client events for tracking ping/pongs. - Makes the framework a single target. - Updates Starscream to 3.0 ================================================ FILE: Cartfile ================================================ github "daltoniam/Starscream" ~> 4.0 ================================================ FILE: Cartfile.resolved ================================================ github "daltoniam/Starscream" "4.0.4" ================================================ FILE: LICENSE ================================================ The MIT License (MIT) Copyright (c) 2014-2015 Erik Little 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. This library makes use of the following third party libraries: Starscream ---------- Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 1. Definitions. "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: (a) You must give any other recipients of the Work or Derivative Works a copy of this License; and (b) You must cause any modified files to carry prominent notices stating that You changed the files; and (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and (d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. END OF TERMS AND CONDITIONS APPENDIX: How to apply the Apache License to your work. To apply the Apache License to your work, attach the following boilerplate notice, with the fields enclosed by brackets "{}" replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a file or class name and description of purpose be included on the same "printed page" as the copyright notice for easier identification within third-party archives. Copyright {yyyy} {name of copyright owner} Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. ================================================ FILE: Package.resolved ================================================ { "object": { "pins": [ { "package": "Starscream", "repositoryURL": "https://github.com/daltoniam/Starscream", "state": { "branch": null, "revision": "df8d82047f6654d8e4b655d1b1525c64e1059d21", "version": "4.0.4" } } ] }, "version": 1 } ================================================ FILE: Package.swift ================================================ // swift-tools-version:5.3 import PackageDescription let package = Package( name: "SocketIO", products: [ .library(name: "SocketIO", targets: ["SocketIO"]) ], dependencies: [ .package(url: "https://github.com/daltoniam/Starscream", .upToNextMinor(from: "4.0.0")), ], targets: [ .target(name: "SocketIO", dependencies: ["Starscream"]), .testTarget(name: "TestSocketIO", dependencies: ["SocketIO"]), ] ) ================================================ FILE: README.md ================================================ [![Build Status](https://travis-ci.org/socketio/socket.io-client-swift.svg?branch=master)](https://travis-ci.org/socketio/socket.io-client-swift) # Socket.IO-Client-Swift Socket.IO-client for iOS/OS X. ## Example ```swift import SocketIO let manager = SocketManager(socketURL: URL(string: "http://localhost:8080")!, config: [.log(true), .compress]) let socket = manager.defaultSocket socket.on(clientEvent: .connect) {data, ack in print("socket connected") } socket.on("currentAmount") {data, ack in guard let cur = data[0] as? Double else { return } socket.emitWithAck("canUpdate", cur).timingOut(after: 0) {data in if data.first as? String ?? "passed" == SocketAckValue.noAck { // Handle ack timeout } socket.emit("update", ["amount": cur + 2.50]) } ack.with("Got your currentAmount", "dude") } socket.connect() ``` ## Features - Supports socket.io 2.0+/3.0+. - Supports Binary - Supports Polling and WebSockets - Supports TLS/SSL ## FAQS Checkout the [FAQs](https://nuclearace.github.io/Socket.IO-Client-Swift/faq.html) for commonly asked questions. Checkout the [12to13](https://nuclearace.github.io/Socket.IO-Client-Swift/12to13.html) guide for migrating to v13+ from v12 below. Checkout the [15to16](https://nuclearace.github.io/Socket.IO-Client-Swift/15to16.html) guide for migrating to v16+ from v15. ## Installation Requires Swift 4/5 and Xcode 10.x ### Swift Package Manager Add the project as a dependency to your Package.swift: ```swift // swift-tools-version:4.2 import PackageDescription let package = Package( name: "socket.io-test", products: [ .executable(name: "socket.io-test", targets: ["YourTargetName"]) ], dependencies: [ .package(url: "https://github.com/socketio/socket.io-client-swift", .upToNextMinor(from: "15.0.0")) ], targets: [ .target(name: "YourTargetName", dependencies: ["SocketIO"], path: "./Path/To/Your/Sources") ] ) ``` Then import `import SocketIO`. ### Carthage Add this line to your `Cartfile`: ``` github "socketio/socket.io-client-swift" ~> 15.2.0 ``` Run `carthage update --platform ios,macosx`. Add the `Starscream` and `SocketIO` frameworks to your projects and follow the usual Carthage process. ### CocoaPods 1.0.0 or later Create `Podfile` and add `pod 'Socket.IO-Client-Swift'`: ```ruby use_frameworks! target 'YourApp' do pod 'Socket.IO-Client-Swift', '~> 15.2.0' end ``` Install pods: ``` $ pod install ``` Import the module: Swift: ```swift import SocketIO ``` Objective-C: ```Objective-C @import SocketIO; ``` # [Docs](https://nuclearace.github.io/Socket.IO-Client-Swift/index.html) - [Client](https://nuclearace.github.io/Socket.IO-Client-Swift/Classes/SocketIOClient.html) - [Manager](https://nuclearace.github.io/Socket.IO-Client-Swift/Classes/SocketManager.html) - [Engine](https://nuclearace.github.io/Socket.IO-Client-Swift/Classes/SocketEngine.html) - [Options](https://nuclearace.github.io/Socket.IO-Client-Swift/Enums/SocketIOClientOption.html) ## Detailed Example A more detailed example can be found [here](https://github.com/nuclearace/socket.io-client-swift-example) An example using the Swift Package Manager can be found [here](https://github.com/nuclearace/socket.io-client-swift-spm-example) ## License MIT ================================================ FILE: Socket.IO-Client-Swift.podspec ================================================ Pod::Spec.new do |s| s.name = "Socket.IO-Client-Swift" s.module_name = "SocketIO" s.version = "16.0.1" s.summary = "Socket.IO-client for iOS and OS X" s.description = <<-DESC Socket.IO-client for iOS and OS X. Supports ws/wss/polling connections and binary. For socket.io 3.0+ and Swift. DESC s.homepage = "https://github.com/socketio/socket.io-client-swift" s.license = { :type => 'MIT' } s.author = { "Erik" => "nuclear.ace@gmail.com" } s.ios.deployment_target = '10.0' s.osx.deployment_target = '10.13' s.tvos.deployment_target = '10.0' s.watchos.deployment_target = '5.0' s.requires_arc = true s.source = { :git => "https://github.com/socketio/socket.io-client-swift.git", :tag => 'v16.0.1', :submodules => true } s.swift_version = "5" s.pod_target_xcconfig = { 'SWIFT_VERSION' => '5.0' } s.source_files = "Source/SocketIO/**/*.swift", "Source/SocketIO/*.swift" s.dependency "Starscream", "~> 4.0" end ================================================ FILE: SocketIO/Info.plist ================================================ CFBundleDevelopmentRegion en CFBundleExecutable $(EXECUTABLE_NAME) CFBundleIdentifier $(PRODUCT_BUNDLE_IDENTIFIER) CFBundleInfoDictionaryVersion 6.0 CFBundleName $(PRODUCT_NAME) CFBundlePackageType FMWK CFBundleShortVersionString 1.0 CFBundleSignature ???? CFBundleVersion $(CURRENT_PROJECT_VERSION) NSPrincipalClass ================================================ FILE: SocketIO/SocketIO.h ================================================ // // SocketIO-Mac.h // SocketIO-Mac // // Created by Nacho Soto on 7/11/15. // // #import //! Project version number for SocketIO-Mac. FOUNDATION_EXPORT double SocketIO_MacVersionNumber; //! Project version string for SocketIO-Mac. FOUNDATION_EXPORT const unsigned char SocketIO_MacVersionString[]; // In this header, you should import all the public headers of your framework using statements like #import ================================================ FILE: Source/SocketIO/Ack/SocketAckEmitter.swift ================================================ // // SocketAckEmitter.swift // Socket.IO-Client-Swift // // Created by Erik Little on 9/16/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 Dispatch import Foundation /// A class that represents a waiting ack call. /// /// **NOTE**: You should not store this beyond the life of the event handler. public final class SocketAckEmitter : NSObject { private unowned let socket: SocketIOClient private let ackNum: Int /// A view into this emitter where emits do not check for binary data. /// /// Usage: /// /// ```swift /// ack.rawEmitView.with(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 = SocketRawAckView(socket: socket, ackNum: ackNum) // MARK: Properties /// If true, this handler is expecting to be acked. Call `with(_: SocketData...)` to ack. public var expected: Bool { return ackNum != -1 } // MARK: Initializers /// Creates a new `SocketAckEmitter`. /// /// - parameter socket: The socket for this emitter. /// - parameter ackNum: The ack number for this emitter. public init(socket: SocketIOClient, ackNum: Int) { self.socket = socket self.ackNum = ackNum } // MARK: Methods /// 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.emitAck(ackNum, with: try items.map({ try $0.socketRepresentation() })) } 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.emitAck(ackNum, with: items) } } /// A class that represents an emit that will request an ack that has not yet been sent. /// Call `timingOut(after:callback:)` to complete the emit /// Example: /// /// ```swift /// socket.emitWithAck("myEvent").timingOut(after: 1) {data in /// ... /// } /// ``` public final class OnAckCallback : NSObject { private let ackNumber: Int private let binary: Bool private let items: [Any] private weak var socket: SocketIOClient? init(ackNumber: Int, items: [Any], socket: SocketIOClient, binary: Bool = true) { self.ackNumber = ackNumber self.items = items self.socket = socket self.binary = binary } /// :nodoc: deinit { DefaultSocketLogger.Logger.log("OnAckCallback for \(ackNumber) being released", type: "OnAckCallback") } // MARK: Methods /// Completes an emitWithAck. If this isn't called, the emit never happens. /// /// - parameter seconds: The number of seconds before this emit times out if an ack hasn't been received. /// - parameter callback: The callback called when an ack is received, or when a timeout happens. /// To check for timeout, use `SocketAckStatus`'s `noAck` case. @objc public func timingOut(after seconds: Double, callback: @escaping AckCallback) { guard let socket = self.socket, ackNumber != -1 else { return } socket.ackHandlers.addAck(ackNumber, callback: callback) socket.emit(items, ack: ackNumber, binary: binary) guard seconds != 0 else { return } socket.manager?.handleQueue.asyncAfter(deadline: DispatchTime.now() + seconds) {[weak socket] in guard let socket = socket else { return } socket.ackHandlers.timeoutAck(self.ackNumber) } } } ================================================ FILE: Source/SocketIO/Ack/SocketAckManager.swift ================================================ // // SocketAckManager.swift // Socket.IO-Client-Swift // // Created by Erik Little on 4/3/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 Dispatch import Foundation /// The status of an ack. public enum SocketAckStatus : String { // MARK: Cases /// The ack timed out. case noAck = "NO ACK" /// Tests whether a string is equal to a given SocketAckStatus public static func == (lhs: String, rhs: SocketAckStatus) -> Bool { return lhs == rhs.rawValue } /// Tests whether a string is equal to a given SocketAckStatus public static func == (lhs: SocketAckStatus, rhs: String) -> Bool { return rhs == lhs } } private struct SocketAck : Hashable { let ack: Int var callback: AckCallback! init(ack: Int) { self.ack = ack } init(ack: Int, callback: @escaping AckCallback) { self.ack = ack self.callback = callback } func hash(into hasher: inout Hasher) { ack.hash(into: &hasher) } fileprivate static func <(lhs: SocketAck, rhs: SocketAck) -> Bool { return lhs.ack < rhs.ack } fileprivate static func ==(lhs: SocketAck, rhs: SocketAck) -> Bool { return lhs.ack == rhs.ack } } class SocketAckManager { private var acks = Set(minimumCapacity: 1) func addAck(_ ack: Int, callback: @escaping AckCallback) { acks.insert(SocketAck(ack: ack, callback: callback)) } /// Should be called on handle queue func executeAck(_ ack: Int, with items: [Any]) { acks.remove(SocketAck(ack: ack))?.callback(items) } /// Should be called on handle queue func timeoutAck(_ ack: Int) { acks.remove(SocketAck(ack: ack))?.callback?([SocketAckStatus.noAck.rawValue]) } } ================================================ FILE: Source/SocketIO/Client/SocketAnyEvent.swift ================================================ // // 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 } } ================================================ FILE: Source/SocketIO/Client/SocketEventHandler.swift ================================================ // // 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)) } } ================================================ FILE: Source/SocketIO/Client/SocketIOClient.swift ================================================ // // 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 `/`. public let nsp: String /// 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. 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. public private(set) lazy var rawEmitView = SocketRawView(socket: self) /// The status of this client. public private(set) var status = SocketIOStatus.notConnected { didSet { handleClientEvent(.statusChange, data: [status, status.rawValue]) } } /// The id of this socket.io connect. This is different from the sid of the engine.io connection. public private(set) var sid: String? let ackHandlers = SocketAckManager() var connectPayload: [String: Any]? 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. public init(manager: SocketManagerSpec, nsp: String) { self.manager = manager self.nsp = nsp super.init() } /// :nodoc: 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. /// /// - parameter withPayload: An optional payload sent on connect open func connect(withPayload payload: [String: Any]? = nil) { connect(withPayload: payload, 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 withPayload: An optional payload sent on connect /// - 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. open func connect(withPayload payload: [String: Any]? = nil, 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(withPayload: payload) switch manager.version { case .three: break case .two where manager.status == .connected && nsp == "/": // We might not get a connect event for the default nsp, fire immediately didConnect(toNamespace: nsp, payload: nil) return case _: break } 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, payload: [String: Any]?) { guard status != .connected else { return } DefaultSocketLogger.Logger.log("Socket connected", type: logType) status = .connected sid = payload?["sid"] as? String handleClientEvent(.connect, data: payload == nil ? [namespace] : [namespace, payload!]) } /// 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 sid = "" 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`. open func disconnect() { DefaultSocketLogger.Logger.log("Closing socket", type: logType) leaveNamespace() } /// Send an event to the server, with optional data items and optional write completion handler. /// /// 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. /// - parameter completion: Callback called on transport write completion. open func emit(_ event: String, _ items: SocketData..., completion: (() -> ())? = nil) { emit(event, with: items, completion: completion) } /// Send an event to the server, with optional data items and optional write completion handler. /// /// 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. /// - parameter completion: Callback called on transport write completion. open func emit(_ event: String, with items: [SocketData], completion: (() -> ())?) { do { emit([event] + (try items.map({ try $0.socketRepresentation() })), completion: completion) } catch { DefaultSocketLogger.Logger.error("Error creating socketRepresentation for emit: \(event), \(items)", type: logType) handleClientEvent(.error, data: [event, items, error]) } } /// 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 { emitWithAck(event, with: 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, with items: [SocketData]) -> OnAckCallback { do { return createOnAck([event] + (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) } } func emit(_ data: [Any], ack: Int? = nil, binary: Bool = true, isAck: Bool = false, completion: (() -> ())? = nil ) { // wrap the completion handler so it always runs async via handlerQueue let wrappedCompletion: (() -> ())? = (completion == nil) ? nil : {[weak self] in guard let this = self else { return } this.manager?.handleQueue.async { completion!() } } guard status == .connected else { wrappedCompletion?() 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, completion: wrappedCompletion) } /// 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. 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. 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, payload: packet.data.isEmpty ? nil : packet.data[0] as? [String: Any]) 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. open func leaveNamespace() { manager?.disconnectSocket(self) } /// Joins `nsp`. You shouldn't need to call this directly, instead call `connect`. /// /// - parameter withPayload: An optional payload sent on connect open func joinNamespace(withPayload payload: [String: Any]? = nil) { DefaultSocketLogger.Logger.log("Joining namespace \(nsp)", type: logType) connectPayload = payload manager?.connectSocket(self, withPayload: connectPayload) } /// 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. 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. 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. @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. @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. open func onAny(_ handler: @escaping (SocketAnyEvent) -> ()) { anyHandler = handler } /// Tries to reconnect to the server. @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. 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. 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) } } ================================================ FILE: Source/SocketIO/Client/SocketIOClientConfiguration.swift ================================================ // // 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.Index /// Iterator type. public typealias Iterator = Array.Iterator /// SubSequence type. public typealias SubSequence = Array.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) -> 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.. 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) /// If passed `true`, the WebSocket stream will be configured with the enableSOCKSProxy `true`. case enableSOCKSProxy(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 minimum number of seconds to wait before reconnect attempts. case reconnectWait(Int) /// The maximum number of seconds to wait before reconnect attempts. case reconnectWaitMax(Int) /// The randomization factor for calculating reconnect jitter. case randomizationFactor(Double) /// 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(CertificatePinning) /// 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) /// The version of socket.io being used. This should match the server version. Default is 3. case version(SocketIOVersion) // 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 .reconnectWaitMax: description = "reconnectWaitMax" case .randomizationFactor: description = "randomizationFactor" case .secure: description = "secure" case .selfSigned: description = "selfSigned" case .security: description = "security" case .sessionDelegate: description = "sessionDelegate" case .enableSOCKSProxy: description = "enableSOCKSProxy" case .version: description = "version" } 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 .reconnectWaitMax(wait): value = wait case let .randomizationFactor(factor): value = factor case let .secure(secure): value = secure case let .security(security): value = security case let .selfSigned(signed): value = signed case let .sessionDelegate(delegate): value = delegate case let .enableSOCKSProxy(enable): value = enable case let.version(versionNum): value = versionNum } 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 } } ================================================ FILE: Source/SocketIO/Client/SocketIOClientSpec.swift ================================================ // // 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 id of this socket.io connect. This is different from the sid of the engine.io connection. var sid: String? { 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. /// /// - parameter payload: An optional payload sent on connect func connect(withPayload payload: [String: Any]?) /// 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 withPayload: An optional payload sent on connect /// - 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(withPayload payload: [String: Any]?, 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, payload: [String: Any]?) /// 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 and optional write completion handler. /// /// 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. /// - parameter completion: Callback called on transport write completion. func emit(_ event: String, _ items: SocketData..., completion: (() -> ())?) /// Send an event to the server, with optional data items and optional write completion handler. /// /// 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. /// - parameter completion: Callback called on transport write completion. func emit(_ event: String, with items: [SocketData], completion: (() -> ())?) /// 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 /// 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, with 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`. You shouldn't need to call this directly, instead call `connect`. /// /// - Parameter withPayload: The payload to connect when joining this namespace func joinNamespace(withPayload payload: [String: Any]?) /// 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. 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. /// /// The payload for data is [SocketIOClientStatus, Int]. Where the second item is the raw value. Use the second one /// if you are working in Objective-C. /// /// Usage: /// /// ```swift /// socket.on(clientEvent: .statusChange) {data, ack in /// // Some status changing logging /// } /// ``` case statusChange /// Emitted when when upgrading the http connection to a websocket connection. /// /// Usage: /// /// ```swift /// socket.on(clientEvent: .websocketUpgrade) {data, ack in /// let headers = (data as [Any])[0] /// // Some header logic /// } /// ``` case websocketUpgrade } ================================================ FILE: Source/SocketIO/Client/SocketIOStatus.swift ================================================ // // 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" } } } ================================================ FILE: Source/SocketIO/Client/SocketRawView.swift ================================================ // // 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) } } ================================================ FILE: Source/SocketIO/Engine/SocketEngine.swift ================================================ // // SocketEngine.swift // Socket.IO-Client-Swift // // Created by Erik Little on 3/3/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 Dispatch import Foundation import Starscream /// The class that handles the engine.io protocol and transports. /// See `SocketEnginePollable` and `SocketEngineWebsocket` for transport specific methods. open class SocketEngine: NSObject, WebSocketDelegate, URLSessionDelegate, SocketEnginePollable, SocketEngineWebsocket, ConfigSettable { // MARK: Properties private static let logType = "SocketEngine" /// The queue that all engine actions take place on. public let engineQueue = DispatchQueue(label: "com.socketio.engineHandleQueue") /// The connect parameters sent during a connect. public var connectParams: [String: Any]? { didSet { (urlPolling, urlWebSocket) = createURLs() } } /// A dictionary of extra http headers that will be set during connection. public var extraHeaders: [String: String]? /// A queue of engine.io messages waiting for POSTing /// /// **You should not touch this directly** public var postWait = [Post]() /// `true` if there is an outstanding poll. Trying to poll before the first is done will cause socket.io to /// disconnect us. /// /// **Do not touch this directly** public var waitingForPoll = false /// `true` if there is an outstanding post. Trying to post before the first is done will cause socket.io to /// disconnect us. /// /// **Do not touch this directly** public var waitingForPost = false /// `true` if this engine is closed. public private(set) var closed = false /// If `true` the engine will attempt to use WebSocket compression. public private(set) var compress = false /// `true` if this engine is connected. Connected means that the initial poll connect has succeeded. public private(set) var connected = false /// An array of HTTPCookies that are sent during the connection. public private(set) var cookies: [HTTPCookie]? /// When `true`, the engine is in the process of switching to WebSockets. /// /// **Do not touch this directly** public private(set) var fastUpgrade = false /// When `true`, the engine will only use HTTP long-polling as a transport. public private(set) var forcePolling = false /// When `true`, the engine will only use WebSockets as a transport. public private(set) var forceWebsockets = false /// `true` If engine's session has been invalidated. public private(set) var invalidated = false /// If `true`, the engine is currently in HTTP long-polling mode. public private(set) var polling = true /// If `true`, the engine is currently seeing whether it can upgrade to WebSockets. public private(set) var probing = false /// The URLSession that will be used for polling. public private(set) var session: URLSession? /// The session id for this engine. public private(set) var sid = "" /// The path to engine.io. public private(set) var socketPath = "/engine.io/" /// The url for polling. public private(set) var urlPolling = URL(string: "http://localhost/")! /// The url for WebSockets. public private(set) var urlWebSocket = URL(string: "http://localhost/")! /// The version of engine.io being used. Default is three. public private(set) var version: SocketIOVersion = .three /// If `true`, then the engine is currently in WebSockets mode. @available(*, deprecated, message: "No longer needed, if we're not polling, then we must be doing websockets") public private(set) var websocket = false /// When `true`, the WebSocket `stream` will be configured with the enableSOCKSProxy `true`. public private(set) var enableSOCKSProxy = false /// The WebSocket for this engine. public private(set) var ws: WebSocket? /// Whether or not the WebSocket is currently connected. public private(set) var wsConnected = false /// The client for this engine. public weak var client: SocketEngineClient? private weak var sessionDelegate: URLSessionDelegate? private let url: URL private var lastCommunication: Date? private var pingInterval: Int? private var pingTimeout = 0 { didSet { pongsMissedMax = Int(pingTimeout / (pingInterval ?? 25000)) } } private var pongsMissed = 0 private var pongsMissedMax = 0 private var probeWait = ProbeWaitQueue() private var secure = false private var certPinner: CertificatePinning? private var selfSigned = false // MARK: Initializers /// Creates a new engine. /// /// - parameter client: The client for this engine. /// - parameter url: The url for this engine. /// - parameter config: An array of configuration options for this engine. public init(client: SocketEngineClient, url: URL, config: SocketIOClientConfiguration) { self.client = client self.url = url super.init() setConfigs(config) sessionDelegate = sessionDelegate ?? self (urlPolling, urlWebSocket) = createURLs() } /// Creates a new engine. /// /// - parameter client: The client for this engine. /// - parameter url: The url for this engine. /// - parameter options: The options for this engine. public required convenience init(client: SocketEngineClient, url: URL, options: [String: Any]?) { self.init(client: client, url: url, config: options?.toSocketConfiguration() ?? []) } /// :nodoc: deinit { DefaultSocketLogger.Logger.log("Engine is being released", type: SocketEngine.logType) closed = true stopPolling() } // MARK: Methods private func checkAndHandleEngineError(_ msg: String) { do { let dict = try msg.toDictionary() guard let error = dict["message"] as? String else { return } /* 0: Unknown transport 1: Unknown sid 2: Bad handshake request 3: Bad request */ didError(reason: error) } catch { client?.engineDidError(reason: "Got unknown error from server \(msg)") } } private func handleBase64(message: String) { let offset = version.rawValue >= 3 ? 1 : 2 // binary in base64 string let noPrefix = String(message[message.index(message.startIndex, offsetBy: offset).. (URL, URL) { if client == nil { return (URL(string: "http://localhost/")!, URL(string: "http://localhost/")!) } var urlPolling = URLComponents(string: url.absoluteString)! var urlWebSocket = URLComponents(string: url.absoluteString)! var queryString = "" urlWebSocket.path = socketPath urlPolling.path = socketPath if secure { urlPolling.scheme = "https" urlWebSocket.scheme = "wss" } else { urlPolling.scheme = "http" urlWebSocket.scheme = "ws" } if let connectParams = self.connectParams { for (key, value) in connectParams { let keyEsc = key.urlEncode()! let valueEsc = "\(value)".urlEncode()! queryString += "&\(keyEsc)=\(valueEsc)" } } urlWebSocket.percentEncodedQuery = "transport=websocket" + queryString urlPolling.percentEncodedQuery = "transport=polling&b64=1" + queryString if !urlWebSocket.percentEncodedQuery!.contains("EIO") { urlWebSocket.percentEncodedQuery = urlWebSocket.percentEncodedQuery! + engineIOParam } if !urlPolling.percentEncodedQuery!.contains("EIO") { urlPolling.percentEncodedQuery = urlPolling.percentEncodedQuery! + engineIOParam } return (urlPolling.url!, urlWebSocket.url!) } private func createWebSocketAndConnect() { var req = URLRequest(url: urlWebSocketWithSid) addHeaders( to: &req, includingCookies: session?.configuration.httpCookieStorage?.cookies(for: urlPollingWithSid) ) ws = WebSocket(request: req, certPinner: certPinner, compressionHandler: compress ? WSCompression() : nil) ws?.callbackQueue = engineQueue ws?.delegate = self ws?.connect() } /// Called when an error happens during execution. Causes a disconnection. open func didError(reason: String) { DefaultSocketLogger.Logger.error("\(reason)", type: SocketEngine.logType) client?.engineDidError(reason: reason) disconnect(reason: reason) } /// Disconnects from the server. /// /// - parameter reason: The reason for the disconnection. This is communicated up to the client. open func disconnect(reason: String) { engineQueue.async { self._disconnect(reason: reason) } } private func _disconnect(reason: String) { guard connected && !closed else { return closeOutEngine(reason: reason) } DefaultSocketLogger.Logger.log("Engine is being closed.", type: SocketEngine.logType) if polling { disconnectPolling(reason: reason) } else { sendWebSocketMessage("", withType: .close, withData: [], completion: nil) closeOutEngine(reason: reason) } } // We need to take special care when we're polling that we send it ASAP // Also make sure we're on the emitQueue since we're touching postWait private func disconnectPolling(reason: String) { postWait.append((String(SocketEnginePacketType.close.rawValue), {})) doRequest(for: createRequestForPostWithPostWait()) {_, _, _ in } closeOutEngine(reason: reason) } /// Called to switch from HTTP long-polling to WebSockets. After calling this method the engine will be in /// WebSocket mode. /// /// **You shouldn't call this directly** open func doFastUpgrade() { if waitingForPoll { DefaultSocketLogger.Logger.error("Outstanding poll when switched to WebSockets," + "we'll probably disconnect soon. You should report this.", type: SocketEngine.logType) } DefaultSocketLogger.Logger.log("Switching to WebSockets", type: SocketEngine.logType) sendWebSocketMessage("", withType: .upgrade, withData: [], completion: nil) polling = false fastUpgrade = false probing = false flushProbeWait() // Need to flush postWait to socket since it connected successfully // moved from flushProbeWait() since it is also called on connected failure, and we don't want to try and send // packets through WebSockets when WebSockets has failed! if !postWait.isEmpty { flushWaitingForPostToWebSocket() } } private func flushProbeWait() { DefaultSocketLogger.Logger.log("Flushing probe wait", type: SocketEngine.logType) for waiter in probeWait { write(waiter.msg, withType: waiter.type, withData: waiter.data, completion: waiter.completion) } probeWait.removeAll(keepingCapacity: false) } /// Causes any packets that were waiting for POSTing to be sent through the WebSocket. This happens because when /// the engine is attempting to upgrade to WebSocket it does not do any POSTing. /// /// **You shouldn't call this directly** open func flushWaitingForPostToWebSocket() { guard let ws = self.ws else { return } for msg in postWait { ws.write(string: msg.msg, completion: msg.completion) } postWait.removeAll(keepingCapacity: false) } private func handleClose(_ reason: String) { client?.engineDidClose(reason: reason) } private func handleMessage(_ message: String) { client?.parseEngineMessage(message) } private func handleNOOP() { doPoll() } private func handleOpen(openData: String) { guard let json = try? openData.toDictionary() else { didError(reason: "Error parsing open packet") return } guard let sid = json["sid"] as? String else { didError(reason: "Open packet contained no sid") return } let upgradeWs: Bool self.sid = sid connected = true pongsMissed = 0 if let upgrades = json["upgrades"] as? [String] { upgradeWs = upgrades.contains("websocket") } else { upgradeWs = false } if let pingInterval = json["pingInterval"] as? Int, let pingTimeout = json["pingTimeout"] as? Int { self.pingInterval = pingInterval self.pingTimeout = pingTimeout } if !forcePolling && !forceWebsockets && upgradeWs { createWebSocketAndConnect() } if version.rawValue >= 3 { checkPings() } else { sendPing() } if !forceWebsockets { doPoll() } client?.engineDidOpen(reason: "Connect") } private func handlePong(with message: String) { pongsMissed = 0 // We should upgrade if message == "3probe" { DefaultSocketLogger.Logger.log("Received probe response, should upgrade to WebSockets", type: SocketEngine.logType) upgradeTransport() } client?.engineDidReceivePong() } private func handlePing(with message: String) { if version.rawValue >= 3 { write("", withType: .pong, withData: []) } client?.engineDidReceivePing() } private func checkPings() { let pingInterval = self.pingInterval ?? 25_000 let deadlineMs = Double(pingInterval + pingTimeout) / 1000 let timeoutDeadline = DispatchTime.now() + .milliseconds(pingInterval + pingTimeout) engineQueue.asyncAfter(deadline: timeoutDeadline) {[weak self, id = self.sid] in // Make sure not to ping old connections guard let this = self, this.sid == id else { return } if abs(this.lastCommunication?.timeIntervalSinceNow ?? deadlineMs) >= deadlineMs { this.closeOutEngine(reason: "Ping timeout") } else { this.checkPings() } } } /// Parses raw binary received from engine.io. /// /// - parameter data: The data to parse. open func parseEngineData(_ data: Data) { DefaultSocketLogger.Logger.log("Got binary data: \(data)", type: SocketEngine.logType) lastCommunication = Date() client?.parseEngineBinaryData(version.rawValue >= 3 ? data : data.subdata(in: 1..= 3 ? "b" : "b4") { return handleBase64(message: message) } guard let type = SocketEnginePacketType(rawValue: message.first?.wholeNumberValue ?? -1) else { checkAndHandleEngineError(message) return } switch type { case .message: handleMessage(String(message.dropFirst())) case .noop: handleNOOP() case .ping: handlePing(with: message) case .pong: handlePong(with: message) case .open: handleOpen(openData: String(message.dropFirst())) case .close: handleClose(message) default: DefaultSocketLogger.Logger.log("Got unknown packet type", type: SocketEngine.logType) } } // Puts the engine back in its default state private func resetEngine() { let queue = OperationQueue() queue.underlyingQueue = engineQueue closed = false connected = false fastUpgrade = false polling = true probing = false invalidated = false session = Foundation.URLSession(configuration: .default, delegate: sessionDelegate, delegateQueue: queue) sid = "" waitingForPoll = false waitingForPost = false } private func sendPing() { guard connected, let pingInterval = pingInterval else { return } // Server is not responding if pongsMissed > pongsMissedMax { closeOutEngine(reason: "Ping timeout") return } pongsMissed += 1 write("", withType: .ping, withData: [], completion: nil) engineQueue.asyncAfter(deadline: .now() + .milliseconds(pingInterval)) {[weak self, id = self.sid] in // Make sure not to ping old connections guard let this = self, this.sid == id else { return } this.sendPing() } client?.engineDidSendPing() } /// Called when the engine should set/update its configs from a given configuration. /// /// parameter config: The `SocketIOClientConfiguration` that should be used to set/update configs. open func setConfigs(_ config: SocketIOClientConfiguration) { for option in config { switch option { case let .connectParams(params): connectParams = params case let .cookies(cookies): self.cookies = cookies case let .extraHeaders(headers): extraHeaders = headers case let .sessionDelegate(delegate): sessionDelegate = delegate case let .forcePolling(force): forcePolling = force case let .forceWebsockets(force): forceWebsockets = force case let .path(path): socketPath = path if !socketPath.hasSuffix("/") { socketPath += "/" } case let .secure(secure): self.secure = secure case let .selfSigned(selfSigned): self.selfSigned = selfSigned case let .security(pinner): self.certPinner = pinner case .compress: self.compress = true case .enableSOCKSProxy: self.enableSOCKSProxy = true case let .version(num): version = num default: continue } } } // Moves from long-polling to websockets private func upgradeTransport() { if wsConnected { DefaultSocketLogger.Logger.log("Upgrading transport to WebSockets", type: SocketEngine.logType) fastUpgrade = true sendPollMessage("", withType: .noop, withData: [], completion: nil) // After this point, we should not send anymore polling messages } } /// Writes a message to engine.io, independent of transport. /// /// - parameter msg: The message to send. /// - parameter type: The type of this message. /// - parameter data: Any data that this message has. /// - parameter completion: Callback called on transport write completion. open func write(_ msg: String, withType type: SocketEnginePacketType, withData data: [Data], completion: (() -> ())? = nil) { engineQueue.async { guard self.connected else { completion?() return } guard !self.probing else { self.probeWait.append((msg, type, data, completion)) return } if self.polling { DefaultSocketLogger.Logger.log("Writing poll: \(msg) has data: \(data.count != 0)", type: SocketEngine.logType) self.sendPollMessage(msg, withType: type, withData: data, completion: completion) } else { DefaultSocketLogger.Logger.log("Writing ws: \(msg) has data: \(data.count != 0)", type: SocketEngine.logType) self.sendWebSocketMessage(msg, withType: type, withData: data, completion: completion) } } } // WebSocket Methods private func websocketDidConnect() { if !forceWebsockets { probing = true probeWebSocket() } else { connected = true probing = false polling = false } } private func websocketDidDisconnect(error: Error?) { probing = false if closed { client?.engineDidClose(reason: "Disconnect") return } guard !polling else { flushProbeWait() return } connected = false polling = true if let error = error as? WSError { didError(reason: "\(error.message). code=\(error.code), type=\(error.type)") } else if let reason = error?.localizedDescription { didError(reason: reason) } else { client?.engineDidClose(reason: "Socket Disconnected") } } // Test Properties func setConnected(_ value: Bool) { connected = value } } extension SocketEngine { // MARK: URLSessionDelegate methods /// Delegate called when the session becomes invalid. public func URLSession(session: URLSession, didBecomeInvalidWithError error: NSError?) { DefaultSocketLogger.Logger.error("Engine URLSession became invalid", type: "SocketEngine") didError(reason: "Engine URLSession became invalid") } } enum EngineError: Error { case canceled } extension SocketEngine { /// Delegate method for WebSocketDelegate. /// /// - Parameters: /// - event: WS Event /// - _: public func didReceive(event: WebSocketEvent, client _: WebSocket) { switch event { case let .connected(headers): wsConnected = true client?.engineDidWebsocketUpgrade(headers: headers) websocketDidConnect() case .cancelled: wsConnected = false websocketDidDisconnect(error: EngineError.canceled) case let .disconnected(reason, code): wsConnected = false websocketDidDisconnect(error: nil) case let .text(msg): parseEngineMessage(msg) case let .binary(data): parseEngineData(data) case _: break } } } ================================================ FILE: Source/SocketIO/Engine/SocketEngineClient.swift ================================================ // // SocketEngineClient.swift // Socket.IO-Client-Swift // // Created by Erik Little on 3/19/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 /// Declares that a type will be a delegate to an engine. @objc public protocol SocketEngineClient { // MARK: Methods /// Called when the engine errors. /// /// - parameter reason: The reason the engine errored. func engineDidError(reason: String) /// Called when the engine closes. /// /// - parameter reason: The reason that the engine closed. func engineDidClose(reason: String) /// Called when the engine opens. /// /// - parameter reason: The reason the engine opened. func engineDidOpen(reason: String) /// Called when the engine receives a ping message. Only called in socket.io >3. func engineDidReceivePing() /// Called when the engine receives a pong message. Only called in socket.io 2. func engineDidReceivePong() /// Called when the engine sends a ping to the server. Only called in socket.io 2. func engineDidSendPing() /// Called when the engine sends a pong to the server. Only called in socket.io >3. func engineDidSendPong() /// Called when the engine has a message that must be parsed. /// /// - parameter msg: The message that needs parsing. func parseEngineMessage(_ msg: String) /// Called when the engine receives binary data. /// /// - parameter data: The data the engine received. func parseEngineBinaryData(_ data: Data) /// Called when when upgrading the http connection to a websocket connection. /// /// - parameter headers: The http headers. func engineDidWebsocketUpgrade(headers: [String: String]) } ================================================ FILE: Source/SocketIO/Engine/SocketEnginePacketType.swift ================================================ // // SocketEnginePacketType.swift // Socket.IO-Client-Swift // // Created by Erik Little on 10/7/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 the type of engine.io packet types. @objc public enum SocketEnginePacketType: Int { /// Open message. case open /// Close message. case close /// Ping message. case ping /// Pong message. case pong /// Regular message. case message /// Upgrade message. case upgrade /// NOOP. case noop } ================================================ FILE: Source/SocketIO/Engine/SocketEnginePollable.swift ================================================ // // SocketEnginePollable.swift // Socket.IO-Client-Swift // // Created by Erik Little on 1/15/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 Foundation /// Protocol that is used to implement socket.io polling support public protocol SocketEnginePollable: SocketEngineSpec { // MARK: Properties /// `true` If engine's session has been invalidated. var invalidated: Bool { get } /// A queue of engine.io messages waiting for POSTing /// /// **You should not touch this directly** var postWait: [Post] { get set } /// The URLSession that will be used for polling. var session: URLSession? { get } /// `true` if there is an outstanding poll. Trying to poll before the first is done will cause socket.io to /// disconnect us. /// /// **Do not touch this directly** var waitingForPoll: Bool { get set } /// `true` if there is an outstanding post. Trying to post before the first is done will cause socket.io to /// disconnect us. /// /// **Do not touch this directly** var waitingForPost: Bool { get set } // MARK: Methods /// Call to send a long-polling request. /// /// You shouldn't need to call this directly, the engine should automatically maintain a long-poll request. func doPoll() /// Sends an engine.io message through the polling transport. /// /// You shouldn't call this directly, instead call the `write` method on `SocketEngine`. /// /// - parameter message: The message to send. /// - parameter withType: The type of message to send. /// - parameter withData: The data associated with this message. func sendPollMessage(_ message: String, withType type: SocketEnginePacketType, withData datas: [Data], completion: (() -> ())?) /// Call to stop polling and invalidate the URLSession. func stopPolling() } // Default polling methods extension SocketEnginePollable { func createRequestForPostWithPostWait() -> URLRequest { defer { for packet in postWait { packet.completion?() } postWait.removeAll(keepingCapacity: true) } var postStr = "" if version.rawValue >= 3 { postStr = postWait.lazy.map({ $0.msg }).joined(separator: "\u{1e}") } else { for packet in postWait { postStr += "\(packet.msg.utf16.count):\(packet.msg)" } } DefaultSocketLogger.Logger.log("Created POST string: \(postStr)", type: "SocketEnginePolling") var req = URLRequest(url: urlPollingWithSid) let postData = postStr.data(using: .utf8, allowLossyConversion: false)! addHeaders(to: &req) req.httpMethod = "POST" req.setValue("text/plain; charset=UTF-8", forHTTPHeaderField: "Content-Type") req.httpBody = postData req.setValue(String(postData.count), forHTTPHeaderField: "Content-Length") return req } /// Call to send a long-polling request. /// /// You shouldn't need to call this directly, the engine should automatically maintain a long-poll request. public func doPoll() { guard polling && !waitingForPoll && connected && !closed else { return } var req = URLRequest(url: urlPollingWithSid) addHeaders(to: &req) doLongPoll(for: req) } func doRequest(for req: URLRequest, callbackWith callback: @escaping (Data?, URLResponse?, Error?) -> ()) { guard polling && !closed && !invalidated && !fastUpgrade else { return } DefaultSocketLogger.Logger.log("Doing polling \(req.httpMethod ?? "") \(req)", type: "SocketEnginePolling") session?.dataTask(with: req, completionHandler: callback).resume() } func doLongPoll(for req: URLRequest) { waitingForPoll = true doRequest(for: req) {[weak self] data, res, err in guard let this = self, this.polling else { return } guard let data = data, let res = res as? HTTPURLResponse, res.statusCode == 200 else { if let err = err { DefaultSocketLogger.Logger.error(err.localizedDescription, type: "SocketEnginePolling") } else { DefaultSocketLogger.Logger.error("Error during long poll request", type: "SocketEnginePolling") } if this.polling { this.didError(reason: err?.localizedDescription ?? "Error") } return } DefaultSocketLogger.Logger.log("Got polling response", type: "SocketEnginePolling") if let str = String(data: data, encoding: .utf8) { this.parsePollingMessage(str) } this.waitingForPoll = false if this.fastUpgrade { this.doFastUpgrade() } else if !this.closed && this.polling { this.doPoll() } } } private func flushWaitingForPost() { guard postWait.count != 0 && connected else { return } guard polling else { flushWaitingForPostToWebSocket() return } let req = createRequestForPostWithPostWait() waitingForPost = true DefaultSocketLogger.Logger.log("POSTing", type: "SocketEnginePolling") doRequest(for: req) {[weak self] _, res, err in guard let this = self else { return } guard let res = res as? HTTPURLResponse, res.statusCode == 200 else { if let err = err { DefaultSocketLogger.Logger.error(err.localizedDescription, type: "SocketEnginePolling") } else { DefaultSocketLogger.Logger.error("Error flushing waiting posts", type: "SocketEnginePolling") } if this.polling { this.didError(reason: err?.localizedDescription ?? "Error") } return } this.waitingForPost = false if !this.fastUpgrade { this.flushWaitingForPost() this.doPoll() } } } func parsePollingMessage(_ str: String) { guard !str.isEmpty else { return } DefaultSocketLogger.Logger.log("Got poll message: \(str)", type: "SocketEnginePolling") if version.rawValue >= 3 { let records = str.components(separatedBy: "\u{1e}") for record in records { parseEngineMessage(record) } } else { guard str.count != 1 else { parseEngineMessage(str) return } var reader = SocketStringReader(message: str) while reader.hasNext { if let n = Int(reader.readUntilOccurence(of: ":")) { parseEngineMessage(reader.read(count: n)) } else { parseEngineMessage(str) break } } } } /// Sends an engine.io message through the polling transport. /// /// You shouldn't call this directly, instead call the `write` method on `SocketEngine`. /// /// - parameter message: The message to send. /// - parameter withType: The type of message to send. /// - parameter withData: The data associated with this message. /// - parameter completion: Callback called on transport write completion. public func sendPollMessage(_ message: String, withType type: SocketEnginePacketType, withData datas: [Data], completion: (() -> ())? = nil) { DefaultSocketLogger.Logger.log("Sending poll: \(message) as type: \(type.rawValue)", type: "SocketEnginePolling") postWait.append((String(type.rawValue) + message, completion)) for data in datas { if case let .right(bin) = createBinaryDataForSend(using: data) { postWait.append((bin, {})) } } if !waitingForPost { flushWaitingForPost() } } /// Call to stop polling and invalidate the URLSession. public func stopPolling() { waitingForPoll = false waitingForPost = false session?.finishTasksAndInvalidate() } } ================================================ FILE: Source/SocketIO/Engine/SocketEngineSpec.swift ================================================ // // SocketEngineSpec.swift // Socket.IO-Client-Swift // // Created by Erik Little on 10/7/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 /// Specifies a SocketEngine. public protocol SocketEngineSpec: class { // MARK: Properties /// The client for this engine. var client: SocketEngineClient? { get set } /// `true` if this engine is closed. var closed: Bool { get } /// If `true` the engine will attempt to use WebSocket compression. var compress: Bool { get } /// `true` if this engine is connected. Connected means that the initial poll connect has succeeded. var connected: Bool { get } /// The connect parameters sent during a connect. var connectParams: [String: Any]? { get set } /// An array of HTTPCookies that are sent during the connection. var cookies: [HTTPCookie]? { get } /// The queue that all engine actions take place on. var engineQueue: DispatchQueue { get } /// A dictionary of extra http headers that will be set during connection. var extraHeaders: [String: String]? { get set } /// When `true`, the engine is in the process of switching to WebSockets. var fastUpgrade: Bool { get } /// When `true`, the engine will only use HTTP long-polling as a transport. var forcePolling: Bool { get } /// When `true`, the engine will only use WebSockets as a transport. var forceWebsockets: Bool { get } /// If `true`, the engine is currently in HTTP long-polling mode. var polling: Bool { get } /// If `true`, the engine is currently seeing whether it can upgrade to WebSockets. var probing: Bool { get } /// The session id for this engine. var sid: String { get } /// The path to engine.io. var socketPath: String { get } /// The url for polling. var urlPolling: URL { get } /// The url for WebSockets. var urlWebSocket: URL { get } /// The version of engine.io being used. Default is three. var version: SocketIOVersion { get } /// If `true`, then the engine is currently in WebSockets mode. @available(*, deprecated, message: "No longer needed, if we're not polling, then we must be doing websockets") var websocket: Bool { get } /// The WebSocket for this engine. var ws: WebSocket? { get } // MARK: Initializers /// Creates a new engine. /// /// - parameter client: The client for this engine. /// - parameter url: The url for this engine. /// - parameter options: The options for this engine. init(client: SocketEngineClient, url: URL, options: [String: Any]?) // MARK: Methods /// Starts the connection to the server. func connect() /// Called when an error happens during execution. Causes a disconnection. func didError(reason: String) /// Disconnects from the server. /// /// - parameter reason: The reason for the disconnection. This is communicated up to the client. func disconnect(reason: String) /// Called to switch from HTTP long-polling to WebSockets. After calling this method the engine will be in /// WebSocket mode. /// /// **You shouldn't call this directly** func doFastUpgrade() /// Causes any packets that were waiting for POSTing to be sent through the WebSocket. This happens because when /// the engine is attempting to upgrade to WebSocket it does not do any POSTing. /// /// **You shouldn't call this directly** func flushWaitingForPostToWebSocket() /// Parses raw binary received from engine.io. /// /// - parameter data: The data to parse. func parseEngineData(_ data: Data) /// Parses a raw engine.io packet. /// /// - parameter message: The message to parse. func parseEngineMessage(_ message: String) /// Writes a message to engine.io, independent of transport. /// /// - parameter msg: The message to send. /// - parameter type: The type of this message. /// - parameter data: Any data that this message has. /// - parameter completion: Callback called on transport write completion. func write(_ msg: String, withType type: SocketEnginePacketType, withData data: [Data], completion: (() -> ())?) } extension SocketEngineSpec { var engineIOParam: String { switch version { case .two: return "&EIO=3" case .three: return "&EIO=4" } } var urlPollingWithSid: URL { var com = URLComponents(url: urlPolling, resolvingAgainstBaseURL: false)! com.percentEncodedQuery = com.percentEncodedQuery! + "&sid=\(sid.urlEncode()!)" if !com.percentEncodedQuery!.contains("EIO") { com.percentEncodedQuery = com.percentEncodedQuery! + engineIOParam } return com.url! } var urlWebSocketWithSid: URL { var com = URLComponents(url: urlWebSocket, resolvingAgainstBaseURL: false)! com.percentEncodedQuery = com.percentEncodedQuery! + (sid == "" ? "" : "&sid=\(sid.urlEncode()!)") if !com.percentEncodedQuery!.contains("EIO") { com.percentEncodedQuery = com.percentEncodedQuery! + engineIOParam } return com.url! } func addHeaders(to req: inout URLRequest, includingCookies additionalCookies: [HTTPCookie]? = nil) { var cookiesToAdd: [HTTPCookie] = cookies ?? [] cookiesToAdd += additionalCookies ?? [] if !cookiesToAdd.isEmpty { req.allHTTPHeaderFields = HTTPCookie.requestHeaderFields(with: cookiesToAdd) } if let extraHeaders = extraHeaders { for (headerName, value) in extraHeaders { req.setValue(value, forHTTPHeaderField: headerName) } } } func createBinaryDataForSend(using data: Data) -> Either { let prefixB64 = version.rawValue >= 3 ? "b" : "b4" if polling { return .right(prefixB64 + data.base64EncodedString(options: Data.Base64EncodingOptions(rawValue: 0))) } else { return .left(version.rawValue >= 3 ? data : Data([0x4]) + data) } } /// Send an engine message (4) func send(_ msg: String, withData datas: [Data], completion: (() -> ())? = nil) { write(msg, withType: .message, withData: datas, completion: completion) } } ================================================ FILE: Source/SocketIO/Engine/SocketEngineWebsocket.swift ================================================ // // SocketEngineWebsocket.swift // Socket.IO-Client-Swift // // Created by Erik Little on 1/15/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 Foundation import Starscream /// Protocol that is used to implement socket.io WebSocket support public protocol SocketEngineWebsocket: SocketEngineSpec { // MARK: Properties /// Whether or not the ws is connected var wsConnected: Bool { get } // MARK: Methods /// Sends an engine.io message through the WebSocket transport. /// /// You shouldn't call this directly, instead call the `write` method on `SocketEngine`. /// /// - parameter message: The message to send. /// - parameter withType: The type of message to send. /// - parameter withData: The data associated with this message. /// - parameter completion: Callback called on transport write completion. func sendWebSocketMessage(_ str: String, withType type: SocketEnginePacketType, withData datas: [Data], completion: (() -> ())?) } // WebSocket methods extension SocketEngineWebsocket { func probeWebSocket() { if wsConnected { sendWebSocketMessage("probe", withType: .ping, withData: [], completion: nil) } } /// Sends an engine.io message through the WebSocket transport. /// /// You shouldn't call this directly, instead call the `write` method on `SocketEngine`. /// /// - parameter message: The message to send. /// - parameter withType: The type of message to send. /// - parameter withData: The data associated with this message. /// - parameter completion: Callback called on transport write completion. public func sendWebSocketMessage(_ str: String, withType type: SocketEnginePacketType, withData data: [Data], completion: (() -> ())? ) { DefaultSocketLogger.Logger.log("Sending ws: \(str) as type: \(type.rawValue)", type: "SocketEngineWebSocket") ws?.write(string: "\(type.rawValue)\(str)") for item in data { if case let .left(bin) = createBinaryDataForSend(using: item) { ws?.write(data: bin, completion: completion) } } if data.count == 0 { completion?() } } } ================================================ FILE: Source/SocketIO/Manager/SocketManager.swift ================================================ // // Created by Erik Little on 10/14/17. // // 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 /// /// A manager for a socket.io connection. /// /// A `SocketManager` is responsible for multiplexing multiple namespaces through a single `SocketEngineSpec`. /// /// Example: /// /// ```swift /// let manager = SocketManager(socketURL: URL(string:"http://localhost:8080/")!) /// let defaultNamespaceSocket = manager.defaultSocket /// let swiftSocket = manager.socket(forNamespace: "/swift") /// /// // defaultNamespaceSocket and swiftSocket both share a single connection to the server /// ``` /// /// Sockets created through the manager are retained by the manager. So at the very least, a single strong reference /// to the manager must be maintained to keep sockets alive. /// /// To disconnect a socket and remove it from the manager, either call `SocketIOClient.disconnect()` on the socket, /// or call one of the `disconnectSocket` methods on this class. /// /// **NOTE**: The manager is not thread/queue safe, all interaction with the manager should be done on the `handleQueue` /// open class SocketManager: NSObject, SocketManagerSpec, SocketParsable, SocketDataBufferable, ConfigSettable { private static let logType = "SocketManager" // MARK: Properties /// The socket associated with the default namespace ("/"). public var defaultSocket: SocketIOClient { return socket(forNamespace: "/") } /// The URL of the socket.io server. /// /// If changed after calling `init`, `forceNew` must be set to `true`, or it will only connect to the url set in the /// init. public let socketURL: URL /// The configuration for this client. /// /// **Some configs will not take affect until after a reconnect if set after calling a connect method**. public var config: SocketIOClientConfiguration { get { return _config } set { if status.active { DefaultSocketLogger.Logger.log("Setting configs on active manager. Some configs may not be applied until reconnect", type: SocketManager.logType) } setConfigs(newValue) } } /// The engine for this manager. public var engine: SocketEngineSpec? /// If `true` then every time `connect` is called, a new engine will be created. public var forceNew = false /// 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**. public var handleQueue = DispatchQueue.main /// The sockets in this manager indexed by namespace. public var nsps = [String: SocketIOClient]() /// If `true`, this client will try and reconnect on any disconnects. public var reconnects = true /// The minimum number of seconds to wait before attempting to reconnect. public var reconnectWait = 10 /// The maximum number of seconds to wait before attempting to reconnect. public var reconnectWaitMax = 30 /// The randomization factor for calculating reconnect jitter. public var randomizationFactor = 0.5 /// The status of this manager. public private(set) var status: SocketIOStatus = .notConnected { didSet { switch status { case .connected: reconnecting = false currentReconnectAttempt = 0 default: break } } } public private(set) var version = SocketIOVersion.three /// A list of packets that are waiting for binary data. /// /// The way that socket.io works all data should be sent directly after each packet. /// So this should ideally be an array of one packet waiting for data. /// /// **This should not be modified directly.** public var waitingPackets = [SocketPacket]() private(set) var reconnectAttempts = -1 private var _config: SocketIOClientConfiguration private var currentReconnectAttempt = 0 private var reconnecting = false // MARK: Initializers /// Type safe way to create a new SocketIOClient. `opts` can be omitted. /// /// - parameter socketURL: The url of the socket.io server. /// - parameter config: The config for this socket. public init(socketURL: URL, config: SocketIOClientConfiguration = []) { self._config = config self.socketURL = socketURL super.init() setConfigs(_config) } /// Not so type safe way to create a SocketIOClient, meant for Objective-C compatiblity. /// If using Swift it's recommended to use `init(socketURL: NSURL, options: Set)` /// /// - parameter socketURL: The url of the socket.io server. /// - parameter config: The config for this socket. @objc public convenience init(socketURL: URL, config: [String: Any]?) { self.init(socketURL: socketURL, config: config?.toSocketConfiguration() ?? []) } /// :nodoc: deinit { DefaultSocketLogger.Logger.log("Manager is being released", type: SocketManager.logType) engine?.disconnect(reason: "Manager Deinit") } // MARK: Methods private func addEngine() { DefaultSocketLogger.Logger.log("Adding engine", type: SocketManager.logType) engine?.engineQueue.sync { self.engine?.client = nil // Close old engine so it will not leak because of URLSession if in polling mode self.engine?.disconnect(reason: "Adding new engine") } engine = SocketEngine(client: self, url: socketURL, config: config) } /// Connects the underlying transport and the default namespace socket. /// /// Override if you wish to attach a custom `SocketEngineSpec`. open func connect() { guard !status.active else { DefaultSocketLogger.Logger.log("Tried connecting an already active socket", type: SocketManager.logType) return } if engine == nil || forceNew { addEngine() } status = .connecting engine?.connect() } /// Connects a socket through this manager's engine. /// /// - parameter socket: The socket who we should connect through this manager. /// - parameter withPayload: Optional payload to send on connect open func connectSocket(_ socket: SocketIOClient, withPayload payload: [String: Any]? = nil) { guard status == .connected else { DefaultSocketLogger.Logger.log("Tried connecting socket when engine isn't open. Connecting", type: SocketManager.logType) connect() return } var payloadStr = "" if version.rawValue >= 3 && payload != nil, let payloadData = try? JSONSerialization.data(withJSONObject: payload!, options: .fragmentsAllowed), let jsonString = String(data: payloadData, encoding: .utf8) { payloadStr = jsonString } engine?.send("0\(socket.nsp),\(payloadStr)", withData: []) } /// Called when the manager has disconnected from socket.io. /// /// - parameter reason: The reason for the disconnection. open func didDisconnect(reason: String) { forAll {socket in socket.didDisconnect(reason: reason) } } /// Disconnects the manager and all associated sockets. open func disconnect() { DefaultSocketLogger.Logger.log("Manager closing", type: SocketManager.logType) status = .disconnected engine?.disconnect(reason: "Disconnect") } /// Disconnects the given socket. /// /// This will remove the socket for the manager's control, and make the socket instance useless and ready for /// releasing. /// /// - parameter socket: The socket to disconnect. open func disconnectSocket(_ socket: SocketIOClient) { engine?.send("1\(socket.nsp),", withData: []) socket.didDisconnect(reason: "Namespace leave") } /// Disconnects the socket associated with `forNamespace`. /// /// This will remove the socket for the manager's control, and make the socket instance useless and ready for /// releasing. /// /// - parameter nsp: The namespace to disconnect from. open func disconnectSocket(forNamespace nsp: String) { guard let socket = nsps.removeValue(forKey: nsp) else { DefaultSocketLogger.Logger.log("Could not find socket for \(nsp) to disconnect", type: SocketManager.logType) return } disconnectSocket(socket) } /// Sends a client event to all sockets in `nsps` /// /// - parameter clientEvent: The event to emit. open func emitAll(clientEvent event: SocketClientEvent, data: [Any]) { forAll {socket in socket.handleClientEvent(event, data: data) } } /// Sends an event to the server on all namespaces in this manager. /// /// - parameter event: The event to send. /// - parameter items: The data to send with this event. open func emitAll(_ event: String, _ items: SocketData...) { guard let emitData = try? items.map({ try $0.socketRepresentation() }) else { DefaultSocketLogger.Logger.error("Error creating socketRepresentation for emit: \(event), \(items)", type: SocketManager.logType) return } forAll {socket in socket.emit([event] + emitData) } } /// Called when the engine closes. /// /// - parameter reason: The reason that the engine closed. open func engineDidClose(reason: String) { handleQueue.async { self._engineDidClose(reason: reason) } } private func _engineDidClose(reason: String) { waitingPackets.removeAll() if status != .disconnected { status = .notConnected } if status == .disconnected || !reconnects { didDisconnect(reason: reason) } else if !reconnecting { reconnecting = true tryReconnect(reason: reason) } } /// Called when the engine errors. /// /// - parameter reason: The reason the engine errored. open func engineDidError(reason: String) { handleQueue.async { self._engineDidError(reason: reason) } } private func _engineDidError(reason: String) { DefaultSocketLogger.Logger.error("\(reason)", type: SocketManager.logType) emitAll(clientEvent: .error, data: [reason]) } /// Called when the engine opens. /// /// - parameter reason: The reason the engine opened. open func engineDidOpen(reason: String) { handleQueue.async { self._engineDidOpen(reason: reason) } } private func _engineDidOpen(reason: String) { DefaultSocketLogger.Logger.log("Engine opened \(reason)", type: SocketManager.logType) status = .connected if version.rawValue < 3 { nsps["/"]?.didConnect(toNamespace: "/", payload: nil) } for (nsp, socket) in nsps where socket.status == .connecting { if version.rawValue < 3 && nsp == "/" { continue } connectSocket(socket, withPayload: socket.connectPayload) } } /// Called when the engine receives a ping message. open func engineDidReceivePing() { handleQueue.async { self._engineDidReceivePing() } } private func _engineDidReceivePing() { emitAll(clientEvent: .ping, data: []) } /// Called when the sends a ping to the server. open func engineDidSendPing() { handleQueue.async { self._engineDidSendPing() } } private func _engineDidSendPing() { emitAll(clientEvent: .ping, data: []) } /// Called when the engine receives a pong message. open func engineDidReceivePong() { handleQueue.async { self._engineDidReceivePong() } } private func _engineDidReceivePong() { emitAll(clientEvent: .pong, data: []) } /// Called when the sends a pong to the server. open func engineDidSendPong() { handleQueue.async { self._engineDidSendPong() } } private func _engineDidSendPong() { emitAll(clientEvent: .pong, data: []) } private func forAll(do: (SocketIOClient) throws -> ()) rethrows { for (_, socket) in nsps { try `do`(socket) } } /// Called when when upgrading the http connection to a websocket connection. /// /// - parameter headers: The http headers. open func engineDidWebsocketUpgrade(headers: [String: String]) { handleQueue.async { self._engineDidWebsocketUpgrade(headers: headers) } } private func _engineDidWebsocketUpgrade(headers: [String: String]) { emitAll(clientEvent: .websocketUpgrade, data: [headers]) } /// Called when the engine has a message that must be parsed. /// /// - parameter msg: The message that needs parsing. open func parseEngineMessage(_ msg: String) { handleQueue.async { self._parseEngineMessage(msg) } } private func _parseEngineMessage(_ msg: String) { guard let packet = parseSocketMessage(msg) else { return } guard !packet.type.isBinary else { waitingPackets.append(packet) return } nsps[packet.nsp]?.handlePacket(packet) } /// Called when the engine receives binary data. /// /// - parameter data: The data the engine received. open func parseEngineBinaryData(_ data: Data) { handleQueue.async { self._parseEngineBinaryData(data) } } private func _parseEngineBinaryData(_ data: Data) { guard let packet = parseBinaryData(data) else { return } nsps[packet.nsp]?.handlePacket(packet) } /// Tries to reconnect to the server. /// /// This will cause a `SocketClientEvent.reconnect` event to be emitted, as well as /// `SocketClientEvent.reconnectAttempt` events. open func reconnect() { guard !reconnecting else { return } engine?.disconnect(reason: "manual reconnect") } /// Removes the socket from the manager's control. One of the disconnect methods should be called before calling this /// method. /// /// After calling this method the socket should no longer be considered usable. /// /// - parameter socket: The socket to remove. /// - returns: The socket removed, if it was owned by the manager. @discardableResult open func removeSocket(_ socket: SocketIOClient) -> SocketIOClient? { return nsps.removeValue(forKey: socket.nsp) } private func tryReconnect(reason: String) { guard reconnecting else { return } DefaultSocketLogger.Logger.log("Starting reconnect", type: SocketManager.logType) // Set status to connecting and emit reconnect for all sockets forAll {socket in guard socket.status == .connected else { return } socket.setReconnecting(reason: reason) } _tryReconnect() } private func _tryReconnect() { guard reconnects && reconnecting && status != .disconnected else { return } if reconnectAttempts != -1 && currentReconnectAttempt + 1 > reconnectAttempts { return didDisconnect(reason: "Reconnect Failed") } DefaultSocketLogger.Logger.log("Trying to reconnect", type: SocketManager.logType) forAll {socket in guard socket.status == .connecting else { return } socket.handleClientEvent(.reconnectAttempt, data: [(reconnectAttempts - currentReconnectAttempt)]) } currentReconnectAttempt += 1 connect() let interval = reconnectInterval(attempts: currentReconnectAttempt) DefaultSocketLogger.Logger.log("Scheduling reconnect in \(interval)s", type: SocketManager.logType) handleQueue.asyncAfter(deadline: .now() + interval, execute: _tryReconnect) } func reconnectInterval(attempts: Int) -> Double { // apply exponential factor let backoffFactor = pow(1.5, attempts) let interval = Double(reconnectWait) * Double(truncating: backoffFactor as NSNumber) // add in a random factor smooth thundering herds let rand = Double.random(in: 0 ..< 1) let randomFactor = rand * randomizationFactor * Double(truncating: interval as NSNumber) // add in random factor, and clamp to min and max values let combined = interval + randomFactor return Double(fmax(Double(reconnectWait), fmin(combined, Double(reconnectWaitMax)))) } /// Sets manager specific configs. /// /// parameter config: The configs that should be set. open func setConfigs(_ config: SocketIOClientConfiguration) { for option in config { switch option { case let .forceNew(new): forceNew = new case let .handleQueue(queue): handleQueue = queue case let .reconnects(reconnects): self.reconnects = reconnects case let .reconnectAttempts(attempts): reconnectAttempts = attempts case let .reconnectWait(wait): reconnectWait = abs(wait) case let .reconnectWaitMax(wait): reconnectWaitMax = abs(wait) case let .randomizationFactor(factor): randomizationFactor = factor case let .log(log): DefaultSocketLogger.Logger.log = log case let .logger(logger): DefaultSocketLogger.Logger = logger case let .version(num): version = num case _: continue } } _config = config if socketURL.absoluteString.hasPrefix("https://") { _config.insert(.secure(true)) } _config.insert(.path("/socket.io/"), replacing: false) // If `ConfigSettable` & `SocketEngineSpec`, update its configs. if var settableEngine = engine as? ConfigSettable & SocketEngineSpec { settableEngine.engineQueue.sync { settableEngine.setConfigs(self._config) } engine = settableEngine } } /// Returns a `SocketIOClient` for the given namespace. This socket shares a transport with the manager. /// /// Calling multiple times returns the same socket. /// /// Sockets created from this method are retained by the manager. /// Call one of the `disconnectSocket` methods on this class to remove the socket from manager control. /// Or call `SocketIOClient.disconnect()` on the client. /// /// - parameter nsp: The namespace for the socket. /// - returns: A `SocketIOClient` for the given namespace. open func socket(forNamespace nsp: String) -> SocketIOClient { assert(nsp.hasPrefix("/"), "forNamespace must have a leading /") if let socket = nsps[nsp] { return socket } let client = SocketIOClient(manager: self, nsp: nsp) nsps[nsp] = client return client } // Test properties func setTestStatus(_ status: SocketIOStatus) { self.status = status } } ================================================ FILE: Source/SocketIO/Manager/SocketManagerSpec.swift ================================================ // // Created by Erik Little on 10/18/17. // // 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 // TODO Fix the types so that we aren't using concrete types /// /// A manager for a socket.io connection. /// /// A `SocketManagerSpec` is responsible for multiplexing multiple namespaces through a single `SocketEngineSpec`. /// /// Example with `SocketManager`: /// /// ```swift /// let manager = SocketManager(socketURL: URL(string:"http://localhost:8080/")!) /// let defaultNamespaceSocket = manager.defaultSocket /// let swiftSocket = manager.socket(forNamespace: "/swift") /// /// // defaultNamespaceSocket and swiftSocket both share a single connection to the server /// ``` /// /// Sockets created through the manager are retained by the manager. So at the very least, a single strong reference /// to the manager must be maintained to keep sockets alive. /// /// To disconnect a socket and remove it from the manager, either call `SocketIOClient.disconnect()` on the socket, /// or call one of the `disconnectSocket` methods on this class. /// public protocol SocketManagerSpec : AnyObject, SocketEngineClient { // MARK: Properties /// Returns the socket associated with the default namespace ("/"). var defaultSocket: SocketIOClient { get } /// The engine for this manager. var engine: SocketEngineSpec? { get set } /// If `true` then every time `connect` is called, a new engine will be created. var forceNew: Bool { get set } // TODO Per socket queues? /// The queue that all interaction with the client should occur on. This is the queue that event handlers are /// called on. var handleQueue: DispatchQueue { get set } /// The sockets in this manager indexed by namespace. var nsps: [String: SocketIOClient] { get set } /// If `true`, this manager will try and reconnect on any disconnects. var reconnects: Bool { get set } /// The minimum number of seconds to wait before attempting to reconnect. var reconnectWait: Int { get set } /// The maximum number of seconds to wait before attempting to reconnect. var reconnectWaitMax: Int { get set } /// The randomization factor for calculating reconnect jitter. var randomizationFactor: Double { get set } /// The URL of the socket.io server. var socketURL: URL { get } /// The status of this manager. var status: SocketIOStatus { get } /// The version of socket.io in use. var version: SocketIOVersion { get } // MARK: Methods /// Connects the underlying transport. func connect() /// Connects a socket through this manager's engine. /// /// - parameter socket: The socket who we should connect through this manager. /// - parameter withPayload: Optional payload to send on connect func connectSocket(_ socket: SocketIOClient, withPayload: [String: Any]?) /// Called when the manager has disconnected from socket.io. /// /// - parameter reason: The reason for the disconnection. func didDisconnect(reason: String) /// Disconnects the manager and all associated sockets. func disconnect() /// Disconnects the given socket. /// /// - parameter socket: The socket to disconnect. func disconnectSocket(_ socket: SocketIOClient) /// Disconnects the socket associated with `forNamespace`. /// /// - parameter nsp: The namespace to disconnect from. func disconnectSocket(forNamespace nsp: String) /// Sends an event to the server on all namespaces in this manager. /// /// - parameter event: The event to send. /// - parameter items: The data to send with this event. func emitAll(_ event: String, _ items: SocketData...) /// Tries to reconnect to the server. /// /// This will cause a `disconnect` event to be emitted, as well as an `reconnectAttempt` event. func reconnect() /// Removes the socket from the manager's control. /// After calling this method the socket should no longer be considered usable. /// /// - parameter socket: The socket to remove. /// - returns: The socket removed, if it was owned by the manager. @discardableResult func removeSocket(_ socket: SocketIOClient) -> SocketIOClient? /// Returns a `SocketIOClient` for the given namespace. This socket shares a transport with the manager. /// /// Calling multiple times returns the same socket. /// /// Sockets created from this method are retained by the manager. /// Call one of the `disconnectSocket` methods on the implementing class to remove the socket from manager control. /// Or call `SocketIOClient.disconnect()` on the client. /// /// - parameter nsp: The namespace for the socket. /// - returns: A `SocketIOClient` for the given namespace. func socket(forNamespace nsp: String) -> SocketIOClient } ================================================ FILE: Source/SocketIO/Parse/SocketPacket.swift ================================================ // // SocketPacket.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 struct that represents a socket.io packet. public struct SocketPacket : CustomStringConvertible { // MARK: Properties private static let logType = "SocketPacket" /// The namespace for this packet. public let nsp: String /// If > 0 then this packet is using acking. public let id: Int /// The type of this packet. public let type: PacketType /// An array of binary data for this packet. public internal(set) var binary: [Data] /// The data for this event. /// /// Note: This includes all data inside of the socket.io packet payload array, which includes the event name for /// event type packets. public internal(set) var data: [Any] /// Returns the payload for this packet, minus the event name if this is an event or binaryEvent type packet. public var args: [Any] { if type == .event || type == .binaryEvent && data.count != 0 { return Array(data.dropFirst()) } else { return data } } private let placeholders: Int /// A string representation of this packet. public var description: String { return "SocketPacket {type: \(String(type.rawValue)); data: " + "\(String(describing: data)); id: \(id); placeholders: \(placeholders); nsp: \(nsp)}" } /// The event name for this packet. public var event: String { return String(describing: data[0]) } /// A string representation of this packet. public var packetString: String { return createPacketString() } init(type: PacketType, data: [Any] = [Any](), id: Int = -1, nsp: String, placeholders: Int = 0, binary: [Data] = [Data]()) { self.data = data self.id = id self.nsp = nsp self.type = type self.placeholders = placeholders self.binary = binary } mutating func addData(_ data: Data) -> Bool { if placeholders == binary.count { return true } binary.append(data) if placeholders == binary.count { fillInPlaceholders() return true } else { return false } } private func completeMessage(_ message: String) -> String { guard data.count != 0 else { return message + "[]" } guard let jsonSend = try? data.toJSON(), let jsonString = String(data: jsonSend, encoding: .utf8) else { DefaultSocketLogger.Logger.error("Error creating JSON object in SocketPacket.completeMessage", type: SocketPacket.logType) return message + "[]" } return message + jsonString } private func createPacketString() -> String { let typeString = String(type.rawValue) // Binary count? let binaryCountString = typeString + (type.isBinary ? "\(String(binary.count))-" : "") // Namespace? let nspString = binaryCountString + (nsp != "/" ? "\(nsp)," : "") // Ack number? let idString = nspString + (id != -1 ? String(id) : "") return completeMessage(idString) } // Called when we have all the binary data for a packet // calls _fillInPlaceholders, which replaces placeholders with the // corresponding binary private mutating func fillInPlaceholders() { data = data.map(_fillInPlaceholders) } // Helper method that looks for placeholders // If object is a collection it will recurse // Returns the object if it is not a placeholder or the corresponding // binary data private func _fillInPlaceholders(_ object: Any) -> Any { switch object { case let dict as JSON: if dict["_placeholder"] as? Bool ?? false { return binary[dict["num"] as! Int] } else { return dict.reduce(into: JSON(), {cur, keyValue in cur[keyValue.0] = _fillInPlaceholders(keyValue.1) }) } case let arr as [Any]: return arr.map(_fillInPlaceholders) default: return object } } } public extension SocketPacket { // MARK: PacketType enum /// The type of packets. enum PacketType: Int { // MARK: Cases /// Connect: 0 case connect /// Disconnect: 1 case disconnect /// Event: 2 case event /// Ack: 3 case ack /// Error: 4 case error /// Binary Event: 5 case binaryEvent /// Binary Ack: 6 case binaryAck // MARK: Properties /// Whether or not this type is binary public var isBinary: Bool { return self == .binaryAck || self == .binaryEvent } } } extension SocketPacket { private static func findType(_ binCount: Int, ack: Bool) -> PacketType { switch binCount { case 0 where !ack: return .event case 0 where ack: return .ack case _ where !ack: return .binaryEvent case _ where ack: return .binaryAck default: return .error } } static func packetFromEmit(_ items: [Any], id: Int, nsp: String, ack: Bool, checkForBinary: Bool = true) -> SocketPacket { if checkForBinary { let (parsedData, binary) = deconstructData(items) return SocketPacket(type: findType(binary.count, ack: ack), data: parsedData, id: id, nsp: nsp, binary: binary) } else { return SocketPacket(type: findType(0, ack: ack), data: items, id: id, nsp: nsp) } } } private extension SocketPacket { // Recursive function that looks for NSData in collections static func shred(_ data: Any, binary: inout [Data]) -> Any { let placeholder = ["_placeholder": true, "num": binary.count] as JSON switch data { case let bin as Data: binary.append(bin) return placeholder case let arr as [Any]: return arr.map({shred($0, binary: &binary)}) case let dict as JSON: return dict.reduce(into: JSON(), {cur, keyValue in cur[keyValue.0] = shred(keyValue.1, binary: &binary) }) default: return data } } // Removes binary data from emit data // Returns a type containing the de-binaryed data and the binary static func deconstructData(_ data: [Any]) -> ([Any], [Data]) { var binary = [Data]() return (data.map({ shred($0, binary: &binary) }), binary) } } ================================================ FILE: Source/SocketIO/Parse/SocketParsable.swift ================================================ // // SocketParsable.swift // Socket.IO-Client-Swift // // 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 /// Defines that a type will be able to parse socket.io-protocol messages. public protocol SocketParsable : AnyObject { // MARK: Methods /// Called when the engine has received some binary data that should be attached to a packet. /// /// Packets binary data should be sent directly after the packet that expects it, so there's confusion over /// where the data should go. Data should be received in the order it is sent, so that the correct data is put /// into the correct placeholder. /// /// - parameter data: The data that should be attached to a packet. func parseBinaryData(_ data: Data) -> SocketPacket? /// Called when the engine has received a string that should be parsed into a socket.io packet. /// /// - parameter message: The string that needs parsing. /// - returns: A completed socket packet if there is no more data left to collect. func parseSocketMessage(_ message: String) -> SocketPacket? } /// Errors that can be thrown during parsing. public enum SocketParsableError : Error { // MARK: Cases /// Thrown when a packet received has an invalid data array, or is missing the data array. case invalidDataArray /// Thrown when an malformed packet is received. case invalidPacket /// Thrown when the parser receives an unknown packet type. case invalidPacketType } /// Says that a type will be able to buffer binary data before all data for an event has come in. public protocol SocketDataBufferable : AnyObject { // MARK: Properties /// A list of packets that are waiting for binary data. /// /// The way that socket.io works all data should be sent directly after each packet. /// So this should ideally be an array of one packet waiting for data. /// /// **This should not be modified directly.** var waitingPackets: [SocketPacket] { get set } } public extension SocketParsable where Self: SocketManagerSpec & SocketDataBufferable { /// Parses a message from the engine, returning a complete SocketPacket or throwing. /// /// - parameter message: The message to parse. /// - returns: A completed packet, or throwing. internal func parseString(_ message: String) throws -> SocketPacket { var reader = SocketStringReader(message: message) guard let type = Int(reader.read(count: 1)).flatMap({ SocketPacket.PacketType(rawValue: $0) }) else { throw SocketParsableError.invalidPacketType } if !reader.hasNext { return SocketPacket(type: type, nsp: "/") } var namespace = "/" var placeholders = -1 if type.isBinary { if let holders = Int(reader.readUntilOccurence(of: "-")) { placeholders = holders } else { throw SocketParsableError.invalidPacket } } if reader.currentCharacter == "/" { namespace = reader.readUntilOccurence(of: ",") } if !reader.hasNext { return SocketPacket(type: type, nsp: namespace, placeholders: placeholders) } var idString = "" if type == .error { reader.advance(by: -1) } else { while let int = Int(reader.read(count: 1)) { idString += String(int) } reader.advance(by: -2) } var dataArray = String(message.utf16[message.utf16.index(reader.currentIndex, offsetBy: 1)...])! if (type == .error || type == .connect) && !dataArray.hasPrefix("[") && !dataArray.hasSuffix("]") { dataArray = "[" + dataArray + "]" } let data = try parseData(dataArray) return SocketPacket(type: type, data: data, id: Int(idString) ?? -1, nsp: namespace, placeholders: placeholders) } // Parses data for events private func parseData(_ data: String) throws -> [Any] { do { return try data.toArray() } catch { throw SocketParsableError.invalidDataArray } } /// Called when the engine has received a string that should be parsed into a socket.io packet. /// /// - parameter message: The string that needs parsing. /// - returns: A completed socket packet or nil if the packet is invalid. func parseSocketMessage(_ message: String) -> SocketPacket? { guard !message.isEmpty else { return nil } DefaultSocketLogger.Logger.log("Parsing \(message)", type: "SocketParser") do { let packet = try parseString(message) DefaultSocketLogger.Logger.log("Decoded packet as: \(packet.description)", type: "SocketParser") return packet } catch { DefaultSocketLogger.Logger.error("\(error): \(message)", type: "SocketParser") return nil } } /// Called when the engine has received some binary data that should be attached to a packet. /// /// Packets binary data should be sent directly after the packet that expects it, so there's confusion over /// where the data should go. Data should be received in the order it is sent, so that the correct data is put /// into the correct placeholder. /// /// - parameter data: The data that should be attached to a packet. /// - returns: A completed socket packet if there is no more data left to collect. func parseBinaryData(_ data: Data) -> SocketPacket? { guard !waitingPackets.isEmpty else { DefaultSocketLogger.Logger.error("Got data when not remaking packet", type: "SocketParser") return nil } // Should execute event? guard waitingPackets[waitingPackets.count - 1].addData(data) else { return nil } return waitingPackets.removeLast() } } ================================================ FILE: Source/SocketIO/Util/SocketExtensions.swift ================================================ // // SocketExtensions.swift // Socket.IO-Client-Swift // // Created by Erik Little on 7/1/2016. // // 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 enum JSONError : Error { case notArray case notNSDictionary } extension Array { func toJSON() throws -> Data { return try JSONSerialization.data(withJSONObject: self, options: JSONSerialization.WritingOptions(rawValue: 0)) } } extension CharacterSet { static var allowedURLCharacterSet: CharacterSet { return CharacterSet(charactersIn: "!*'();:@&=+$,/?%#[]\" {}^|").inverted } } extension Dictionary where Key == String, Value == Any { private static func keyValueToSocketIOClientOption(key: String, value: Any) -> SocketIOClientOption? { switch (key, value) { case let ("connectParams", params as [String: Any]): return .connectParams(params) case let ("cookies", cookies as [HTTPCookie]): return .cookies(cookies) case let ("extraHeaders", headers as [String: String]): return .extraHeaders(headers) case let ("forceNew", force as Bool): return .forceNew(force) case let ("forcePolling", force as Bool): return .forcePolling(force) case let ("forceWebsockets", force as Bool): return .forceWebsockets(force) case let ("handleQueue", queue as DispatchQueue): return .handleQueue(queue) case let ("log", log as Bool): return .log(log) case let ("logger", logger as SocketLogger): return .logger(logger) case let ("path", path as String): return .path(path) case let ("reconnects", reconnects as Bool): return .reconnects(reconnects) case let ("reconnectAttempts", attempts as Int): return .reconnectAttempts(attempts) case let ("reconnectWait", wait as Int): return .reconnectWait(wait) case let ("reconnectWaitMax", wait as Int): return .reconnectWaitMax(wait) case let ("randomizationFactor", factor as Double): return .randomizationFactor(factor) case let ("secure", secure as Bool): return .secure(secure) case let ("security", security as CertificatePinning): return .security(security) case let ("selfSigned", selfSigned as Bool): return .selfSigned(selfSigned) case let ("sessionDelegate", delegate as URLSessionDelegate): return .sessionDelegate(delegate) case let ("compress", compress as Bool): return compress ? .compress : nil case let ("enableSOCKSProxy", enable as Bool): return .enableSOCKSProxy(enable) case let ("version", version as Int): return .version(SocketIOVersion(rawValue: version) ?? .three) case _: return nil } } func toSocketConfiguration() -> SocketIOClientConfiguration { var options = [] as SocketIOClientConfiguration for (rawKey, value) in self { if let opt = Dictionary.keyValueToSocketIOClientOption(key: rawKey, value: value) { options.insert(opt) } } return options } } extension String { func toArray() throws -> [Any] { guard let stringData = data(using: .utf16, allowLossyConversion: false) else { return [] } guard let array = try JSONSerialization.jsonObject(with: stringData, options: .mutableContainers) as? [Any] else { throw JSONError.notArray } return array } func toDictionary() throws -> [String: Any] { guard let binData = data(using: .utf16, allowLossyConversion: false) else { return [:] } guard let json = try JSONSerialization.jsonObject(with: binData, options: .allowFragments) as? [String: Any] else { throw JSONError.notNSDictionary } return json } func urlEncode() -> String? { return addingPercentEncoding(withAllowedCharacters: .allowedURLCharacterSet) } } ================================================ FILE: Source/SocketIO/Util/SocketLogger.swift ================================================ // // SocketLogger.swift // Socket.IO-Client-Swift // // Created by Erik Little on 4/11/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 a class will log client events. public protocol SocketLogger : AnyObject { // MARK: Properties /// Whether to log or not var log: Bool { get set } // MARK: Methods /// Normal log messages /// /// - parameter message: The message being logged. Can include `%@` that will be replaced with `args` /// - parameter type: The type of entity that called for logging. /// - parameter args: Any args that should be inserted into the message. May be left out. func log(_ message: @autoclosure () -> String, type: String) /// Error Messages /// /// - parameter message: The message being logged. Can include `%@` that will be replaced with `args` /// - parameter type: The type of entity that called for logging. /// - parameter args: Any args that should be inserted into the message. May be left out. func error(_ message: @autoclosure () -> String, type: String) } public extension SocketLogger { /// Default implementation. func log(_ message: @autoclosure () -> String, type: String) { guard log else { return } abstractLog("LOG", message: message(), type: type) } /// Default implementation. func error(_ message: @autoclosure () -> String, type: String) { guard log else { return } abstractLog("ERROR", message: message(), type: type) } private func abstractLog(_ logType: String, message: @autoclosure () -> String, type: String) { NSLog("\(logType) \(type): %@", message()) } } class DefaultSocketLogger : SocketLogger { static var Logger: SocketLogger = DefaultSocketLogger() var log = false } ================================================ FILE: Source/SocketIO/Util/SocketStringReader.swift ================================================ // // SocketStringReader.swift // Socket.IO-Client-Swift // // Created by Lukas Schmidt on 07.09.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. struct SocketStringReader { let message: String var currentIndex: String.UTF16View.Index var hasNext: Bool { return currentIndex != message.utf16.endIndex } var currentCharacter: String { return String(UnicodeScalar(message.utf16[currentIndex])!) } init(message: String) { self.message = message currentIndex = message.utf16.startIndex } @discardableResult mutating func advance(by: Int) -> String.UTF16View.Index { currentIndex = message.utf16.index(currentIndex, offsetBy: by) return currentIndex } mutating func read(count: Int) -> String { let readString = String(message.utf16[currentIndex.. String { let substring = message.utf16[currentIndex...] guard let foundIndex = substring.firstIndex(where: { $0 == string.utf16.first! }) else { currentIndex = message.utf16.endIndex return String(substring)! } advance(by: substring.distance(from: substring.startIndex, to: foundIndex) + 1) return String(substring[substring.startIndex.. String { return read(count: message.utf16.distance(from: currentIndex, to: message.utf16.endIndex)) } } ================================================ FILE: Source/SocketIO/Util/SocketTypes.swift ================================================ // // SocketTypes.swift // Socket.IO-Client-Swift // // Created by Erik Little on 4/8/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 marking protocol that says a type can be represented in a socket.io packet. /// /// Example: /// /// ```swift /// struct CustomData : SocketData { /// let name: String /// let age: Int /// /// func socketRepresentation() -> SocketData { /// return ["name": name, "age": age] /// } /// } /// /// socket.emit("myEvent", CustomData(name: "Erik", age: 24)) /// ``` public protocol SocketData { // MARK: Methods /// A representation of self that can sent over socket.io. func socketRepresentation() throws -> SocketData } public extension SocketData { /// Default implementation. Only works for native Swift types and a few Foundation types. func socketRepresentation() -> SocketData { return self } } extension Array : SocketData { } extension Bool : SocketData { } extension Dictionary : SocketData { } extension Double : SocketData { } extension Int : SocketData { } extension NSArray : SocketData { } extension Data : SocketData { } extension NSData : SocketData { } extension NSDictionary : SocketData { } extension NSString : SocketData { } extension NSNull : SocketData { } extension String : SocketData { } /// A typealias for an ack callback. public typealias AckCallback = ([Any]) -> () /// A typealias for a normal callback. public typealias NormalCallback = ([Any], SocketAckEmitter) -> () /// A typealias for a queued POST public typealias Post = (msg: String, completion: (() -> ())?) typealias JSON = [String: Any] typealias Probe = (msg: String, type: SocketEnginePacketType, data: [Data], completion: (() -> ())?) typealias ProbeWaitQueue = [Probe] enum Either { case left(E) case right(V) } ================================================ FILE: Tests/TestSocketIO/SocketAckManagerTest.swift ================================================ // // SocketAckManagerTest.swift // Socket.IO-Client-Swift // // Created by Lukas Schmidt on 04.09.15. // // import XCTest @testable import SocketIO class SocketAckManagerTest : XCTestCase { var ackManager = SocketAckManager() func testAddAcks() { let callbackExpection = expectation(description: "callbackExpection") let itemsArray = ["Hi", "ho"] func callback(_ items: [Any]) { callbackExpection.fulfill() } ackManager.addAck(1, callback: callback) ackManager.executeAck(1, with: itemsArray) waitForExpectations(timeout: 3.0, handler: nil) } func testManagerTimeoutAck() { let callbackExpection = expectation(description: "Manager should timeout ack with noAck status") let itemsArray = ["Hi", "ho"] func callback(_ items: [Any]) { XCTAssertEqual(items.count, 1, "Timed out ack should have one value") guard let timeoutReason = items[0] as? String else { XCTFail("Timeout reason should be a string") return } XCTAssert(timeoutReason == SocketAckStatus.noAck) callbackExpection.fulfill() } ackManager.addAck(1, callback: callback) ackManager.timeoutAck(1) waitForExpectations(timeout: 0.2, handler: nil) } } ================================================ FILE: Tests/TestSocketIO/SocketBasicPacketTest.swift ================================================ // // SocketBasicPacketTest.swift // Socket.IO-Client-Swift // // Created by Erik Little on 10/7/15. // // import XCTest @testable import SocketIO class SocketBasicPacketTest : XCTestCase { func testEmptyEmit() { let sendData = ["test"] let packetStr = SocketPacket.packetFromEmit(sendData, id: -1, nsp: "/", ack: false).packetString let parsed = parser.parseSocketMessage(packetStr)! XCTAssertEqual(parsed.type, .event) XCTAssertTrue(compareAnyArray(input: parsed.data, expected: sendData)) } func testNullEmit() { let sendData: [Any] = ["test", NSNull()] let packetStr = SocketPacket.packetFromEmit(sendData, id: -1, nsp: "/", ack: false).packetString let parsed = parser.parseSocketMessage(packetStr)! XCTAssertEqual(parsed.type, .event) XCTAssertTrue(compareAnyArray(input: parsed.data, expected: sendData)) } func testStringEmit() { let sendData = ["test", "foo bar"] let packetStr = SocketPacket.packetFromEmit(sendData, id: -1, nsp: "/", ack: false).packetString let parsed = parser.parseSocketMessage(packetStr)! XCTAssertEqual(parsed.type, .event) XCTAssertTrue(compareAnyArray(input: parsed.data, expected: sendData)) } func testStringEmitWithQuotes() { let sendData = ["test", "\"he\"llo world\""] let packetStr = SocketPacket.packetFromEmit(sendData, id: -1, nsp: "/", ack: false).packetString let parsed = parser.parseSocketMessage(packetStr)! XCTAssertEqual(parsed.type, .event) XCTAssertTrue(compareAnyArray(input: parsed.data, expected: sendData)) } func testJSONEmit() { let sendData: [Any] = ["test", ["foobar": true, "hello": 1, "test": "hello", "null": NSNull()]] let packetStr = SocketPacket.packetFromEmit(sendData, id: -1, nsp: "/", ack: false).packetString let parsed = parser.parseSocketMessage(packetStr)! XCTAssertEqual(parsed.type, .event) XCTAssertTrue(compareAnyArray(input: parsed.data, expected: sendData)) } func testArrayEmit() { let sendData: [Any] = ["test", ["hello", 1, ["test": "test"]]] let packetStr = SocketPacket.packetFromEmit(sendData, id: -1, nsp: "/", ack: false).packetString let parsed = parser.parseSocketMessage(packetStr)! XCTAssertEqual(parsed.type, .event) XCTAssertTrue(compareAnyArray(input: parsed.data, expected: sendData)) } func testBinaryEmit() { let sendData: [Any] = ["test", data] let packet = SocketPacket.packetFromEmit(sendData, id: -1, nsp: "/", ack: false) let parsed = parser.parseSocketMessage(packet.packetString)! XCTAssertEqual(parsed.type, .binaryEvent) XCTAssertEqual(packet.binary, [data]) XCTAssertTrue(compareAnyArray(input: parsed.data, expected: [ "test", ["_placeholder": true, "num": 0] ])) } func testMultipleBinaryEmit() { let sendData: [Any] = ["test", ["data1": data, "data2": data2] as NSDictionary] let packet = SocketPacket.packetFromEmit(sendData, id: -1, nsp: "/", ack: false) let parsed = parser.parseSocketMessage(packet.packetString)! XCTAssertEqual(parsed.type, .binaryEvent) let binaryObj = parsed.data[1] as! [String: Any] let data1Loc = (binaryObj["data1"] as! [String: Any])["num"] as! Int let data2Loc = (binaryObj["data2"] as! [String: Any])["num"] as! Int XCTAssertEqual(packet.binary[data1Loc], data) XCTAssertEqual(packet.binary[data2Loc], data2) } func testEmitWithAck() { let sendData = ["test"] let packetStr = SocketPacket.packetFromEmit(sendData, id: 0, nsp: "/", ack: false).packetString let parsed = parser.parseSocketMessage(packetStr)! XCTAssertEqual(parsed.type, .event) XCTAssertEqual(parsed.id, 0) XCTAssertTrue(compareAnyArray(input: parsed.data, expected: sendData)) } func testEmitDataWithAck() { let sendData: [Any] = ["test", data] let packet = SocketPacket.packetFromEmit(sendData, id: 0, nsp: "/", ack: false) let parsed = parser.parseSocketMessage(packet.packetString)! XCTAssertEqual(parsed.type, .binaryEvent) XCTAssertEqual(parsed.id, 0) XCTAssertTrue(compareAnyArray(input: parsed.data, expected: [ "test", ["_placeholder": true, "num": 0] ])) XCTAssertEqual(packet.binary, [data]) } // Acks func testEmptyAck() { let packetStr = SocketPacket.packetFromEmit([], id: 0, nsp: "/", ack: true).packetString let parsed = parser.parseSocketMessage(packetStr)! XCTAssertEqual(parsed.type, .ack) XCTAssertEqual(parsed.id, 0) XCTAssertTrue(compareAnyArray(input: parsed.data, expected: [])) } func testNullAck() { let sendData = [NSNull()] let packetStr = SocketPacket.packetFromEmit(sendData, id: 0, nsp: "/", ack: true).packetString let parsed = parser.parseSocketMessage(packetStr)! XCTAssertEqual(parsed.type, .ack) XCTAssertEqual(parsed.id, 0) XCTAssertTrue(compareAnyArray(input: parsed.data, expected: sendData)) } func testStringAck() { let sendData = ["test"] let packetStr = SocketPacket.packetFromEmit(sendData, id: 0, nsp: "/", ack: true).packetString let parsed = parser.parseSocketMessage(packetStr)! XCTAssertEqual(parsed.type, .ack) XCTAssertEqual(parsed.id, 0) XCTAssertTrue(compareAnyArray(input: parsed.data, expected: sendData)) } func testJSONAck() { let sendData = [["foobar": true, "hello": 1, "test": "hello", "null": NSNull()]] let packetStr = SocketPacket.packetFromEmit(sendData, id: 0, nsp: "/", ack: true).packetString let parsed = parser.parseSocketMessage(packetStr)! XCTAssertEqual(parsed.type, .ack) XCTAssertEqual(parsed.id, 0) XCTAssertTrue(compareAnyArray(input: parsed.data, expected: sendData)) } func testBinaryAck() { let sendData = [data] let packet = SocketPacket.packetFromEmit(sendData, id: 0, nsp: "/", ack: true) let parsed = parser.parseSocketMessage(packet.packetString)! XCTAssertEqual(parsed.type, .binaryAck) XCTAssertEqual(packet.binary, [data]) XCTAssertEqual(parsed.id, 0) XCTAssertTrue(compareAnyArray(input: parsed.data, expected: [ ["_placeholder": true, "num": 0] ])) } func testMultipleBinaryAck() { let sendData = [["data1": data, "data2": data2]] let packet = SocketPacket.packetFromEmit(sendData, id: 0, nsp: "/", ack: true) let parsed = parser.parseSocketMessage(packet.packetString)! XCTAssertEqual(parsed.id, 0) XCTAssertEqual(parsed.type, .binaryAck) let binaryObj = parsed.data[0] as! [String: Any] let data1Loc = (binaryObj["data1"] as! [String: Any])["num"] as! Int let data2Loc = (binaryObj["data2"] as! [String: Any])["num"] as! Int XCTAssertEqual(packet.binary[data1Loc], data) XCTAssertEqual(packet.binary[data2Loc], data2) } func testBinaryStringPlaceholderInMessage() { let engineString = "52-[\"test\",\"~~0\",{\"num\":0,\"_placeholder\":true},{\"_placeholder\":true,\"num\":1}]" let manager = SocketManager(socketURL: URL(string: "http://localhost/")!) var packet = try! manager.parseString(engineString) XCTAssertEqual(packet.event, "test") _ = packet.addData(data) _ = packet.addData(data2) XCTAssertEqual(packet.args[0] as? String, "~~0") } private func compareAnyArray(input: [Any], expected: [Any]) -> Bool { guard input.count == expected.count else { return false } return (input as NSArray).isEqual(to: expected) } let data = "test".data(using: String.Encoding.utf8)! let data2 = "test2".data(using: String.Encoding.utf8)! var parser: SocketParsable! override func setUp() { super.setUp() parser = SocketManager(socketURL: URL(string: "http://localhost")!) } } ================================================ FILE: Tests/TestSocketIO/SocketEngineTest.swift ================================================ // // SocketEngineTest.swift // Socket.IO-Client-Swift // // Created by Erik Little on 10/15/15. // // import XCTest @testable import SocketIO class SocketEngineTest: XCTestCase { func testBasicPollingMessageV3() { let expect = expectation(description: "Basic polling test v3") socket.on("blankTest") {data, ack in expect.fulfill() } engine.setConfigs([.version(.two)]) engine.parsePollingMessage("15:42[\"blankTest\"]") waitForExpectations(timeout: 3, handler: nil) } func testBasicPollingMessage() { let expect = expectation(description: "Basic polling test") socket.on("blankTest") {data, ack in expect.fulfill() } engine.parsePollingMessage("42[\"blankTest\"]") waitForExpectations(timeout: 3, handler: nil) } func testTwoPacketsInOnePollTest() { let finalExpectation = expectation(description: "Final packet in poll test") var gotBlank = false socket.on("blankTest") {data, ack in gotBlank = true } socket.on("stringTest") {data, ack in if let str = data[0] as? String, gotBlank { if str == "hello" { finalExpectation.fulfill() } } } engine.parsePollingMessage("42[\"blankTest\"]\u{1e}42[\"stringTest\",\"hello\"]") waitForExpectations(timeout: 3, handler: nil) } func testEngineDoesErrorOnUnknownTransport() { let finalExpectation = expectation(description: "Unknown Transport") socket.on("error") {data, ack in if let error = data[0] as? String, error == "Unknown transport" { finalExpectation.fulfill() } } engine.parseEngineMessage("{\"code\": 0, \"message\": \"Unknown transport\"}") waitForExpectations(timeout: 3, handler: nil) } func testEngineDoesErrorOnUnknownMessage() { let finalExpectation = expectation(description: "Engine Errors") socket.on("error") {data, ack in finalExpectation.fulfill() } engine.parseEngineMessage("afafafda") waitForExpectations(timeout: 3, handler: nil) } func testEngineDecodesUTF8Properly() { let expect = expectation(description: "Engine Decodes utf8") socket.on("stringTest") {data, ack in XCTAssertEqual(data[0] as? String, "lïne one\nlīne \rtwo𦅙𦅛", "Failed string test") expect.fulfill() } let stringMessage = "42[\"stringTest\",\"lïne one\\nlīne \\rtwo𦅙𦅛\"]" engine.parsePollingMessage("\(stringMessage)") waitForExpectations(timeout: 3, handler: nil) } func testEncodeURLProperly() { engine.connectParams = [ "created": "2016-05-04T18:31:15+0200" ] XCTAssertEqual(engine.urlPolling.query, "transport=polling&b64=1&created=2016-05-04T18%3A31%3A15%2B0200&EIO=4") XCTAssertEqual(engine.urlWebSocket.query, "transport=websocket&created=2016-05-04T18%3A31%3A15%2B0200&EIO=4") engine.connectParams = [ "forbidden": "!*'();:@&=+$,/?%#[]\" {}^|" ] XCTAssertEqual(engine.urlPolling.query, "transport=polling&b64=1&forbidden=%21%2A%27%28%29%3B%3A%40%26%3D%2B%24%2C%2F%3F%25%23%5B%5D%22%20%7B%7D%5E%7C&EIO=4") XCTAssertEqual(engine.urlWebSocket.query, "transport=websocket&forbidden=%21%2A%27%28%29%3B%3A%40%26%3D%2B%24%2C%2F%3F%25%23%5B%5D%22%20%7B%7D%5E%7C&EIO=4") } func testBase64Data() { let expect = expectation(description: "Engine Decodes base64 data") let b64String = "baGVsbG8NCg==" let packetString = "451-[\"test\",{\"test\":{\"_placeholder\":true,\"num\":0}}]" socket.on("test") {data, ack in if let data = data[0] as? Data, let string = String(data: data, encoding: .utf8) { XCTAssertEqual(string, "hello") } expect.fulfill() } engine.parseEngineMessage(packetString) engine.parseEngineMessage(b64String) waitForExpectations(timeout: 3, handler: nil) } func testSettingExtraHeadersBeforeConnectSetsEngineExtraHeaders() { let newValue = ["hello": "world"] manager.engine = engine manager.setTestStatus(.notConnected) manager.config = [.extraHeaders(["new": "value"])] manager.config.insert(.extraHeaders(newValue), replacing: true) XCTAssertEqual(2, manager.config.count) XCTAssertEqual(manager.engine!.extraHeaders!, newValue) for config in manager.config { switch config { case let .extraHeaders(headers): XCTAssertTrue(headers.keys.contains("hello"), "It should contain hello header key") XCTAssertFalse(headers.keys.contains("new"), "It should not contain old data") case .path: continue default: XCTFail("It should only have two configs") } } } func testSettingExtraHeadersAfterConnectDoesNotIgnoreChanges() { let newValue = ["hello": "world"] manager.engine = engine manager.setTestStatus(.connected) engine.setConnected(true) manager.config = [.extraHeaders(["new": "value"])] manager.config.insert(.extraHeaders(["hello": "world"]), replacing: true) XCTAssertEqual(2, manager.config.count) XCTAssertEqual(manager.engine!.extraHeaders!, newValue) } func testSettingPathAfterConnectDoesNotIgnoreChanges() { let newValue = "/newpath/" manager.engine = engine manager.setTestStatus(.connected) engine.setConnected(true) manager.config.insert(.path(newValue)) XCTAssertEqual(1, manager.config.count) XCTAssertEqual(manager.engine!.socketPath, newValue) } func testSettingCompressAfterConnectDoesNotIgnoreChanges() { manager.engine = engine manager.setTestStatus(.connected) engine.setConnected(true) manager.config.insert(.compress) XCTAssertEqual(2, manager.config.count) XCTAssertTrue(manager.engine!.compress) } func testSettingForcePollingAfterConnectDoesNotIgnoreChanges() { manager.engine = engine manager.setTestStatus(.connected) engine.setConnected(true) manager.config.insert(.forcePolling(true)) XCTAssertEqual(2, manager.config.count) XCTAssertTrue(manager.engine!.forcePolling) } func testSettingForceWebSocketsAfterConnectDoesNotIgnoreChanges() { manager.engine = engine manager.setTestStatus(.connected) engine.setConnected(true) manager.config.insert(.forceWebsockets(true)) XCTAssertEqual(2, manager.config.count) XCTAssertTrue(manager.engine!.forceWebsockets) } func testChangingEngineHeadersAfterInit() { engine.extraHeaders = ["Hello": "World"] let req = engine.createRequestForPostWithPostWait() XCTAssertEqual("World", req.allHTTPHeaderFields?["Hello"]) } var manager: SocketManager! var socket: SocketIOClient! var engine: SocketEngine! override func setUp() { super.setUp() manager = SocketManager(socketURL: URL(string: "http://localhost")!) socket = manager.defaultSocket engine = SocketEngine(client: manager, url: URL(string: "http://localhost")!, options: nil) socket.setTestable() } } ================================================ FILE: Tests/TestSocketIO/SocketIOClientConfigurationTest.swift ================================================ // // TestSocketIOClientConfiguration.swift // Socket.IO-Client-Swift // // Created by Erik Little on 8/13/16. // // import XCTest import SocketIO class TestSocketIOClientConfiguration : XCTestCase { func testReplaceSameOption() { config.insert(.log(true)) XCTAssertEqual(config.count, 2) switch config[0] { case let .log(log): XCTAssertTrue(log) default: XCTFail() } } func testIgnoreIfExisting() { config.insert(.forceNew(false), replacing: false) XCTAssertEqual(config.count, 2) switch config[1] { case let .forceNew(new): XCTAssertTrue(new) default: XCTFail() } } var config = [] as SocketIOClientConfiguration override func setUp() { config = [.log(false), .forceNew(true)] super.setUp() } } ================================================ FILE: Tests/TestSocketIO/SocketMangerTest.swift ================================================ // // Created by Erik Little on 10/21/17. // import Dispatch import Foundation @testable import SocketIO import XCTest class SocketMangerTest : XCTestCase { func testManagerProperties() { XCTAssertNotNil(manager.defaultSocket) XCTAssertNil(manager.engine) XCTAssertFalse(manager.forceNew) XCTAssertEqual(manager.handleQueue, DispatchQueue.main) XCTAssertTrue(manager.reconnects) XCTAssertEqual(manager.reconnectWait, 10) XCTAssertEqual(manager.reconnectWaitMax, 30) XCTAssertEqual(manager.randomizationFactor, 0.5) XCTAssertEqual(manager.status, .notConnected) } func testSettingConfig() { let manager = SocketManager(socketURL: URL(string: "https://example.com/")!) XCTAssertEqual(manager.config.first!, .secure(true)) manager.config = [] XCTAssertEqual(manager.config.first!, .secure(true)) } func testBackoffIntervalCalulation() { XCTAssertLessThanOrEqual(manager.reconnectInterval(attempts: -1), Double(manager.reconnectWaitMax)) XCTAssertLessThanOrEqual(manager.reconnectInterval(attempts: 0), 15) XCTAssertLessThanOrEqual(manager.reconnectInterval(attempts: 1), 22.5) XCTAssertLessThanOrEqual(manager.reconnectInterval(attempts: 2), 33.75) XCTAssertLessThanOrEqual(manager.reconnectInterval(attempts: 50), Double(manager.reconnectWaitMax)) XCTAssertLessThanOrEqual(manager.reconnectInterval(attempts: 10000), Double(manager.reconnectWaitMax)) XCTAssertGreaterThanOrEqual(manager.reconnectInterval(attempts: -1), Double(manager.reconnectWait)) XCTAssertGreaterThanOrEqual(manager.reconnectInterval(attempts: 0), Double(manager.reconnectWait)) XCTAssertGreaterThanOrEqual(manager.reconnectInterval(attempts: 1), 15) XCTAssertGreaterThanOrEqual(manager.reconnectInterval(attempts: 2), 22.5) XCTAssertGreaterThanOrEqual(manager.reconnectInterval(attempts: 10000), Double(manager.reconnectWait)) } func testManagerCallsConnect() { setUpSockets() socket.expectations[ManagerExpectation.didConnectCalled] = expectation(description: "The manager should call connect on the default socket") socket2.expectations[ManagerExpectation.didConnectCalled] = expectation(description: "The manager should call connect on the socket") socket.connect() socket2.connect() manager.fakeConnecting() manager.fakeConnecting(toNamespace: "/swift") waitForExpectations(timeout: 0.3) } func testManagerCallsDisconnect() { setUpSockets() socket.expectations[ManagerExpectation.didDisconnectCalled] = expectation(description: "The manager should call disconnect on the default socket") socket2.expectations[ManagerExpectation.didDisconnectCalled] = expectation(description: "The manager should call disconnect on the socket") socket2.on(clientEvent: .connect) {data, ack in self.manager.disconnect() self.manager.fakeDisconnecting() } socket.connect() socket2.connect() manager.fakeConnecting() manager.fakeConnecting(toNamespace: "/swift") waitForExpectations(timeout: 0.3) } // func testManagerEmitAll() { // setUpSockets() // // socket.expectations[ManagerExpectation.emitAllEventCalled] = expectation(description: "The manager should emit an event to the default socket") // socket2.expectations[ManagerExpectation.emitAllEventCalled] = expectation(description: "The manager should emit an event to the socket") // // socket2.on(clientEvent: .connect) {data, ack in // print("connect") // self.manager.emitAll("event", "testing") // } // // socket.connect() // socket2.connect() // // manager.fakeConnecting(toNamespace: "/swift") // // waitForExpectations(timeout: 0.3) // } func testManagerSetsConfigs() { let queue = DispatchQueue(label: "testQueue") manager = TestManager(socketURL: URL(string: "http://localhost/")!, config: [ .handleQueue(queue), .forceNew(true), .reconnects(false), .reconnectWait(5), .reconnectWaitMax(5), .randomizationFactor(0.7), .reconnectAttempts(5) ]) XCTAssertEqual(manager.handleQueue, queue) XCTAssertTrue(manager.forceNew) XCTAssertFalse(manager.reconnects) XCTAssertEqual(manager.reconnectWait, 5) XCTAssertEqual(manager.reconnectWaitMax, 5) XCTAssertEqual(manager.randomizationFactor, 0.7) XCTAssertEqual(manager.reconnectAttempts, 5) } func testManagerRemovesSocket() { setUpSockets() manager.removeSocket(socket) XCTAssertNil(manager.nsps[socket.nsp]) } private func setUpSockets() { socket = manager.testSocket(forNamespace: "/") socket2 = manager.testSocket(forNamespace: "/swift") } private var manager: TestManager! private var socket: TestSocket! private var socket2: TestSocket! override func setUp() { super.setUp() manager = TestManager(socketURL: URL(string: "http://localhost/")!, config: [.log(false)]) socket = nil socket2 = nil } } public enum ManagerExpectation: String { case didConnectCalled case didDisconnectCalled case emitAllEventCalled } public class TestManager: SocketManager { public override func disconnect() { setTestStatus(.disconnected) } public func testSocket(forNamespace nsp: String) -> TestSocket { return socket(forNamespace: nsp) as! TestSocket } public func fakeDisconnecting() { engineDidClose(reason: "") } public func fakeConnecting(toNamespace nsp: String = "/") { DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) { // Fake connecting self.parseEngineMessage("0\(nsp)") } } public override func socket(forNamespace nsp: String) -> SocketIOClient { // set socket to our test socket, the superclass method will get this from nsps nsps[nsp] = TestSocket(manager: self, nsp: nsp) return super.socket(forNamespace: nsp) } } public class TestSocket: SocketIOClient { public var expectations = [ManagerExpectation: XCTestExpectation]() public override func didConnect(toNamespace nsp: String, payload: [String: Any]?) { expectations[ManagerExpectation.didConnectCalled]?.fulfill() expectations[ManagerExpectation.didConnectCalled] = nil super.didConnect(toNamespace: nsp, payload: payload) } public override func didDisconnect(reason: String) { expectations[ManagerExpectation.didDisconnectCalled]?.fulfill() expectations[ManagerExpectation.didDisconnectCalled] = nil super.didDisconnect(reason: reason) } public override func emit(_ event: String, _ items: SocketData..., completion: (() -> ())? = nil) { expectations[ManagerExpectation.emitAllEventCalled]?.fulfill() expectations[ManagerExpectation.emitAllEventCalled] = nil } } ================================================ FILE: Tests/TestSocketIO/SocketNamespacePacketTest.swift ================================================ // // SocketNamespacePacketTest.swift // Socket.IO-Client-Swift // // Created by Erik Little on 10/11/15. // // import XCTest @testable import SocketIO class SocketNamespacePacketTest : XCTestCase { func testEmptyEmit() { let sendData: [Any] = ["test"] let packet = SocketPacket.packetFromEmit(sendData, id: -1, nsp: "/swift", ack: false) let parsed = parser.parseSocketMessage(packet.packetString)! XCTAssertEqual(parsed.type, .event) XCTAssertEqual(parsed.nsp, "/swift") XCTAssertTrue(compareAnyArray(input: parsed.data, expected: sendData)) } func testNullEmit() { let sendData: [Any] = ["test", NSNull()] let packet = SocketPacket.packetFromEmit(sendData, id: -1, nsp: "/swift", ack: false) let parsed = parser.parseSocketMessage(packet.packetString)! XCTAssertEqual(parsed.type, .event) XCTAssertEqual(parsed.nsp, "/swift") XCTAssertTrue(compareAnyArray(input: parsed.data, expected: sendData)) } func testStringEmit() { let sendData: [Any] = ["test", "foo bar"] let packet = SocketPacket.packetFromEmit(sendData, id: -1, nsp: "/swift", ack: false) let parsed = parser.parseSocketMessage(packet.packetString)! XCTAssertEqual(parsed.type, .event) XCTAssertEqual(parsed.nsp, "/swift") XCTAssertTrue(compareAnyArray(input: parsed.data, expected: sendData)) } func testJSONEmit() { let sendData: [Any] = ["test", ["foobar": true, "hello": 1, "test": "hello", "null": NSNull()] as NSDictionary] let packet = SocketPacket.packetFromEmit(sendData, id: -1, nsp: "/swift", ack: false) let parsed = parser.parseSocketMessage(packet.packetString)! XCTAssertEqual(parsed.type, .event) XCTAssertEqual(parsed.nsp, "/swift") XCTAssertTrue(compareAnyArray(input: parsed.data, expected: sendData)) } func testArrayEmit() { let sendData: [Any] = ["test", ["hello", 1, ["test": "test"], true]] let packet = SocketPacket.packetFromEmit(sendData, id: -1, nsp: "/swift", ack: false) let parsed = parser.parseSocketMessage(packet.packetString)! XCTAssertEqual(parsed.type, .event) XCTAssertEqual(parsed.nsp, "/swift") XCTAssertTrue(compareAnyArray(input: parsed.data, expected: sendData)) } func testBinaryEmit() { let sendData: [Any] = ["test", data] let packet = SocketPacket.packetFromEmit(sendData, id: -1, nsp: "/swift", ack: false) let parsed = parser.parseSocketMessage(packet.packetString)! XCTAssertEqual(parsed.type, .binaryEvent) XCTAssertEqual(parsed.nsp, "/swift") XCTAssertEqual(packet.binary, [data]) XCTAssertTrue(compareAnyArray(input: parsed.data, expected: [ "test", ["_placeholder": true, "num": 0] ])) } func testMultipleBinaryEmit() { let sendData: [Any] = ["test", ["data1": data, "data2": data2] as NSDictionary] let packet = SocketPacket.packetFromEmit(sendData, id: -1, nsp: "/swift", ack: false) let parsed = parser.parseSocketMessage(packet.packetString)! XCTAssertEqual(parsed.type, .binaryEvent) XCTAssertEqual(parsed.nsp, "/swift") let binaryObj = parsed.data[1] as! [String: Any] let data1Loc = (binaryObj["data1"] as! [String: Any])["num"] as! Int let data2Loc = (binaryObj["data2"] as! [String: Any])["num"] as! Int XCTAssertEqual(packet.binary[data1Loc], data) XCTAssertEqual(packet.binary[data2Loc], data2) } func testEmitWithAck() { let sendData = ["test"] let packet = SocketPacket.packetFromEmit(sendData, id: 0, nsp: "/swift", ack: false) let parsed = parser.parseSocketMessage(packet.packetString)! XCTAssertEqual(parsed.type, .event) XCTAssertEqual(parsed.nsp, "/swift") XCTAssertEqual(parsed.id, 0) XCTAssertTrue(compareAnyArray(input: parsed.data, expected: sendData)) } func testEmitDataWithAck() { let sendData: [Any] = ["test", data] let packet = SocketPacket.packetFromEmit(sendData, id: 0, nsp: "/swift", ack: false) let parsed = parser.parseSocketMessage(packet.packetString)! XCTAssertEqual(parsed.type, .binaryEvent) XCTAssertEqual(parsed.nsp, "/swift") XCTAssertEqual(parsed.id, 0) XCTAssertTrue(compareAnyArray(input: parsed.data, expected: [ "test", ["_placeholder": true, "num": 0] ])) } // Acks func testEmptyAck() { let packet = SocketPacket.packetFromEmit([], id: 0, nsp: "/swift", ack: true) let parsed = parser.parseSocketMessage(packet.packetString)! XCTAssertEqual(parsed.type, .ack) XCTAssertEqual(parsed.nsp, "/swift") XCTAssertEqual(parsed.id, 0) } func testNullAck() { let sendData = [NSNull()] let packet = SocketPacket.packetFromEmit(sendData, id: 0, nsp: "/swift", ack: true) let parsed = parser.parseSocketMessage(packet.packetString)! XCTAssertEqual(parsed.type, .ack) XCTAssertEqual(parsed.nsp, "/swift") XCTAssertEqual(parsed.id, 0) XCTAssertTrue(compareAnyArray(input: parsed.data, expected: sendData)) } func testStringAck() { let sendData = ["test"] let packet = SocketPacket.packetFromEmit(sendData, id: 0, nsp: "/swift", ack: true) let parsed = parser.parseSocketMessage(packet.packetString)! XCTAssertEqual(parsed.type, .ack) XCTAssertEqual(parsed.nsp, "/swift") XCTAssertEqual(parsed.id, 0) XCTAssertTrue(compareAnyArray(input: parsed.data, expected: sendData)) } func testJSONAck() { let sendData = [["foobar": true, "hello": 1, "test": "hello", "null": NSNull()]] let packet = SocketPacket.packetFromEmit(sendData, id: 0, nsp: "/swift", ack: true) let parsed = parser.parseSocketMessage(packet.packetString)! XCTAssertEqual(parsed.type, .ack) XCTAssertEqual(parsed.nsp, "/swift") XCTAssertEqual(parsed.id, 0) XCTAssertTrue(compareAnyArray(input: parsed.data, expected: sendData)) } func testBinaryAck() { let sendData = [data] let packet = SocketPacket.packetFromEmit(sendData, id: 0, nsp: "/swift", ack: true) let parsed = parser.parseSocketMessage(packet.packetString)! XCTAssertEqual(parsed.type, .binaryAck) XCTAssertEqual(parsed.nsp, "/swift") XCTAssertEqual(parsed.id, 0) XCTAssertTrue(compareAnyArray(input: parsed.data, expected: [ ["_placeholder": true, "num": 0] ])) } func testMultipleBinaryAck() { let sendData = [["data1": data, "data2": data2]] let packet = SocketPacket.packetFromEmit(sendData, id: 0, nsp: "/swift", ack: true) let parsed = parser.parseSocketMessage(packet.packetString)! XCTAssertEqual(parsed.type, .binaryAck) XCTAssertEqual(parsed.nsp, "/swift") XCTAssertEqual(parsed.id, 0) let binaryObj = parsed.data[0] as! [String: Any] let data1Loc = (binaryObj["data1"] as! [String: Any])["num"] as! Int let data2Loc = (binaryObj["data2"] as! [String: Any])["num"] as! Int XCTAssertEqual(packet.binary[data1Loc], data) XCTAssertEqual(packet.binary[data2Loc], data2) } let data = "test".data(using: String.Encoding.utf8)! let data2 = "test2".data(using: String.Encoding.utf8)! var parser: SocketParsable! private func compareAnyArray(input: [Any], expected: [Any]) -> Bool { guard input.count == expected.count else { return false } return (input as NSArray).isEqual(to: expected) } override func setUp() { super.setUp() parser = SocketManager(socketURL: URL(string: "http://localhost")!) } } ================================================ FILE: Tests/TestSocketIO/SocketParserTest.swift ================================================ // // SocketParserTest.swift // Socket.IO-Client-Swift // // Created by Lukas Schmidt on 05.09.15. // // import XCTest @testable import SocketIO class SocketParserTest: XCTestCase { func testDisconnect() { let message = "1" validateParseResult(message) } func testConnect() { let message = "0" validateParseResult(message) } func testDisconnectNameSpace() { let message = "1/swift" validateParseResult(message) } func testConnecttNameSpace() { let message = "0/swift" validateParseResult(message) } func testIdEvent() { let message = "25[\"test\"]" validateParseResult(message) } func testBinaryPlaceholderAsString() { let message = "2[\"test\",\"~~0\"]" validateParseResult(message) } func testNameSpaceArrayParse() { let message = "2/swift,[\"testArrayEmitReturn\",[\"test3\",\"test4\"]]" validateParseResult(message) } func testNameSpaceArrayAckParse() { let message = "3/swift,0[[\"test3\",\"test4\"]]" validateParseResult(message) } func testNameSpaceBinaryEventParse() { let message = "51-/swift,[\"testMultipleItemsWithBufferEmitReturn\",[1,2],{\"test\":\"bob\"},25,\"polo\",{\"_placeholder\":true,\"num\":0}]" validateParseResult(message) } func testNameSpaceBinaryAckParse() { let message = "61-/swift,19[[1,2],{\"test\":\"bob\"},25,\"polo\",{\"_placeholder\":true,\"num\":0}]" validateParseResult(message) } func testNamespaceErrorParse() { let message = "4/swift," validateParseResult(message) } func testErrorTypeString() { let message = "4\"ERROR\"" validateParseResult(message) } func testErrorTypeDictionary() { let message = "4{\"test\":2}" validateParseResult(message) } func testErrorTypeInt() { let message = "41" validateParseResult(message) } func testErrorTypeArray() { let message = "4[1, \"hello\"]" validateParseResult(message) } func testInvalidInput() { let message = "8" do { let _ = try testManager.parseString(message) XCTFail() } catch { } } func testGenericParser() { var parser = SocketStringReader(message: "61-/swift,") XCTAssertEqual(parser.read(count: 1), "6") XCTAssertEqual(parser.currentCharacter, "1") XCTAssertEqual(parser.readUntilOccurence(of: "-"), "1") XCTAssertEqual(parser.currentCharacter, "/") } func validateParseResult(_ message: String) { let validValues = SocketParserTest.packetTypes[message]! let packet = try! testManager.parseString(message) let type = String(message.prefix(1)) XCTAssertEqual(packet.type, SocketPacket.PacketType(rawValue: Int(type) ?? -1)!) XCTAssertEqual(packet.nsp, validValues.0) XCTAssertTrue((packet.data as NSArray).isEqual(to: validValues.1), "\(packet.data)") XCTAssertTrue((packet.binary as NSArray).isEqual(to: validValues.2), "\(packet.binary)") XCTAssertEqual(packet.id, validValues.3) } func testParsePerformance() { let keys = Array(SocketParserTest.packetTypes.keys) measure { for item in keys.enumerated() { _ = try! self.testManager.parseString(item.element) } } } let testManager = SocketManager(socketURL: URL(string: "http://localhost/")!) //Format key: message; namespace-data-binary-id static let packetTypes: [String: (String, [Any], [Data], Int)] = [ "0": ("/", [], [], -1), "1": ("/", [], [], -1), "25[\"test\"]": ("/", ["test"], [], 5), "2[\"test\",\"~~0\"]": ("/", ["test", "~~0"], [], -1), "2/swift,[\"testArrayEmitReturn\",[\"test3\",\"test4\"]]": ("/swift", ["testArrayEmitReturn", ["test3", "test4"] as NSArray], [], -1), "51-/swift,[\"testMultipleItemsWithBufferEmitReturn\",[1,2],{\"test\":\"bob\"},25,\"polo\",{\"_placeholder\":true,\"num\":0}]": ("/swift", ["testMultipleItemsWithBufferEmitReturn", [1, 2] as NSArray, ["test": "bob"] as NSDictionary, 25, "polo", ["_placeholder": true, "num": 0] as NSDictionary], [], -1), "3/swift,0[[\"test3\",\"test4\"]]": ("/swift", [["test3", "test4"] as NSArray], [], 0), "61-/swift,19[[1,2],{\"test\":\"bob\"},25,\"polo\",{\"_placeholder\":true,\"num\":0}]": ("/swift", [ [1, 2] as NSArray, ["test": "bob"] as NSDictionary, 25, "polo", ["_placeholder": true, "num": 0] as NSDictionary], [], 19), "4/swift,": ("/swift", [], [], -1), "0/swift": ("/swift", [], [], -1), "1/swift": ("/swift", [], [], -1), "4\"ERROR\"": ("/", ["ERROR"], [], -1), "4{\"test\":2}": ("/", [["test": 2]], [], -1), "41": ("/", [1], [], -1), "4[1, \"hello\"]": ("/", [1, "hello"], [], -1) ] } ================================================ FILE: Tests/TestSocketIO/SocketSideEffectTest.swift ================================================ // // SocketSideEffectTest.swift // Socket.IO-Client-Swift // // Created by Erik Little on 10/11/15. // // import XCTest @testable import SocketIO import Starscream class SocketSideEffectTest: XCTestCase { func testInitialCurrentAck() { XCTAssertEqual(socket.currentAck, -1) } func testFirstAck() { socket.emitWithAck("test").timingOut(after: 0) {data in} XCTAssertEqual(socket.currentAck, 0) } func testSecondAck() { socket.emitWithAck("test").timingOut(after: 0) {data in} socket.emitWithAck("test").timingOut(after: 0) {data in} XCTAssertEqual(socket.currentAck, 1) } func testEmitCompletionSyntax() { socket.emit("test", completion: {}) socket.emit("test", "thing", completion: {}) } func testHandleAck() { let expect = expectation(description: "handled ack") socket.emitWithAck("test").timingOut(after: 0) {data in XCTAssertEqual(data[0] as? String, "hello world") expect.fulfill() } manager.parseEngineMessage("30[\"hello world\"]") waitForExpectations(timeout: 3, handler: nil) } func testHandleAckWithAckEmit() { let expect = expectation(description: "handled ack") socket.emitWithAck("test").timingOut(after: 0) {data in XCTAssertEqual(data[0] as? String, "hello world") self.socket.emitWithAck("test").timingOut(after: 0) {data in} expect.fulfill() } manager.parseEngineMessage("30[\"hello world\"]") waitForExpectations(timeout: 3, handler: nil) } func testHandleAck2() { let expect = expectation(description: "handled ack2") socket.emitWithAck("test").timingOut(after: 0) {data in XCTAssertTrue(data.count == 2, "Wrong number of ack items") expect.fulfill() } manager.parseEngineMessage("61-0[{\"_placeholder\":true,\"num\":0},{\"test\":true}]") manager.parseEngineBinaryData(Data()) waitForExpectations(timeout: 3, handler: nil) } func testHandleEvent() { let expect = expectation(description: "handled event") socket.on("test") {data, ack in XCTAssertEqual(data[0] as? String, "hello world") expect.fulfill() } manager.parseEngineMessage("2[\"test\",\"hello world\"]") waitForExpectations(timeout: 3, handler: nil) } func testHandleStringEventWithQuotes() { let expect = expectation(description: "handled event") socket.on("test") {data, ack in XCTAssertEqual(data[0] as? String, "\"hello world\"") expect.fulfill() } manager.parseEngineMessage("2[\"test\",\"\\\"hello world\\\"\"]") waitForExpectations(timeout: 3, handler: nil) } func testHandleOnceEvent() { let expect = expectation(description: "handled event") socket.once("test") {data, ack in XCTAssertEqual(data[0] as? String, "hello world") XCTAssertEqual(self.socket.testHandlers.count, 0) expect.fulfill() } manager.parseEngineMessage("2[\"test\",\"hello world\"]") waitForExpectations(timeout: 3, handler: nil) } func testHandleOnceClientEvent() { let expect = expectation(description: "handled event") socket.setTestStatus(.connecting) socket.once(clientEvent: .connect) {data, ack in XCTAssertEqual(self.socket.testHandlers.count, 0) expect.fulfill() } DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + 0.1) { // Fake connecting self.manager.parseEngineMessage("0/") } waitForExpectations(timeout: 3, handler: nil) } func testOffWithEvent() { socket.on("test") {data, ack in } socket.on("test") {data, ack in } XCTAssertEqual(socket.testHandlers.count, 2) socket.off("test") XCTAssertEqual(socket.testHandlers.count, 0) } func testOffClientEvent() { socket.on(clientEvent: .connect) {data, ack in } socket.on(clientEvent: .disconnect) {data, ack in } XCTAssertEqual(socket.testHandlers.count, 2) socket.off(clientEvent: .disconnect) XCTAssertEqual(socket.testHandlers.count, 1) XCTAssertTrue(socket.testHandlers.contains(where: { $0.event == "connect" })) } func testOffWithId() { let handler = socket.on("test") {data, ack in } XCTAssertEqual(socket.testHandlers.count, 1) socket.on("test") {data, ack in } XCTAssertEqual(socket.testHandlers.count, 2) socket.off(id: handler) XCTAssertEqual(socket.testHandlers.count, 1) } func testHandlesErrorPacket() { let expect = expectation(description: "Handled error") socket.on("error") {data, ack in if let error = data[0] as? String, error == "test error" { expect.fulfill() } } manager.parseEngineMessage("4\"test error\"") waitForExpectations(timeout: 3, handler: nil) } func testHandleBinaryEvent() { let expect = expectation(description: "handled binary event") socket.on("test") {data, ack in if let dict = data[0] as? [String: Any], let data = dict["test"] as? Data { XCTAssertEqual(data as Data, self.data) expect.fulfill() } } manager.parseEngineMessage("51-[\"test\",{\"test\":{\"_placeholder\":true,\"num\":0}}]") manager.parseEngineBinaryData(data) waitForExpectations(timeout: 3, handler: nil) } func testHandleMultipleBinaryEvent() { let expect = expectation(description: "handled multiple binary event") socket.on("test") {data, ack in if let dict = data[0] as? [String: Any], let data = dict["test"] as? Data, let data2 = dict["test2"] as? Data { XCTAssertEqual(data as Data, self.data) XCTAssertEqual(data2 as Data, self.data2) expect.fulfill() } } manager.parseEngineMessage("52-[\"test\",{\"test\":{\"_placeholder\":true,\"num\":0},\"test2\":{\"_placeholder\":true,\"num\":1}}]") manager.parseEngineBinaryData(data) manager.parseEngineBinaryData(data2) waitForExpectations(timeout: 3, handler: nil) } func testChangingStatusCallsStatusChangeHandler() { let expect = expectation(description: "The client should announce when the status changes") let statusChange = SocketIOStatus.connecting socket.on("statusChange") {data, ack in guard let status = data[0] as? SocketIOStatus else { XCTFail("Status should be one of the defined statuses") return } XCTAssertEqual(status, statusChange, "The status changed should be the one set") expect.fulfill() } socket.setTestStatus(statusChange) waitForExpectations(timeout: 0.2) } func testOnClientEvent() { let expect = expectation(description: "The client should call client event handlers") let event = SocketClientEvent.disconnect let closeReason = "testing" socket.on(clientEvent: event) {data, ack in guard let reason = data[0] as? String else { XCTFail("Client should pass data for client events") return } XCTAssertEqual(closeReason, reason, "The data should be what was sent to handleClientEvent") expect.fulfill() } socket.handleClientEvent(event, data: [closeReason]) waitForExpectations(timeout: 0.2) } func testClientEventsAreBackwardsCompatible() { let expect = expectation(description: "The client should call old style client event handlers") let event = SocketClientEvent.disconnect let closeReason = "testing" socket.on("disconnect") {data, ack in guard let reason = data[0] as? String else { XCTFail("Client should pass data for client events") return } XCTAssertEqual(closeReason, reason, "The data should be what was sent to handleClientEvent") expect.fulfill() } socket.handleClientEvent(event, data: [closeReason]) waitForExpectations(timeout: 0.2) } func testConnectTimesOutIfNotConnected() { let expect = expectation(description: "The client should call the timeout function") socket = manager.socket(forNamespace: "/someNamespace") socket.setTestStatus(.notConnected) manager.engine = TestEngine(client: manager, url: manager.socketURL, options: nil) socket.connect(timeoutAfter: 0.5, withHandler: { expect.fulfill() }) waitForExpectations(timeout: 0.8) } func testConnectDoesNotTimeOutIfConnected() { let expect = expectation(description: "The client should not call the timeout function") socket.setTestStatus(.notConnected) manager.engine = TestEngine(client: manager, url: manager.socketURL, options: nil) socket.on(clientEvent: .connect) {data, ack in expect.fulfill() } socket.connect(timeoutAfter: 0.5, withHandler: { XCTFail("Should not call timeout handler if status is connected") }) DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + 0.1) { // Fake connecting self.manager.parseEngineMessage("0/") } waitForExpectations(timeout: 2) } func testClientCallsConnectOnEngineOpen() { let expect = expectation(description: "The client call the connect handler") let eng = TestEngine(client: manager, url: manager.socketURL, options: nil) eng.onConnect = { self.socket.didConnect(toNamespace: self.socket.nsp, payload: nil) } manager.engine = eng socket.setTestStatus(.notConnected) socket.on(clientEvent: .connect) {data, ack in expect.fulfill() } socket.connect(timeoutAfter: 0.5, withHandler: { XCTFail("Should not call timeout handler if status is connected") }) waitForExpectations(timeout: 2) } func testConnectIsCalledWithNamespace() { let expect = expectation(description: "The client should not call the timeout function") let nspString = "/swift" socket = manager.socket(forNamespace: "/swift") socket.setTestStatus(.notConnected) manager.engine = TestEngine(client: manager, url: manager.socketURL, options: nil) socket.on(clientEvent: .connect) {data, ack in guard let nsp = data[0] as? String else { XCTFail("Connect should be called with a namespace") return } XCTAssertEqual(nspString, nsp, "It should connect with the correct namespace") expect.fulfill() } socket.connect(timeoutAfter: 0.3, withHandler: { XCTFail("Should not call timeout handler if status is connected") }) DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + 0.1) { // Fake connecting self.manager.parseEngineMessage("0/swift") } waitForExpectations(timeout: 2) } func testErrorInCustomSocketDataCallsErrorHandler() { let expect = expectation(description: "The client should call the error handler for emit errors because of " + "custom data") socket.on(clientEvent: .error) {data, ack in guard data.count == 3, data[0] as? String == "myEvent", data[2] is ThrowingData.ThrowingError else { XCTFail("Incorrect error call") return } expect.fulfill() } socket.emit("myEvent", ThrowingData()) waitForExpectations(timeout: 0.2) } func testErrorInCustomSocketDataCallsErrorHandler_ack() { let expect = expectation(description: "The client should call the error handler for emit errors because of " + "custom data") socket.on(clientEvent: .error) {data, ack in guard data.count == 3, data[0] as? String == "myEvent", data[2] is ThrowingData.ThrowingError else { XCTFail("Incorrect error call") return } expect.fulfill() } socket.emitWithAck("myEvent", ThrowingData()).timingOut(after: 0.8, callback: {_ in XCTFail("Ack callback should not be called") }) waitForExpectations(timeout: 0.2) } func testSettingConfigAfterInit() { socket.setTestStatus(.notConnected) manager.config.insert(.log(true)) XCTAssertTrue(DefaultSocketLogger.Logger.log, "It should set logging to true after creation") manager.config = [.log(false)] XCTAssertFalse(DefaultSocketLogger.Logger.log, "It should set logging to false after creation") } func testSettingConfigAfterDisconnect() { socket.setTestStatus(.disconnected) manager.config.insert(.log(true)) XCTAssertTrue(DefaultSocketLogger.Logger.log, "It should set logging to true after creation") manager.config = [.log(false)] XCTAssertFalse(DefaultSocketLogger.Logger.log, "It should set logging to false after creation") } func testSettingConfigAfterInitWhenConnectedDoesNotIgnoreChanges() { manager.setTestStatus(.connected) manager.config = [.log(true)] XCTAssertTrue(DefaultSocketLogger.Logger.log, "It should set logging to false after creation") } func testClientCallsSentPingHandler() { let expect = expectation(description: "The client should emit a ping event") socket.on(clientEvent: .pong) {data, ack in expect.fulfill() } manager.engineDidSendPong() waitForExpectations(timeout: 0.2) } func testClientCallsGotPongHandler() { let expect = expectation(description: "The client should emit a pong event") socket.on(clientEvent: .ping) {data, ack in expect.fulfill() } manager.engineDidReceivePing() waitForExpectations(timeout: 0.2) } let data = "test".data(using: String.Encoding.utf8)! let data2 = "test2".data(using: String.Encoding.utf8)! private var manager: SocketManager! private var socket: SocketIOClient! override func setUp() { super.setUp() manager = SocketManager(socketURL: URL(string: "http://localhost/")!, config: [.log(false)]) socket = manager.defaultSocket socket.setTestable() } } struct ThrowingData: SocketData { enum ThrowingError : Error { case error } func socketRepresentation() throws -> SocketData { throw ThrowingError.error } } class TestEngine: SocketEngineSpec { weak var client: SocketEngineClient? private(set) var closed = false private(set) var compress = false private(set) var connected = false var connectParams: [String: Any]? = nil private(set) var cookies: [HTTPCookie]? = nil private(set) var engineQueue = DispatchQueue.main var extraHeaders: [String: String]? = nil private(set) var fastUpgrade = false private(set) var forcePolling = false private(set) var forceWebsockets = false private(set) var polling = false private(set) var probing = false private(set) var sid = "" private(set) var socketPath = "" private(set) var urlPolling = URL(string: "http://localhost/")! private(set) var urlWebSocket = URL(string: "http://localhost/")! private(set) var websocket = false private(set) var ws: WebSocket? = nil private(set) var version = SocketIOVersion.three fileprivate var onConnect: (() -> ())? required init(client: SocketEngineClient, url: URL, options: [String: Any]?) { self.client = client } func connect() { onConnect?() } func didError(reason: String) { } func disconnect(reason: String) { } func doFastUpgrade() { } func flushWaitingForPostToWebSocket() { } func parseEngineData(_ data: Data) { } func parseEngineMessage(_ message: String) { } func write(_ msg: String, withType type: SocketEnginePacketType, withData data: [Data], completion: (() -> ())?) { } } ================================================ FILE: Tests/TestSocketIO/utils.swift ================================================ // // Created by Erik Little on 2019-01-11. // import Foundation @testable import SocketIO public class OBjcUtils: NSObject { @objc public static func setTestStatus(socket: SocketIOClient, status: SocketIOStatus) { socket.setTestStatus(status) } } ================================================ FILE: Usage Docs/12to13.md ================================================ # Upgrading from v12 This guide will help you navigate the changes that were introduced in v13. ## What are the big changes? The biggest change is how to create and manage clients. Much like the native JS client and server, the swift client now only uses one engine per connection. Previously in order to use namespaces it was required to create multiple clients, and each client had its own engine. Some v12 code might've looked like this: ```swift let defaultSocket = SocketIOClient(socketURL: myURL) let namespaceSocket = SocketIOClient(socketURL: myURL, config: [.nsp("/swift")]) // add handlers for sockets and connect ``` In v12 this would have opened two connections to the socket.io. In v13 the same code would look like this: ```swift let manager = SocketManager(socketURL: myURL) let defaultSocket = manager.defaultSocket let namespaceSocket = manager.socket(forNamespace: "/swift") // add handlers for sockets and connect ``` In v13 `defaultSocket` and `namespaceSocket` will share a single transport. This means one less connection to the server needs to be opened. ## What might I have to change? - The most obvious thing you will need to change is that instead of creating `SocketIOClient`s directly, you will create a `SocketManager` and either use the `defaultSocket` property if you don't need namespaces, or call the `socket(forNamespace:)` method on the manager. - `SocketIOClient` is no longer a client to an engine. So if you were overriding the engine methods, these have been moved to the manager. - The library is now a single target. So you might have to change some of your Xcode project settings. - `SocketIOClient`s no longer take a configuration, they are shared from the manager. - The `joinNamespace()` and `leaveNamespace()` methods on `SocketIOClient` no longer take any arguments, and in most cases no longer need to be called. Namespace joining/leaving can be managed by calling `connect()`/`disconnect()` on the socket associated with that namespace. ---------- # What things should I know? How sockets are stored --- You should know that `SocketIOClient`s no longer need to be held around in properties, but the `SocketManager` should. One of the most common mistakes people made is not maintaining a strong reference to the client. ```swift class Manager { func addHandlers() { let socket = SocketIOClient(socketURL: myURL, config: [.nsp("/swift")]) // Add handlers } } ``` This would have resulted in the client being released and no handlers being called. A *correct* equivalent would be: ```swift class Manager { let socketManager = SocketManager(socketURL: someURL) func addHandlers() { let socket = socketManager.socket(forNamespace: "/swift") // Add handlers } } ``` This code is fine because the `SocketManager` will maintain a strong reference to the socket. It's also worth noting that subsequent calls to `socket(forNamespace:)` will return the *same* socket instance as the first call. So you don't need to hold onto the socket directly just to access it again, just call `socket(forNamespace:)` on the manager to get it. **This does mean that if you need multiple sockets on the same namespace, you will have to use multiple managers.** What to call connect on --- Connect can either be called on the manager directly, or on one of the sockets made from it. In either case, if the manager was not already connected to the server, a connection will be made. Also in both cases the default socket (namespace "/") will fire a `connect` event. The difference is that if `connect()` is just called on the manager, then any sockets for that manager that are not the default socket will not automatically connect. `connect()` will need to be called individually for each socket. However, if `connect()` is called on a client, then in addition to opening the connection if needed, the client will connect to its namespace, and a `connect` event fired. ================================================ FILE: Usage Docs/15to16.md ================================================ # Upgrading from v15 to v16 This guide will help you navigate the changes that were introduced in v16. ## Objective-c is no longer supported. You must now use Swift. ## Client supports multiple socket.io versions The client now supports socket.io 3 servers. This is mostly a transparent change, however if your sever is socket.io 2, you must send `.version(.two)` as an option to the manager. ```swift SocketManager(socketURL: URL(string:"http://localhost:8087/")!, config: [.version(.two)]) ``` ================================================ FILE: Usage Docs/FAQ.md ================================================ ## How do I connect to my WebSocket server? This library is **NOT** a WebSockets library. This library is only for servers that implement the socket.io protocol, such as [socket.io](https://socket.io/). If you need a plain WebSockets client check out [Starscream](https://github.com/daltoniam/Starscream) for Swift and [JetFire](https://github.com/acmacalister/jetfire) for Objective-C. ## Why isn't my event handler being called? One of the most common reasons your event might not be called is if the client is released by [ARC](https://developer.apple.com/library/content/documentation/Swift/Conceptual/Swift_Programming_Language/AutomaticReferenceCounting.html). Take this code for example: ```swift class Manager { func addHandlers() { let manager = SocketManager(socketURL: URL(string: "http://somesocketioserver.com")!) manager.defaultSocket.on("myEvent") {data, ack in print(data) } } } ``` This code is **incorrect**, and the event handler will never be called. Because as soon as this method is called `manager` will be released, along with the socket, and its memory reclaimed. A correct way would be: ```swift class Manager { let manager = SocketManager(socketURL: URL(string: "http://somesocketioserver.com")!) func addHandlers() { manager.defaultSocket.on("myEvent") {data, ack in print(data) } } } ``` ================================================ FILE: docs/12to13.html ================================================ 12to13 Reference

SocketIO 16.0.0 Docs (100% documented)

Upgrading from v12

This guide will help you navigate the changes that were introduced in v13.

What are the big changes?

The biggest change is how to create and manage clients. Much like the native JS client and server, the swift client now only uses one engine per connection. Previously in order to use namespaces it was required to create multiple clients, and each client had its own engine.

Some v12 code might’ve looked like this:

let defaultSocket = SocketIOClient(socketURL: myURL)
let namespaceSocket = SocketIOClient(socketURL: myURL, config: [.nsp("/swift")])

// add handlers for sockets and connect

In v12 this would have opened two connections to the socket.io.

In v13 the same code would look like this:

let manager = SocketManager(socketURL: myURL)
let defaultSocket = manager.defaultSocket
let namespaceSocket = manager.socket(forNamespace: "/swift")

// add handlers for sockets and connect

In v13 defaultSocket and namespaceSocket will share a single transport. This means one less connection to the server needs to be opened.

What might I have to change?

  • The most obvious thing you will need to change is that instead of creating SocketIOClients directly, you will create a SocketManager and either use the defaultSocket property if you don’t need namespaces, or call the socket(forNamespace:) method on the manager.

  • SocketIOClient is no longer a client to an engine. So if you were overriding the engine methods, these have been moved to the manager.

  • The library is now a single target. So you might have to change some of your Xcode project settings.

  • SocketIOClients no longer take a configuration, they are shared from the manager.

  • The joinNamespace() and leaveNamespace() methods on SocketIOClient no longer take any arguments, and in most cases no longer need to be called. Namespace joining/leaving can be managed by calling connect()/disconnect() on the socket associated with that namespace.


What things should I know?

How sockets are stored

You should know that SocketIOClients no longer need to be held around in properties, but the SocketManager should.

One of the most common mistakes people made is not maintaining a strong reference to the client.

class Manager {
    func addHandlers() {
        let socket = SocketIOClient(socketURL: myURL, config: [.nsp("/swift")])

        // Add handlers
    }
}

This would have resulted in the client being released and no handlers being called.

A correct equivalent would be:

class Manager {
    let socketManager = SocketManager(socketURL: someURL)

    func addHandlers() {
        let socket = socketManager.socket(forNamespace: "/swift")

        // Add handlers
    }
}

This code is fine because the SocketManager will maintain a strong reference to the socket.

It’s also worth noting that subsequent calls to socket(forNamespace:) will return the same socket instance as the first call. So you don’t need to hold onto the socket directly just to access it again, just call socket(forNamespace:) on the manager to get it. This does mean that if you need multiple sockets on the same namespace, you will have to use multiple managers.

What to call connect on

Connect can either be called on the manager directly, or on one of the sockets made from it. In either case, if the manager was not already connected to the server, a connection will be made. Also in both cases the default socket (namespace “/”) will fire a connect event.

The difference is that if connect() is just called on the manager, then any sockets for that manager that are not the default socket will not automatically connect. connect() will need to be called individually for each socket. However, if connect() is called on a client, then in addition to opening the connection if needed, the client will connect to its namespace, and a connect event fired.

================================================ FILE: docs/15to16.html ================================================ 15to16 Reference

SocketIO 16.0.0 Docs (100% documented)

Upgrading from v15 to v16

This guide will help you navigate the changes that were introduced in v16.

Objective-c is no longer supported. You must now use Swift.

Client supports multiple socket.io versions

The client now supports socket.io 3 servers. This is mostly a transparent change, however if your sever is socket.io 2, you must send .version(.two) as an option to the manager.

SocketManager(socketURL: URL(string:"http://localhost:8087/")!, config: [.version(.two)])
================================================ FILE: docs/Classes/OnAckCallback.html ================================================ OnAckCallback Class Reference

SocketIO 16.0.0 Docs (100% documented)

OnAckCallback

public final class OnAckCallback : NSObject

A class that represents an emit that will request an ack that has not yet been sent. Call timingOut(after:callback:) to complete the emit Example:

socket.emitWithAck("myEvent").timingOut(after: 1) {data in
    ...
}

Methods

  • Completes an emitWithAck. If this isn’t called, the emit never happens.

    Declaration

    Swift

    @objc
    public func timingOut(after seconds: Double, callback: @escaping AckCallback)

    Parameters

    seconds

    The number of seconds before this emit times out if an ack hasn’t been received.

    callback

    The callback called when an ack is received, or when a timeout happens. To check for timeout, use SocketAckStatus‘s noAck case.

================================================ FILE: docs/Classes/SSLSecurity.html ================================================ SSLSecurity Class Reference

SocketIO Docs (100% documented)

SSLSecurity

open class SSLSecurity : NSObject

A wrapper around Starscream’s SSLSecurity that provides a minimal Objective-C interface.

  • The internal Starscream SSLSecurity.

    Declaration

    Swift

    public let security: Starscream.SSLSecurity
  • Creates a new SSLSecurity that specifies whether to use publicKeys or certificates should be used for SSL pinning validation

    Declaration

    Swift

    @objc
    public convenience init(usePublicKeys: Bool = true)

    Parameters

    usePublicKeys

    is to specific if the publicKeys or certificates should be used for SSL pinning validation

  • Designated init

    Declaration

    Swift

    public convenience init(certs: [SSLCert], usePublicKeys: Bool)

    Parameters

    certs

    is the certificates or public keys to use

    usePublicKeys

    is to specific if the publicKeys or certificates should be used for SSL pinning validation

    Return Value

    a representation security object to be used with

  • Returns whether or not the given trust is valid.

    Declaration

    Swift

    public func isValid(_ trust: SecTrust, domain: String?) -> Bool

    Parameters

    trust

    The trust to validate.

    domain

    The CN domain to validate.

    Return Value

    Whether or not this is valid.

================================================ FILE: docs/Classes/SocketAckEmitter.html ================================================ SocketAckEmitter Class Reference

SocketIO 16.0.0 Docs (100% documented)

SocketAckEmitter

public final class SocketAckEmitter : NSObject

A class that represents a waiting ack call.

NOTE: You should not store this beyond the life of the event handler.

  • A view into this emitter where emits do not check for binary data.

    Usage:

    ack.rawEmitView.with(myObject)
    

    NOTE: It is not safe to hold on to this view beyond the life of the socket.

    Declaration

    Swift

    @objc
    public private(set) lazy var rawEmitView: SocketRawAckView { get set }

Properties

  • If true, this handler is expecting to be acked. Call with(_: SocketData...) to ack.

    Declaration

    Swift

    public var expected: Bool { get }

Initializers

  • Creates a new SocketAckEmitter.

    Declaration

    Swift

    public init(socket: SocketIOClient, ackNum: Int)

    Parameters

    socket

    The socket for this emitter.

    ackNum

    The ack number for this emitter.

Methods

  • 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]

    Declaration

    Swift

    public func with(_ items: SocketData...)

    Parameters

    items

    A variable number of items to send when acking.

  • Call to ack receiving this event.

    Declaration

    Swift

    @objc
    public func with(_ items: [Any])

    Parameters

    items

    An array of items to send when acking. Use [] to send nothing.

================================================ FILE: docs/Classes/SocketAnyEvent.html ================================================ SocketAnyEvent Class Reference

SocketIO 16.0.0 Docs (100% documented)

================================================ FILE: docs/Classes/SocketClientManager.html ================================================ SocketClientManager Class Reference

SocketIO Docs (100% documented)

SocketClientManager

open class SocketClientManager : NSObject

Experimental socket manager.

API subject to change.

Can be used to persist sockets across ViewControllers.

Sockets are strongly stored, so be sure to remove them once they are no longer needed.

Example usage:

let manager = SocketClientManager.sharedManager
manager["room1"] = socket1
manager["room2"] = socket2
manager.removeSocket(socket: socket2)
manager["room1"]?.emit("hello")
  • The shared manager.

    Declaration

    Swift

    open static let sharedManager = SocketClientManager()
  • Gets a socket by its name.

    Declaration

    Swift

    open subscript(string: String) -> SocketIOClient?

    Return Value

    The socket, if one had the given name.

  • Adds a socket.

    Declaration

    Swift

    open func addSocket(_ socket: SocketIOClient, labeledAs label: String)

    Parameters

    socket

    The socket to add.

    labeledAs

    The label for this socket.

  • Removes a socket by a given name.

    Declaration

    Swift

    open func removeSocket(withLabel label: String) -> SocketIOClient?

    Parameters

    withLabel

    The label of the socket to remove.

    Return Value

    The socket for the given label, if one was present.

  • Removes a socket.

    Declaration

    Swift

    open func removeSocket(_ socket: SocketIOClient) -> SocketIOClient?

    Parameters

    socket

    The socket to remove.

    Return Value

    The socket if it was in the manager.

  • Removes all the sockets in the manager.

    Declaration

    Swift

    open func removeSockets()
================================================ FILE: docs/Classes/SocketEngine.html ================================================ SocketEngine Class Reference

SocketIO 16.0.0 Docs (100% documented)

SocketEngine

open class SocketEngine:
        NSObject, WebSocketDelegate, URLSessionDelegate, SocketEnginePollable, SocketEngineWebsocket, ConfigSettable

The class that handles the engine.io protocol and transports. See SocketEnginePollable and SocketEngineWebsocket for transport specific methods.

Properties

  • The queue that all engine actions take place on.

    Declaration

    Swift

    public let engineQueue: DispatchQueue
  • The connect parameters sent during a connect.

    Declaration

    Swift

    public var connectParams: [String : Any]? { get set }
  • A dictionary of extra http headers that will be set during connection.

    Declaration

    Swift

    public var extraHeaders: [String : String]?
  • A queue of engine.io messages waiting for POSTing

    You should not touch this directly

    Declaration

    Swift

    public var postWait: [Post]
  • true if there is an outstanding poll. Trying to poll before the first is done will cause socket.io to disconnect us.

    Do not touch this directly

    Declaration

    Swift

    public var waitingForPoll: Bool
  • true if there is an outstanding post. Trying to post before the first is done will cause socket.io to disconnect us.

    Do not touch this directly

    Declaration

    Swift

    public var waitingForPost: Bool
  • true if this engine is closed.

    Declaration

    Swift

    public private(set) var closed: Bool { get }
  • If true the engine will attempt to use WebSocket compression.

    Declaration

    Swift

    public private(set) var compress: Bool { get }
  • true if this engine is connected. Connected means that the initial poll connect has succeeded.

    Declaration

    Swift

    public private(set) var connected: Bool { get }
  • An array of HTTPCookies that are sent during the connection.

    Declaration

    Swift

    public private(set) var cookies: [HTTPCookie]? { get }
  • When true, the engine is in the process of switching to WebSockets.

    Do not touch this directly

    Declaration

    Swift

    public private(set) var fastUpgrade: Bool { get }
  • When true, the engine will only use HTTP long-polling as a transport.

    Declaration

    Swift

    public private(set) var forcePolling: Bool { get }
  • When true, the engine will only use WebSockets as a transport.

    Declaration

    Swift

    public private(set) var forceWebsockets: Bool { get }
  • true If engine’s session has been invalidated.

    Declaration

    Swift

    public private(set) var invalidated: Bool { get }
  • If true, the engine is currently in HTTP long-polling mode.

    Declaration

    Swift

    public private(set) var polling: Bool { get }
  • If true, the engine is currently seeing whether it can upgrade to WebSockets.

    Declaration

    Swift

    public private(set) var probing: Bool { get }
  • The URLSession that will be used for polling.

    Declaration

    Swift

    public private(set) var session: URLSession? { get }
  • sid

    The session id for this engine.

    Declaration

    Swift

    public private(set) var sid: String { get }
  • The path to engine.io.

    Declaration

    Swift

    public private(set) var socketPath: String { get }
  • The url for polling.

    Declaration

    Swift

    public private(set) var urlPolling: URL { get }
  • The url for WebSockets.

    Declaration

    Swift

    public private(set) var urlWebSocket: URL { get }
  • The version of engine.io being used. Default is three.

    Declaration

    Swift

    public private(set) var version: SocketIOVersion { get }
  • If true, then the engine is currently in WebSockets mode.

    Declaration

    Swift

    @available(*, deprecated, message: "No longer needed, if we're not polling, then we must be doing websockets")
    public private(set) var websocket: Bool { get }
  • When true, the WebSocket stream will be configured with the enableSOCKSProxy true.

    Declaration

    Swift

    public private(set) var enableSOCKSProxy: Bool { get }
  • ws

    The WebSocket for this engine.

    Declaration

    Swift

    public private(set) var ws: WebSocket? { get }
  • Whether or not the WebSocket is currently connected.

    Declaration

    Swift

    public private(set) var wsConnected: Bool { get }
  • The client for this engine.

    Declaration

    Swift

    public weak var client: SocketEngineClient?

Initializers

  • Creates a new engine.

    Declaration

    Swift

    public init(client: SocketEngineClient, url: URL, config: SocketIOClientConfiguration)

    Parameters

    client

    The client for this engine.

    url

    The url for this engine.

    config

    An array of configuration options for this engine.

  • Creates a new engine.

    Declaration

    Swift

    public required convenience init(client: SocketEngineClient, url: URL, options: [String : Any]?)

    Parameters

    client

    The client for this engine.

    url

    The url for this engine.

    options

    The options for this engine.

Methods

  • Starts the connection to the server.

    Declaration

    Swift

    open func connect()
  • Called when an error happens during execution. Causes a disconnection.

    Declaration

    Swift

    open func didError(reason: String)
  • Disconnects from the server.

    Declaration

    Swift

    open func disconnect(reason: String)

    Parameters

    reason

    The reason for the disconnection. This is communicated up to the client.

  • Called to switch from HTTP long-polling to WebSockets. After calling this method the engine will be in WebSocket mode.

    You shouldn’t call this directly

    Declaration

    Swift

    open func doFastUpgrade()
  • Causes any packets that were waiting for POSTing to be sent through the WebSocket. This happens because when the engine is attempting to upgrade to WebSocket it does not do any POSTing.

    You shouldn’t call this directly

    Declaration

    Swift

    open func flushWaitingForPostToWebSocket()
  • Parses raw binary received from engine.io.

    Declaration

    Swift

    open func parseEngineData(_ data: Data)

    Parameters

    data

    The data to parse.

  • Parses a raw engine.io packet.

    Declaration

    Swift

    open func parseEngineMessage(_ message: String)

    Parameters

    message

    The message to parse.

  • Called when the engine should set/update its configs from a given configuration.

    parameter config: The SocketIOClientConfiguration that should be used to set/update configs.

    Declaration

    Swift

    open func setConfigs(_ config: SocketIOClientConfiguration)
  • Writes a message to engine.io, independent of transport.

    Declaration

    Swift

    open func write(_ msg: String, withType type: SocketEnginePacketType, withData data: [Data], completion: (() -> ())? = nil)

    Parameters

    msg

    The message to send.

    type

    The type of this message.

    data

    Any data that this message has.

    completion

    Callback called on transport write completion.

URLSessionDelegate methods

================================================ FILE: docs/Classes/SocketIOClient.html ================================================ SocketIOClient Class Reference

SocketIO 16.0.0 Docs (100% documented)

SocketIOClient

open class SocketIOClient : NSObject, SocketIOClientSpec

Represents a socket.io-client.

Clients are created through a SocketManager, which owns the SocketEngineSpec that controls the connection to the server.

For example:

// 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

Properties

  • nsp

    The namespace that this socket is currently connected to.

    Must start with a /.

    Declaration

    Swift

    public let nsp: String
  • A handler that will be called on any event.

    Declaration

    Swift

    public private(set) var anyHandler: ((SocketAnyEvent) -> ())? { get }
  • The array of handlers for this socket.

    Declaration

    Swift

    public private(set) var handlers: [SocketEventHandler] { get }
  • The manager for this socket.

    Declaration

    Swift

    public private(set) weak var manager: SocketManagerSpec? { get }
  • A view into this socket where emits do not check for binary data.

    Usage:

    socket.rawEmitView.emit("myEvent", myObject)
    

    NOTE: It is not safe to hold on to this view beyond the life of the socket.

    Declaration

    Swift

    public private(set) lazy var rawEmitView: SocketRawView { get set }
  • The status of this client.

    Declaration

    Swift

    public private(set) var status: SocketIOStatus { get set }
  • sid

    The id of this socket.io connect. This is different from the sid of the engine.io connection.

    Declaration

    Swift

    public private(set) var sid: String? { get }

Initializers

  • Type safe way to create a new SocketIOClient. opts can be omitted.

    Declaration

    Swift

    public init(manager: SocketManagerSpec, nsp: String)

    Parameters

    manager

    The manager for this socket.

    nsp

    The namespace of the socket.

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.

    Declaration

    Swift

    open func connect(withPayload payload: [String : Any]? = nil)

    Parameters

    withPayload

    An optional payload sent on 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.

    Declaration

    Swift

    open func connect(withPayload payload: [String : Any]? = nil, timeoutAfter: Double, withHandler handler: (() -> ())?)

    Parameters

    withPayload

    An optional payload sent on connect

    timeoutAfter

    The number of seconds after which if we are not connected we assume the connection has failed. Pass 0 to never timeout.

    handler

    The handler to call when the client fails to connect.

  • 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.

    Declaration

    Swift

    open func didConnect(toNamespace namespace: String, payload: [String : Any]?)

    Parameters

    toNamespace

    The namespace that was connected to.

  • Called when the client has disconnected from socket.io.

    Declaration

    Swift

    open func didDisconnect(reason: String)

    Parameters

    reason

    The reason for the disconnection.

  • Disconnects the socket.

    This will cause the socket to leave the namespace it is associated to, as well as remove itself from the manager.

    Declaration

    Swift

    open func disconnect()
  • Send an event to the server, with optional data items and optional write completion handler.

    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]

    Declaration

    Swift

    open func emit(_ event: String, _ items: SocketData..., completion: (() -> ())? = nil)

    Parameters

    event

    The event to send.

    items

    The items to send with this event. May be left out.

    completion

    Callback called on transport write completion.

  • 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:

    socket.emitWithAck("myEvent", 1).timingOut(after: 1) {data in
        ...
    }
    

    Declaration

    Swift

    open func emitWithAck(_ event: String, _ items: SocketData...) -> OnAckCallback

    Parameters

    event

    The event to send.

    items

    The items to send with this event. May be left out.

    Return Value

    An OnAckCallback. You must call the timingOut(after:) method before the event will be sent.

  • 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.

    Declaration

    Swift

    open func emitAck(_ ack: Int, with items: [Any])

    Parameters

    ack

    The ack number.

    with

    The data for this ack.

  • Called when socket.io has acked one of our emits. Causes the corresponding ack callback to be called.

    Declaration

    Swift

    open func handleAck(_ ack: Int, data: [Any])

    Parameters

    ack

    The number for this ack.

    data

    The data sent back with this ack.

  • Called on socket.io specific events.

    Declaration

    Swift

    open func handleClientEvent(_ event: SocketClientEvent, data: [Any])

    Parameters

    event
    data

    The data for this event.

  • Called when we get an event from socket.io.

    Declaration

    Swift

    open func handleEvent(_ event: String, data: [Any], isInternalMessage: Bool, withAck ack: Int = -1)

    Parameters

    event

    The name of the event.

    data

    The data that was sent with this event.

    isInternalMessage

    Whether this event was sent internally. If true it is always sent to handlers.

    ack

    If > 0 then this event expects to get an ack back from the client.

  • Causes a client to handle a socket.io packet. The namespace for the packet must match the namespace of the socket.

    Declaration

    Swift

    open func handlePacket(_ packet: SocketPacket)

    Parameters

    packet

    The packet to handle.

  • Call when you wish to leave a namespace and disconnect this socket.

    Declaration

    Swift

    open func leaveNamespace()
  • Joins nsp. You shouldn’t need to call this directly, instead call connect.

    Declaration

    Swift

    open func joinNamespace(withPayload payload: [String : Any]? = nil)

    Parameters

    withPayload

    An optional payload sent on connect

  • 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.

    Declaration

    Swift

    open func off(clientEvent event: SocketClientEvent)

    Parameters

    clientEvent

    The event to remove handlers for.

  • 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.

    Declaration

    Swift

    open func off(_ event: String)

    Parameters

    event

    The event to remove handlers for.

  • 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.

    Declaration

    Swift

    open func off(id: UUID)

    Parameters

    id

    The UUID of the handler you wish to remove.

  • Adds a handler for an event.

    Declaration

    Swift

    @discardableResult
    open func on(_ event: String, callback: @escaping NormalCallback) -> UUID

    Parameters

    event

    The event name for this handler.

    callback

    The callback that will execute when this event is received.

    Return Value

    A unique id for the handler that can be used to remove it.

  • Adds a handler for a client event.

    Example:

    socket.on(clientEvent: .connect) {data, ack in
        ...
    }
    

    Declaration

    Swift

    @discardableResult
    open func on(clientEvent event: SocketClientEvent, callback: @escaping NormalCallback) -> UUID

    Parameters

    event

    The event for this handler.

    callback

    The callback that will execute when this event is received.

    Return Value

    A unique id for the handler that can be used to remove it.

  • Adds a single-use handler for a client event.

    Declaration

    Swift

    @discardableResult
    open func once(clientEvent event: SocketClientEvent, callback: @escaping NormalCallback) -> UUID

    Parameters

    clientEvent

    The event for this handler.

    callback

    The callback that will execute when this event is received.

    Return Value

    A unique id for the handler that can be used to remove it.

  • Adds a single-use handler for an event.

    Declaration

    Swift

    @discardableResult
    open func once(_ event: String, callback: @escaping NormalCallback) -> UUID

    Parameters

    event

    The event name for this handler.

    callback

    The callback that will execute when this event is received.

    Return Value

    A unique id for the handler that can be used to remove it.

  • Adds a handler that will be called on every event.

    Declaration

    Swift

    open func onAny(_ handler: @escaping (SocketAnyEvent) -> ())

    Parameters

    handler

    The callback that will execute whenever an event is received.

  • Removes all handlers.

    Can be used after disconnecting to break any potential remaining retain cycles.

    Declaration

    Swift

    open 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.

    Declaration

    Swift

    open func setReconnecting(reason: String)

    Parameters

    reason

    The reason this socket is reconnecting.

================================================ FILE: docs/Classes/SocketManager.html ================================================ SocketManager Class Reference

SocketIO 16.0.0 Docs (100% documented)

SocketManager

open class SocketManager : NSObject, SocketManagerSpec, SocketParsable, SocketDataBufferable, ConfigSettable

A manager for a socket.io connection.

A SocketManager is responsible for multiplexing multiple namespaces through a single SocketEngineSpec.

Example:

let manager = SocketManager(socketURL: URL(string:"http://localhost:8080/")!)
let defaultNamespaceSocket = manager.defaultSocket
let swiftSocket = manager.socket(forNamespace: "/swift")

// defaultNamespaceSocket and swiftSocket both share a single connection to the server

Sockets created through the manager are retained by the manager. So at the very least, a single strong reference to the manager must be maintained to keep sockets alive.

To disconnect a socket and remove it from the manager, either call SocketIOClient.disconnect() on the socket, or call one of the disconnectSocket methods on this class.

NOTE: The manager is not thread/queue safe, all interaction with the manager should be done on the handleQueue

Properties

  • The socket associated with the default namespace (“/”).

    Declaration

    Swift

    public var defaultSocket: SocketIOClient { get }
  • The URL of the socket.io server.

    If changed after calling init, forceNew must be set to true, or it will only connect to the url set in the init.

    Declaration

    Swift

    public let socketURL: URL
  • The configuration for this client.

    Some configs will not take affect until after a reconnect if set after calling a connect method.

    Declaration

    Swift

    public var config: SocketIOClientConfiguration { get set }
  • The engine for this manager.

    Declaration

    Swift

    public var engine: SocketEngineSpec?
  • If true then every time connect is called, a new engine will be created.

    Declaration

    Swift

    public var forceNew: 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.

    Declaration

    Swift

    public var handleQueue: DispatchQueue
  • The sockets in this manager indexed by namespace.

    Declaration

    Swift

    public var nsps: [String : SocketIOClient]
  • If true, this client will try and reconnect on any disconnects.

    Declaration

    Swift

    public var reconnects: Bool
  • The minimum number of seconds to wait before attempting to reconnect.

    Declaration

    Swift

    public var reconnectWait: Int
  • The maximum number of seconds to wait before attempting to reconnect.

    Declaration

    Swift

    public var reconnectWaitMax: Int
  • The randomization factor for calculating reconnect jitter.

    Declaration

    Swift

    public var randomizationFactor: Double
  • The status of this manager.

    Declaration

    Swift

    public private(set) var status: SocketIOStatus { get set }
  • Declaration

    Swift

    public private(set) var version: SocketIOVersion { get }
  • A list of packets that are waiting for binary data.

    The way that socket.io works all data should be sent directly after each packet. So this should ideally be an array of one packet waiting for data.

    This should not be modified directly.

    Declaration

    Swift

    public var waitingPackets: [SocketPacket]

Initializers

  • Type safe way to create a new SocketIOClient. opts can be omitted.

    Declaration

    Swift

    public init(socketURL: URL, config: SocketIOClientConfiguration = [])

    Parameters

    socketURL

    The url of the socket.io server.

    config

    The config for this socket.

  • Not so type safe way to create a SocketIOClient, meant for Objective-C compatiblity. If using Swift it’s recommended to use init(socketURL: NSURL, options: Set<SocketIOClientOption>)

    Declaration

    Swift

    @objc
    public convenience init(socketURL: URL, config: [String : Any]?)

    Parameters

    socketURL

    The url of the socket.io server.

    config

    The config for this socket.

Methods

  • Connects the underlying transport and the default namespace socket.

    Override if you wish to attach a custom SocketEngineSpec.

    Declaration

    Swift

    open func connect()
  • Connects a socket through this manager’s engine.

    Declaration

    Swift

    open func connectSocket(_ socket: SocketIOClient, withPayload payload: [String : Any]? = nil)

    Parameters

    socket

    The socket who we should connect through this manager.

    withPayload

    Optional payload to send on connect

  • Called when the manager has disconnected from socket.io.

    Declaration

    Swift

    open func didDisconnect(reason: String)

    Parameters

    reason

    The reason for the disconnection.

  • Disconnects the manager and all associated sockets.

    Declaration

    Swift

    open func disconnect()
  • Disconnects the given socket.

    This will remove the socket for the manager’s control, and make the socket instance useless and ready for releasing.

    Declaration

    Swift

    open func disconnectSocket(_ socket: SocketIOClient)

    Parameters

    socket

    The socket to disconnect.

  • Disconnects the socket associated with forNamespace.

    This will remove the socket for the manager’s control, and make the socket instance useless and ready for releasing.

    Declaration

    Swift

    open func disconnectSocket(forNamespace nsp: String)

    Parameters

    nsp

    The namespace to disconnect from.

  • Sends a client event to all sockets in nsps

    Declaration

    Swift

    open func emitAll(clientEvent event: SocketClientEvent, data: [Any])

    Parameters

    clientEvent

    The event to emit.

  • Sends an event to the server on all namespaces in this manager.

    Declaration

    Swift

    open func emitAll(_ event: String, _ items: SocketData...)

    Parameters

    event

    The event to send.

    items

    The data to send with this event.

  • Called when the engine closes.

    Declaration

    Swift

    open func engineDidClose(reason: String)

    Parameters

    reason

    The reason that the engine closed.

  • Called when the engine errors.

    Declaration

    Swift

    open func engineDidError(reason: String)

    Parameters

    reason

    The reason the engine errored.

  • Called when the engine opens.

    Declaration

    Swift

    open func engineDidOpen(reason: String)

    Parameters

    reason

    The reason the engine opened.

  • Called when the engine receives a ping message.

    Declaration

    Swift

    open func engineDidReceivePing()
  • Called when the sends a ping to the server.

    Declaration

    Swift

    open func engineDidSendPing()
  • Called when the engine receives a pong message.

    Declaration

    Swift

    open func engineDidReceivePong()
  • Called when the sends a pong to the server.

    Declaration

    Swift

    open func engineDidSendPong()
  • Called when when upgrading the http connection to a websocket connection.

    Declaration

    Swift

    open func engineDidWebsocketUpgrade(headers: [String : String])

    Parameters

    headers

    The http headers.

  • Called when the engine has a message that must be parsed.

    Declaration

    Swift

    open func parseEngineMessage(_ msg: String)

    Parameters

    msg

    The message that needs parsing.

  • Called when the engine receives binary data.

    Declaration

    Swift

    open func parseEngineBinaryData(_ data: Data)

    Parameters

    data

    The data the engine received.

  • Tries to reconnect to the server.

    This will cause a SocketClientEvent.reconnect event to be emitted, as well as SocketClientEvent.reconnectAttempt events.

    Declaration

    Swift

    open func reconnect()
  • Removes the socket from the manager’s control. One of the disconnect methods should be called before calling this method.

    After calling this method the socket should no longer be considered usable.

    Declaration

    Swift

    @discardableResult
    open func removeSocket(_ socket: SocketIOClient) -> SocketIOClient?

    Parameters

    socket

    The socket to remove.

    Return Value

    The socket removed, if it was owned by the manager.

  • Sets manager specific configs.

    parameter config: The configs that should be set.

    Declaration

    Swift

    open func setConfigs(_ config: SocketIOClientConfiguration)
  • Returns a SocketIOClient for the given namespace. This socket shares a transport with the manager.

    Calling multiple times returns the same socket.

    Sockets created from this method are retained by the manager. Call one of the disconnectSocket methods on this class to remove the socket from manager control. Or call SocketIOClient.disconnect() on the client.

    Declaration

    Swift

    open func socket(forNamespace nsp: String) -> SocketIOClient

    Parameters

    nsp

    The namespace for the socket.

    Return Value

    A SocketIOClient for the given namespace.

================================================ FILE: docs/Classes/SocketRawAckView.html ================================================ SocketRawAckView Class Reference

SocketIO 16.0.0 Docs (100% documented)

SocketRawAckView

public final class SocketRawAckView : NSObject

Class that gives a backwards compatible way to cause an emit not to recursively check for Data objects.

Usage:

ack.rawEmitView.with(myObject)
  • 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]

    Declaration

    Swift

    public func with(_ items: SocketData...)

    Parameters

    items

    A variable number of items to send when acking.

  • Call to ack receiving this event.

    Declaration

    Swift

    @objc
    public func with(_ items: [Any])

    Parameters

    items

    An array of items to send when acking. Use [] to send nothing.

================================================ FILE: docs/Classes/SocketRawView.html ================================================ SocketRawView Class Reference

SocketIO 16.0.0 Docs (100% documented)

SocketRawView

public final class SocketRawView : NSObject

Class that gives a backwards compatible way to cause an emit not to recursively check for Data objects.

Usage:

socket.rawEmitView.emit("myEvent", myObject)
  • 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]

    Declaration

    Swift

    public func emit(_ event: String, _ items: SocketData...)

    Parameters

    event

    The event to send.

    items

    The items to send with this event. May be left out.

  • Same as emit, but meant for Objective-C

    Declaration

    Swift

    @objc
    public func emit(_ event: String, with items: [Any])

    Parameters

    event

    The event to send.

    items

    The items to send with this event. Send an empty array to send no data.

  • 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:

    socket.emitWithAck("myEvent", 1).timingOut(after: 1) {data in
        ...
    }
    

    Declaration

    Swift

    public func emitWithAck(_ event: String, _ items: SocketData...) -> OnAckCallback

    Parameters

    event

    The event to send.

    items

    The items to send with this event. May be left out.

    Return Value

    An OnAckCallback. You must call the timingOut(after:) method before the event will be sent.

  • 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:

    socket.emitWithAck("myEvent", with: [1]).timingOut(after: 1) {data in
        ...
    }
    

    Declaration

    Swift

    @objc
    public func emitWithAck(_ event: String, with items: [Any]) -> OnAckCallback

    Parameters

    event

    The event to send.

    items

    The items to send with this event. Use [] to send nothing.

    Return Value

    An OnAckCallback. You must call the timingOut(after:) method before the event will be sent.

================================================ FILE: docs/Classes.html ================================================ Classes Reference

SocketIO 16.0.0 Docs (100% documented)

Classes

The following classes are available globally.

  • A class that represents a waiting ack call.

    NOTE: You should not store this beyond the life of the event handler.

    See more

    Declaration

    Swift

    public final class SocketAckEmitter : NSObject
  • A class that represents an emit that will request an ack that has not yet been sent. Call timingOut(after:callback:) to complete the emit Example:

    socket.emitWithAck("myEvent").timingOut(after: 1) {data in
        ...
    }
    
    See more

    Declaration

    Swift

    public final class OnAckCallback : NSObject
  • Represents some event that was received.

    See more

    Declaration

    Swift

    public final class SocketAnyEvent : NSObject
  • Represents a socket.io-client.

    Clients are created through a SocketManager, which owns the SocketEngineSpec that controls the connection to the server.

    For example:

    // 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

    See more

    Declaration

    Swift

    open class SocketIOClient : NSObject, SocketIOClientSpec
  • Class that gives a backwards compatible way to cause an emit not to recursively check for Data objects.

    Usage:

    socket.rawEmitView.emit("myEvent", myObject)
    
    See more

    Declaration

    Swift

    public final class SocketRawView : NSObject
  • Class that gives a backwards compatible way to cause an emit not to recursively check for Data objects.

    Usage:

    ack.rawEmitView.with(myObject)
    
    See more

    Declaration

    Swift

    public final class SocketRawAckView : NSObject
  • The class that handles the engine.io protocol and transports. See SocketEnginePollable and SocketEngineWebsocket for transport specific methods.

    See more

    Declaration

    Swift

    open class SocketEngine:
            NSObject, WebSocketDelegate, URLSessionDelegate, SocketEnginePollable, SocketEngineWebsocket, ConfigSettable
  • A manager for a socket.io connection.

    A SocketManager is responsible for multiplexing multiple namespaces through a single SocketEngineSpec.

    Example:

    let manager = SocketManager(socketURL: URL(string:"http://localhost:8080/")!)
    let defaultNamespaceSocket = manager.defaultSocket
    let swiftSocket = manager.socket(forNamespace: "/swift")
    
    // defaultNamespaceSocket and swiftSocket both share a single connection to the server
    

    Sockets created through the manager are retained by the manager. So at the very least, a single strong reference to the manager must be maintained to keep sockets alive.

    To disconnect a socket and remove it from the manager, either call SocketIOClient.disconnect() on the socket, or call one of the disconnectSocket methods on this class.

    NOTE: The manager is not thread/queue safe, all interaction with the manager should be done on the handleQueue

    See more

    Declaration

    Swift

    open class SocketManager : NSObject, SocketManagerSpec, SocketParsable, SocketDataBufferable, ConfigSettable
================================================ FILE: docs/Enums/SocketAckStatus.html ================================================ SocketAckStatus Enumeration Reference

SocketIO 16.0.0 Docs (100% documented)

SocketAckStatus

public enum SocketAckStatus : String

The status of an ack.

Cases

  • The ack timed out.

    Declaration

    Swift

    case noAck = "NO ACK"
  • Tests whether a string is equal to a given SocketAckStatus

    Declaration

    Swift

    public static func == (lhs: String, rhs: SocketAckStatus) -> Bool
  • Tests whether a string is equal to a given SocketAckStatus

    Declaration

    Swift

    public static func == (lhs: SocketAckStatus, rhs: String) -> Bool
================================================ FILE: docs/Enums/SocketClientEvent.html ================================================ SocketClientEvent Enumeration Reference

SocketIO 16.0.0 Docs (100% documented)

SocketClientEvent

public enum SocketClientEvent : String

The set of events that are generated by the client.

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.

    socket.on(clientEvent: .connect) {data, ack in
        guard let nsp = data[0] as? String else { return }
        // Some logic using the nsp
    }
    

    Declaration

    Swift

    case connect
  • Emitted when the socket has disconnected and will not attempt to try to reconnect.

    Usage:

    socket.on(clientEvent: .disconnect) {data, ack in
        // Some cleanup logic
    }
    

    Declaration

    Swift

    case disconnect
  • Emitted when an error occurs.

    Usage:

    socket.on(clientEvent: .error) {data, ack in
        // Some logging
    }
    

    Declaration

    Swift

    case error
  • Emitted whenever the engine sends a ping.

    Usage:

    socket.on(clientEvent: .ping) {_, _ in
      // Maybe keep track of latency?
    }
    

    Declaration

    Swift

    case ping
  • Emitted whenever the engine gets a pong.

    Usage:

    socket.on(clientEvent: .pong) {_, _ in
      // Maybe keep track of latency?
    }
    

    Declaration

    Swift

    case pong
  • Emitted when the client begins the reconnection process.

    Usage:

    socket.on(clientEvent: .reconnect) {data, ack in
        // Some reconnect event logic
    }
    

    Declaration

    Swift

    case reconnect
  • Emitted each time the client tries to reconnect to the server.

    Usage:

    socket.on(clientEvent: .reconnectAttempt) {data, ack in
        // Some reconnect attempt logging
    }
    

    Declaration

    Swift

    case reconnectAttempt
  • Emitted every time there is a change in the client’s status.

    The payload for data is [SocketIOClientStatus, Int]. Where the second item is the raw value. Use the second one if you are working in Objective-C.

    Usage:

    socket.on(clientEvent: .statusChange) {data, ack in
        // Some status changing logging
    }
    

    Declaration

    Swift

    case statusChange
  • Emitted when when upgrading the http connection to a websocket connection.

    Usage:

    socket.on(clientEvent: .websocketUpgrade) {data, ack in
        let headers = (data as [Any])[0]
        // Some header logic
    }
    

    Declaration

    Swift

    case websocketUpgrade
================================================ FILE: docs/Enums/SocketEnginePacketType.html ================================================ SocketEnginePacketType Enumeration Reference

SocketIO 16.0.0 Docs (100% documented)

SocketEnginePacketType

@objc
public enum SocketEnginePacketType : Int

Represents the type of engine.io packet types.

  • Open message.

    Declaration

    Swift

    case open
  • Close message.

    Declaration

    Swift

    case close
  • Ping message.

    Declaration

    Swift

    case ping
  • Pong message.

    Declaration

    Swift

    case pong
  • Regular message.

    Declaration

    Swift

    case message
  • Upgrade message.

    Declaration

    Swift

    case upgrade
  • NOOP.

    Declaration

    Swift

    case noop
================================================ FILE: docs/Enums/SocketIOClientOption.html ================================================ SocketIOClientOption Enumeration Reference

SocketIO 16.0.0 Docs (100% documented)

SocketIOClientOption

public enum SocketIOClientOption : ClientOption

The options for a client.

  • If given, the WebSocket transport will attempt to use compression.

    Declaration

    Swift

    case compress
  • A dictionary of GET parameters that will be included in the connect url.

    Declaration

    Swift

    case connectParams([String : Any])
  • An array of cookies that will be sent during the initial connection.

    Declaration

    Swift

    case cookies([HTTPCookie])
  • Any extra HTTP headers that should be sent during the initial connection.

    Declaration

    Swift

    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.

    Declaration

    Swift

    case forceNew(Bool)
  • If passed true, the only transport that will be used will be HTTP long-polling.

    Declaration

    Swift

    case forcePolling(Bool)
  • If passed true, the only transport that will be used will be WebSockets.

    Declaration

    Swift

    case forceWebsockets(Bool)
  • If passed true, the WebSocket stream will be configured with the enableSOCKSProxy true.

    Declaration

    Swift

    case enableSOCKSProxy(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.

    Declaration

    Swift

    case handleQueue(DispatchQueue)
  • If passed true, the client will log debug information. This should be turned off in production code.

    Declaration

    Swift

    case log(Bool)
  • Used to pass in a custom logger.

    Declaration

    Swift

    case logger(SocketLogger)
  • A custom path to socket.io. Only use this if the socket.io server is configured to look for this path.

    Declaration

    Swift

    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.

    Declaration

    Swift

    case reconnects(Bool)
  • The number of times to try and reconnect before giving up. Pass -1 to never give up.

    Declaration

    Swift

    case reconnectAttempts(Int)
  • The minimum number of seconds to wait before reconnect attempts.

    Declaration

    Swift

    case reconnectWait(Int)
  • The maximum number of seconds to wait before reconnect attempts.

    Declaration

    Swift

    case reconnectWaitMax(Int)
  • The randomization factor for calculating reconnect jitter.

    Declaration

    Swift

    case randomizationFactor(Double)
  • Set true if your server is using secure transports.

    Declaration

    Swift

    case secure(Bool)
  • Allows you to set which certs are valid. Useful for SSL pinning.

    Declaration

    Swift

    case security(CertificatePinning)
  • If you’re using a self-signed set. Only use for development.

    Declaration

    Swift

    case selfSigned(Bool)
  • Sets an NSURLSessionDelegate for the underlying engine. Useful if you need to handle self-signed certs.

    Declaration

    Swift

    case sessionDelegate(URLSessionDelegate)
  • The version of socket.io being used. This should match the server version. Default is 3.

    Declaration

    Swift

    case version(SocketIOVersion)

Properties

  • The description of this option.

    Declaration

    Swift

    public var description: String { get }

Operators

  • Compares whether two options are the same.

    Declaration

    Swift

    public static func == (lhs: SocketIOClientOption, rhs: SocketIOClientOption) -> Bool

    Parameters

    lhs

    Left operand to compare.

    rhs

    Right operand to compare.

    Return Value

    true if the two are the same option.

================================================ FILE: docs/Enums/SocketIOClientStatus.html ================================================ SocketIOClientStatus Enumeration Reference

SocketIO Docs (100% documented)

SocketIOClientStatus

@objc public enum SocketIOClientStatus : Int

Represents the state of the client.

  • The client has never been connected. Or the client has been reset.

    Declaration

    Swift

    case notConnected
  • The client was once connected, but not anymore.

    Declaration

    Swift

    case disconnected
  • The client is in the process of connecting.

    Declaration

    Swift

    case connecting
  • The client is currently connected.

    Declaration

    Swift

    case connected
================================================ FILE: docs/Enums/SocketIOStatus.html ================================================ SocketIOStatus Enumeration Reference

SocketIO 16.0.0 Docs (100% documented)

SocketIOStatus

@objc
public enum SocketIOStatus : Int, CustomStringConvertible

Represents state of a manager or client.

Cases

  • The client/manager has never been connected. Or the client has been reset.

    Declaration

    Swift

    case notConnected
  • The client/manager was once connected, but not anymore.

    Declaration

    Swift

    case disconnected
  • The client/manager is in the process of connecting.

    Declaration

    Swift

    case connecting
  • The client/manager is currently connected.

    Declaration

    Swift

    case connected

Properties

  • Declaration

    Swift

    public var active: Bool { get }

    Return Value

    True if this client/manager is connected/connecting to a server.

  • Declaration

    Swift

    public var description: String { get }
================================================ FILE: docs/Enums/SocketIOVersion.html ================================================ SocketIOVersion Enumeration Reference

SocketIO 16.0.0 Docs (100% documented)

================================================ FILE: docs/Enums/SocketParsableError.html ================================================ SocketParsableError Enumeration Reference

SocketIO 16.0.0 Docs (100% documented)

================================================ FILE: docs/Enums.html ================================================ Enumerations Reference

SocketIO 16.0.0 Docs (100% documented)

Enumerations

The following enumerations are available globally.

  • The status of an ack.

    See more

    Declaration

    Swift

    public enum SocketAckStatus : String
  • The socket.io version being used.

    See more

    Declaration

    Swift

    public enum SocketIOVersion : Int
  • The options for a client.

    See more

    Declaration

    Swift

    public enum SocketIOClientOption : ClientOption
  • The set of events that are generated by the client.

    See more

    Declaration

    Swift

    public enum SocketClientEvent : String
  • Represents state of a manager or client.

    See more

    Declaration

    Swift

    @objc
    public enum SocketIOStatus : Int, CustomStringConvertible
  • Represents the type of engine.io packet types.

    See more

    Declaration

    Swift

    @objc
    public enum SocketEnginePacketType : Int
  • Errors that can be thrown during parsing.

    See more

    Declaration

    Swift

    public enum SocketParsableError : Error
================================================ FILE: docs/Extensions.html ================================================ Extensions Reference

SocketIO 16.0.0 Docs (100% documented)

Extensions

The following extensions are available globally.

================================================ FILE: docs/Guides.html ================================================ Guides Reference

SocketIO 16.0.0 Docs (100% documented)

================================================ FILE: docs/Protocols/ConfigSettable.html ================================================ ConfigSettable Protocol Reference

SocketIO 16.0.0 Docs (100% documented)

================================================ FILE: docs/Protocols/SocketData.html ================================================ SocketData Protocol Reference

SocketIO 16.0.0 Docs (100% documented)

SocketData

public protocol SocketData

A marking protocol that says a type can be represented in a socket.io packet.

Example:

struct CustomData : SocketData {
   let name: String
   let age: Int

   func socketRepresentation() -> SocketData {
       return ["name": name, "age": age]
   }
}

socket.emit("myEvent", CustomData(name: "Erik", age: 24))

Methods

  • socketRepresentation() Default implementation

    A representation of self that can sent over socket.io.

    Default Implementation

    Default implementation. Only works for native Swift types and a few Foundation types.

    Declaration

    Swift

    func socketRepresentation() throws -> SocketData
================================================ FILE: docs/Protocols/SocketDataBufferable.html ================================================ SocketDataBufferable Protocol Reference

SocketIO 16.0.0 Docs (100% documented)

SocketDataBufferable

public protocol SocketDataBufferable : AnyObject

Says that a type will be able to buffer binary data before all data for an event has come in.

Properties

  • A list of packets that are waiting for binary data.

    The way that socket.io works all data should be sent directly after each packet. So this should ideally be an array of one packet waiting for data.

    This should not be modified directly.

    Declaration

    Swift

    var waitingPackets: [SocketPacket] { get set }
================================================ FILE: docs/Protocols/SocketEngineClient.html ================================================ SocketEngineClient Protocol Reference

SocketIO 16.0.0 Docs (100% documented)

SocketEngineClient

@objc
public protocol SocketEngineClient

Declares that a type will be a delegate to an engine.

Methods

  • Called when the engine errors.

    Declaration

    Swift

    func engineDidError(reason: String)

    Parameters

    reason

    The reason the engine errored.

  • Called when the engine closes.

    Declaration

    Swift

    func engineDidClose(reason: String)

    Parameters

    reason

    The reason that the engine closed.

  • Called when the engine opens.

    Declaration

    Swift

    func engineDidOpen(reason: String)

    Parameters

    reason

    The reason the engine opened.

  • Called when the engine receives a ping message. Only called in socket.io >3.

    Declaration

    Swift

    func engineDidReceivePing()
  • Called when the engine receives a pong message. Only called in socket.io 2.

    Declaration

    Swift

    func engineDidReceivePong()
  • Called when the engine sends a ping to the server. Only called in socket.io 2.

    Declaration

    Swift

    func engineDidSendPing()
  • Called when the engine sends a pong to the server. Only called in socket.io >3.

    Declaration

    Swift

    func engineDidSendPong()
  • Called when the engine has a message that must be parsed.

    Declaration

    Swift

    func parseEngineMessage(_ msg: String)

    Parameters

    msg

    The message that needs parsing.

  • Called when the engine receives binary data.

    Declaration

    Swift

    func parseEngineBinaryData(_ data: Data)

    Parameters

    data

    The data the engine received.

  • Called when when upgrading the http connection to a websocket connection.

    Declaration

    Swift

    func engineDidWebsocketUpgrade(headers: [String : String])

    Parameters

    headers

    The http headers.

================================================ FILE: docs/Protocols/SocketEnginePollable.html ================================================ SocketEnginePollable Protocol Reference

SocketIO 16.0.0 Docs (100% documented)

SocketEnginePollable

public protocol SocketEnginePollable : SocketEngineSpec

Protocol that is used to implement socket.io polling support

Properties

  • true If engine’s session has been invalidated.

    Declaration

    Swift

    var invalidated: Bool { get }
  • A queue of engine.io messages waiting for POSTing

    You should not touch this directly

    Declaration

    Swift

    var postWait: [Post] { get set }
  • The URLSession that will be used for polling.

    Declaration

    Swift

    var session: URLSession? { get }
  • true if there is an outstanding poll. Trying to poll before the first is done will cause socket.io to disconnect us.

    Do not touch this directly

    Declaration

    Swift

    var waitingForPoll: Bool { get set }
  • true if there is an outstanding post. Trying to post before the first is done will cause socket.io to disconnect us.

    Do not touch this directly

    Declaration

    Swift

    var waitingForPost: Bool { get set }

Methods

  • doPoll() Default implementation

    Call to send a long-polling request.

    You shouldn’t need to call this directly, the engine should automatically maintain a long-poll request.

    Default Implementation

    Call to send a long-polling request.

    You shouldn’t need to call this directly, the engine should automatically maintain a long-poll request.

    Declaration

    Swift

    func doPoll()
  • Sends an engine.io message through the polling transport.

    You shouldn’t call this directly, instead call the write method on SocketEngine.

    Default Implementation

    Sends an engine.io message through the polling transport.

    You shouldn’t call this directly, instead call the write method on SocketEngine.

    Declaration

    Swift

    func sendPollMessage(_ message: String, withType type: SocketEnginePacketType, withData datas: [Data], completion: (() -> ())?)

    Parameters

    message

    The message to send.

    withType

    The type of message to send.

    withData

    The data associated with this message.

  • stopPolling() Default implementation

    Call to stop polling and invalidate the URLSession.

    Default Implementation

    Call to stop polling and invalidate the URLSession.

    Declaration

    Swift

    func stopPolling()
================================================ FILE: docs/Protocols/SocketEngineSpec.html ================================================ SocketEngineSpec Protocol Reference

SocketIO 16.0.0 Docs (100% documented)

SocketEngineSpec

public protocol SocketEngineSpec : AnyObject

Specifies a SocketEngine.

Properties

  • The client for this engine.

    Declaration

    Swift

    var client: SocketEngineClient? { get set }
  • true if this engine is closed.

    Declaration

    Swift

    var closed: Bool { get }
  • If true the engine will attempt to use WebSocket compression.

    Declaration

    Swift

    var compress: Bool { get }
  • true if this engine is connected. Connected means that the initial poll connect has succeeded.

    Declaration

    Swift

    var connected: Bool { get }
  • The connect parameters sent during a connect.

    Declaration

    Swift

    var connectParams: [String : Any]? { get set }
  • An array of HTTPCookies that are sent during the connection.

    Declaration

    Swift

    var cookies: [HTTPCookie]? { get }
  • The queue that all engine actions take place on.

    Declaration

    Swift

    var engineQueue: DispatchQueue { get }
  • A dictionary of extra http headers that will be set during connection.

    Declaration

    Swift

    var extraHeaders: [String : String]? { get set }
  • When true, the engine is in the process of switching to WebSockets.

    Declaration

    Swift

    var fastUpgrade: Bool { get }
  • When true, the engine will only use HTTP long-polling as a transport.

    Declaration

    Swift

    var forcePolling: Bool { get }
  • When true, the engine will only use WebSockets as a transport.

    Declaration

    Swift

    var forceWebsockets: Bool { get }
  • If true, the engine is currently in HTTP long-polling mode.

    Declaration

    Swift

    var polling: Bool { get }
  • If true, the engine is currently seeing whether it can upgrade to WebSockets.

    Declaration

    Swift

    var probing: Bool { get }
  • sid

    The session id for this engine.

    Declaration

    Swift

    var sid: String { get }
  • The path to engine.io.

    Declaration

    Swift

    var socketPath: String { get }
  • The url for polling.

    Declaration

    Swift

    var urlPolling: URL { get }
  • The url for WebSockets.

    Declaration

    Swift

    var urlWebSocket: URL { get }
  • The version of engine.io being used. Default is three.

    Declaration

    Swift

    var version: SocketIOVersion { get }
  • If true, then the engine is currently in WebSockets mode.

    Declaration

    Swift

    @available(*, deprecated, message: "No longer needed, if we're not polling, then we must be doing websockets")
    var websocket: Bool { get }
  • ws

    The WebSocket for this engine.

    Declaration

    Swift

    var ws: WebSocket? { get }

Initializers

  • Creates a new engine.

    Declaration

    Swift

    init(client: SocketEngineClient, url: URL, options: [String : Any]?)

    Parameters

    client

    The client for this engine.

    url

    The url for this engine.

    options

    The options for this engine.

Methods

  • Starts the connection to the server.

    Declaration

    Swift

    func connect()
  • Called when an error happens during execution. Causes a disconnection.

    Declaration

    Swift

    func didError(reason: String)
  • Disconnects from the server.

    Declaration

    Swift

    func disconnect(reason: String)

    Parameters

    reason

    The reason for the disconnection. This is communicated up to the client.

  • Called to switch from HTTP long-polling to WebSockets. After calling this method the engine will be in WebSocket mode.

    You shouldn’t call this directly

    Declaration

    Swift

    func doFastUpgrade()
  • Causes any packets that were waiting for POSTing to be sent through the WebSocket. This happens because when the engine is attempting to upgrade to WebSocket it does not do any POSTing.

    You shouldn’t call this directly

    Declaration

    Swift

    func flushWaitingForPostToWebSocket()
  • Parses raw binary received from engine.io.

    Declaration

    Swift

    func parseEngineData(_ data: Data)

    Parameters

    data

    The data to parse.

  • Parses a raw engine.io packet.

    Declaration

    Swift

    func parseEngineMessage(_ message: String)

    Parameters

    message

    The message to parse.

  • Writes a message to engine.io, independent of transport.

    Declaration

    Swift

    func write(_ msg: String, withType type: SocketEnginePacketType, withData data: [Data], completion: (() -> ())?)

    Parameters

    msg

    The message to send.

    type

    The type of this message.

    data

    Any data that this message has.

    completion

    Callback called on transport write completion.

================================================ FILE: docs/Protocols/SocketEngineWebsocket.html ================================================ SocketEngineWebsocket Protocol Reference

SocketIO 16.0.0 Docs (100% documented)

SocketEngineWebsocket

public protocol SocketEngineWebsocket : SocketEngineSpec

Protocol that is used to implement socket.io WebSocket support

Properties

  • Whether or not the ws is connected

    Declaration

    Swift

    var wsConnected: Bool { get }

Methods

  • Sends an engine.io message through the WebSocket transport.

    You shouldn’t call this directly, instead call the write method on SocketEngine.

    Default Implementation

    Sends an engine.io message through the WebSocket transport.

    You shouldn’t call this directly, instead call the write method on SocketEngine.

    Declaration

    Swift

    func sendWebSocketMessage(_ str: String,
                              withType type: SocketEnginePacketType,
                              withData datas: [Data],
                              completion: (() -> ())?)

    Parameters

    message

    The message to send.

    withType

    The type of message to send.

    withData

    The data associated with this message.

    completion

    Callback called on transport write completion.

================================================ FILE: docs/Protocols/SocketIOClientSpec.html ================================================ SocketIOClientSpec Protocol Reference

SocketIO 16.0.0 Docs (100% documented)

SocketIOClientSpec

public protocol SocketIOClientSpec : AnyObject

Defines the interface for a SocketIOClient.

Properties

  • A handler that will be called on any event.

    Declaration

    Swift

    var anyHandler: ((SocketAnyEvent) -> ())? { get }
  • The array of handlers for this socket.

    Declaration

    Swift

    var handlers: [SocketEventHandler] { get }
  • The manager for this socket.

    Declaration

    Swift

    var manager: SocketManagerSpec? { get }
  • nsp

    The namespace that this socket is currently connected to.

    Must start with a /.

    Declaration

    Swift

    var nsp: String { get }
  • A view into this socket where emits do not check for binary data.

    Usage:

    socket.rawEmitView.emit("myEvent", myObject)
    

    NOTE: It is not safe to hold on to this view beyond the life of the socket.

    Declaration

    Swift

    var rawEmitView: SocketRawView { get }
  • sid

    The id of this socket.io connect. This is different from the sid of the engine.io connection.

    Declaration

    Swift

    var sid: String? { get }
  • The status of this client.

    Declaration

    Swift

    var status: SocketIOStatus { get }

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.

    Declaration

    Swift

    func connect(withPayload payload: [String : Any]?)

    Parameters

    payload

    An optional payload sent on 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.

    Declaration

    Swift

    func connect(withPayload payload: [String : Any]?, timeoutAfter: Double, withHandler handler: (() -> ())?)

    Parameters

    withPayload

    An optional payload sent on connect

    timeoutAfter

    The number of seconds after which if we are not connected we assume the connection has failed. Pass 0 to never timeout.

    handler

    The handler to call when the client fails to connect.

  • 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.

    Declaration

    Swift

    func didConnect(toNamespace namespace: String, payload: [String : Any]?)

    Parameters

    toNamespace

    The namespace that was connected to.

  • Called when the client has disconnected from socket.io.

    Declaration

    Swift

    func didDisconnect(reason: String)

    Parameters

    reason

    The reason for the disconnection.

  • didError(reason:) Default implementation

    Called when the client encounters an error.

    Default Implementation

    Default implementation.

    Declaration

    Swift

    func didError(reason: String)

    Parameters

    reason

    The reason for the disconnection.

  • Disconnects the socket.

    Declaration

    Swift

    func disconnect()
  • Send an event to the server, with optional data items and optional write completion handler.

    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]

    Declaration

    Swift

    func emit(_ event: String, _ items: SocketData..., completion: (() -> ())?)

    Parameters

    event

    The event to send.

    items

    The items to send with this event. May be left out.

    completion

    Callback called on transport write completion.

  • Call when you wish to tell the server that you’ve received the event for ack.

    Declaration

    Swift

    func emitAck(_ ack: Int, with items: [Any])

    Parameters

    ack

    The ack number.

    with

    The data for this ack.

  • 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:

    socket.emitWithAck("myEvent", 1).timingOut(after: 1) {data in
        ...
    }
    

    Declaration

    Swift

    func emitWithAck(_ event: String, _ items: SocketData...) -> OnAckCallback

    Parameters

    event

    The event to send.

    items

    The items to send with this event. May be left out.

    Return Value

    An OnAckCallback. You must call the timingOut(after:) method before the event will be sent.

  • Called when socket.io has acked one of our emits. Causes the corresponding ack callback to be called.

    Declaration

    Swift

    func handleAck(_ ack: Int, data: [Any])

    Parameters

    ack

    The number for this ack.

    data

    The data sent back with this ack.

  • Called on socket.io specific events.

    Declaration

    Swift

    func handleClientEvent(_ event: SocketClientEvent, data: [Any])

    Parameters

    event
    data

    The data for this event.

  • Called when we get an event from socket.io.

    Declaration

    Swift

    func handleEvent(_ event: String, data: [Any], isInternalMessage: Bool, withAck ack: Int)

    Parameters

    event

    The name of the event.

    data

    The data that was sent with this event.

    isInternalMessage

    Whether this event was sent internally. If true it is always sent to handlers.

    ack

    If > 0 then this event expects to get an ack back from the client.

  • Causes a client to handle a socket.io packet. The namespace for the packet must match the namespace of the socket.

    Declaration

    Swift

    func handlePacket(_ packet: SocketPacket)

    Parameters

    packet

    The packet to handle.

  • Call when you wish to leave a namespace and disconnect this socket.

    Declaration

    Swift

    func leaveNamespace()
  • Joins nsp. You shouldn’t need to call this directly, instead call connect.

    Declaration

    Swift

    func joinNamespace(withPayload payload: [String : Any]?)

    Parameters

    withPayload

    The payload to connect when joining this namespace

  • 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.

    Declaration

    Swift

    func off(clientEvent event: SocketClientEvent)

    Parameters

    clientEvent

    The event to remove handlers for.

  • 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.

    Declaration

    Swift

    func off(_ event: String)

    Parameters

    event

    The event to remove handlers for.

  • 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.

    Declaration

    Swift

    func off(id: UUID)

    Parameters

    id

    The UUID of the handler you wish to remove.

  • Adds a handler for an event.

    Declaration

    Swift

    func on(_ event: String, callback: @escaping NormalCallback) -> UUID

    Parameters

    event

    The event name for this handler.

    callback

    The callback that will execute when this event is received.

    Return Value

    A unique id for the handler that can be used to remove it.

  • Adds a handler for a client event.

    Example:

    socket.on(clientEvent: .connect) {data, ack in
        ...
    }
    

    Declaration

    Swift

    func on(clientEvent event: SocketClientEvent, callback: @escaping NormalCallback) -> UUID

    Parameters

    event

    The event for this handler.

    callback

    The callback that will execute when this event is received.

    Return Value

    A unique id for the handler that can be used to remove it.

  • Adds a single-use handler for a client event.

    Declaration

    Swift

    func once(clientEvent event: SocketClientEvent, callback: @escaping NormalCallback) -> UUID

    Parameters

    clientEvent

    The event for this handler.

    callback

    The callback that will execute when this event is received.

    Return Value

    A unique id for the handler that can be used to remove it.

  • Adds a single-use handler for an event.

    Declaration

    Swift

    func once(_ event: String, callback: @escaping NormalCallback) -> UUID

    Parameters

    event

    The event name for this handler.

    callback

    The callback that will execute when this event is received.

    Return Value

    A unique id for the handler that can be used to remove it.

  • Adds a handler that will be called on every event.

    Declaration

    Swift

    func onAny(_ handler: @escaping (SocketAnyEvent) -> ())

    Parameters

    handler

    The callback that will execute whenever an event is received.

  • Removes all handlers.

    Can be used after disconnecting to break any potential remaining retain cycles.

    Declaration

    Swift

    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.

    Declaration

    Swift

    func setReconnecting(reason: String)
================================================ FILE: docs/Protocols/SocketLogger.html ================================================ SocketLogger Protocol Reference

SocketIO 16.0.0 Docs (100% documented)

SocketLogger

public protocol SocketLogger : AnyObject

Represents a class will log client events.

Properties

  • log

    Whether to log or not

    Declaration

    Swift

    var log: Bool { get set }

Methods

  • log(_:type:) Default implementation

    Normal log messages

    Default Implementation

    Default implementation.

    Declaration

    Swift

    func log(_ message: @autoclosure () -> String, type: String)

    Parameters

    message

    The message being logged. Can include %@ that will be replaced with args

    type

    The type of entity that called for logging.

    args

    Any args that should be inserted into the message. May be left out.

  • error(_:type:) Default implementation

    Error Messages

    Default Implementation

    Default implementation.

    Declaration

    Swift

    func error(_ message: @autoclosure () -> String, type: String)

    Parameters

    message

    The message being logged. Can include %@ that will be replaced with args

    type

    The type of entity that called for logging.

    args

    Any args that should be inserted into the message. May be left out.

================================================ FILE: docs/Protocols/SocketManagerSpec.html ================================================ SocketManagerSpec Protocol Reference

SocketIO 16.0.0 Docs (100% documented)

SocketManagerSpec

public protocol SocketManagerSpec : AnyObject, SocketEngineClient

A manager for a socket.io connection.

A SocketManagerSpec is responsible for multiplexing multiple namespaces through a single SocketEngineSpec.

Example with SocketManager:

let manager = SocketManager(socketURL: URL(string:"http://localhost:8080/")!)
let defaultNamespaceSocket = manager.defaultSocket
let swiftSocket = manager.socket(forNamespace: "/swift")

// defaultNamespaceSocket and swiftSocket both share a single connection to the server

Sockets created through the manager are retained by the manager. So at the very least, a single strong reference to the manager must be maintained to keep sockets alive.

To disconnect a socket and remove it from the manager, either call SocketIOClient.disconnect() on the socket, or call one of the disconnectSocket methods on this class.

Properties

  • Returns the socket associated with the default namespace (“/”).

    Declaration

    Swift

    var defaultSocket: SocketIOClient { get }
  • The engine for this manager.

    Declaration

    Swift

    var engine: SocketEngineSpec? { get set }
  • If true then every time connect is called, a new engine will be created.

    Declaration

    Swift

    var forceNew: Bool { get set }
  • The queue that all interaction with the client should occur on. This is the queue that event handlers are called on.

    Declaration

    Swift

    var handleQueue: DispatchQueue { get set }
  • The sockets in this manager indexed by namespace.

    Declaration

    Swift

    var nsps: [String : SocketIOClient] { get set }
  • If true, this manager will try and reconnect on any disconnects.

    Declaration

    Swift

    var reconnects: Bool { get set }
  • The minimum number of seconds to wait before attempting to reconnect.

    Declaration

    Swift

    var reconnectWait: Int { get set }
  • The maximum number of seconds to wait before attempting to reconnect.

    Declaration

    Swift

    var reconnectWaitMax: Int { get set }
  • The randomization factor for calculating reconnect jitter.

    Declaration

    Swift

    var randomizationFactor: Double { get set }
  • The URL of the socket.io server.

    Declaration

    Swift

    var socketURL: URL { get }
  • The status of this manager.

    Declaration

    Swift

    var status: SocketIOStatus { get }
  • The version of socket.io in use.

    Declaration

    Swift

    var version: SocketIOVersion { get }

Methods

  • Connects the underlying transport.

    Declaration

    Swift

    func connect()
  • Connects a socket through this manager’s engine.

    Declaration

    Swift

    func connectSocket(_ socket: SocketIOClient, withPayload: [String : Any]?)

    Parameters

    socket

    The socket who we should connect through this manager.

    withPayload

    Optional payload to send on connect

  • Called when the manager has disconnected from socket.io.

    Declaration

    Swift

    func didDisconnect(reason: String)

    Parameters

    reason

    The reason for the disconnection.

  • Disconnects the manager and all associated sockets.

    Declaration

    Swift

    func disconnect()
  • Disconnects the given socket.

    Declaration

    Swift

    func disconnectSocket(_ socket: SocketIOClient)

    Parameters

    socket

    The socket to disconnect.

  • Disconnects the socket associated with forNamespace.

    Declaration

    Swift

    func disconnectSocket(forNamespace nsp: String)

    Parameters

    nsp

    The namespace to disconnect from.

  • Sends an event to the server on all namespaces in this manager.

    Declaration

    Swift

    func emitAll(_ event: String, _ items: SocketData...)

    Parameters

    event

    The event to send.

    items

    The data to send with this event.

  • Tries to reconnect to the server.

    This will cause a disconnect event to be emitted, as well as an reconnectAttempt event.

    Declaration

    Swift

    func reconnect()
  • Removes the socket from the manager’s control. After calling this method the socket should no longer be considered usable.

    Declaration

    Swift

    @discardableResult
    func removeSocket(_ socket: SocketIOClient) -> SocketIOClient?

    Parameters

    socket

    The socket to remove.

    Return Value

    The socket removed, if it was owned by the manager.

  • Returns a SocketIOClient for the given namespace. This socket shares a transport with the manager.

    Calling multiple times returns the same socket.

    Sockets created from this method are retained by the manager. Call one of the disconnectSocket methods on the implementing class to remove the socket from manager control. Or call SocketIOClient.disconnect() on the client.

    Declaration

    Swift

    func socket(forNamespace nsp: String) -> SocketIOClient

    Parameters

    nsp

    The namespace for the socket.

    Return Value

    A SocketIOClient for the given namespace.

================================================ FILE: docs/Protocols/SocketParsable.html ================================================ SocketParsable Protocol Reference

SocketIO 16.0.0 Docs (100% documented)

SocketParsable

public protocol SocketParsable : AnyObject

Defines that a type will be able to parse socket.io-protocol messages.

Methods

  • Called when the engine has received some binary data that should be attached to a packet.

    Packets binary data should be sent directly after the packet that expects it, so there’s confusion over where the data should go. Data should be received in the order it is sent, so that the correct data is put into the correct placeholder.

    Declaration

    Swift

    func parseBinaryData(_ data: Data) -> SocketPacket?

    Parameters

    data

    The data that should be attached to a packet.

  • Called when the engine has received a string that should be parsed into a socket.io packet.

    Declaration

    Swift

    func parseSocketMessage(_ message: String) -> SocketPacket?

    Parameters

    message

    The string that needs parsing.

    Return Value

    A completed socket packet if there is no more data left to collect.

Available where Self: SocketManagerSpec & SocketDataBufferable

  • parseSocketMessage(_:) Default implementation

    Default Implementation

    Called when the engine has received a string that should be parsed into a socket.io packet.

    Declaration

    Swift

    func parseSocketMessage(_ message: String) -> SocketPacket?

    Parameters

    message

    The string that needs parsing.

    Return Value

    A completed socket packet or nil if the packet is invalid.

  • parseBinaryData(_:) Default implementation

    Default Implementation

    Called when the engine has received some binary data that should be attached to a packet.

    Packets binary data should be sent directly after the packet that expects it, so there’s confusion over where the data should go. Data should be received in the order it is sent, so that the correct data is put into the correct placeholder.

    Declaration

    Swift

    func parseBinaryData(_ data: Data) -> SocketPacket?

    Parameters

    data

    The data that should be attached to a packet.

    Return Value

    A completed socket packet if there is no more data left to collect.

================================================ FILE: docs/Protocols.html ================================================ Protocols Reference

SocketIO 16.0.0 Docs (100% documented)

Protocols

The following protocols are available globally.

  • Declares that a type can set configs from a SocketIOClientConfiguration.

    See more

    Declaration

    Swift

    public protocol ConfigSettable
  • Defines the interface for a SocketIOClient.

    See more

    Declaration

    Swift

    public protocol SocketIOClientSpec : AnyObject
  • Declares that a type will be a delegate to an engine.

    See more

    Declaration

    Swift

    @objc
    public protocol SocketEngineClient
  • Protocol that is used to implement socket.io polling support

    See more

    Declaration

    Swift

    public protocol SocketEnginePollable : SocketEngineSpec
  • Specifies a SocketEngine.

    See more

    Declaration

    Swift

    public protocol SocketEngineSpec : AnyObject
  • Protocol that is used to implement socket.io WebSocket support

    See more

    Declaration

    Swift

    public protocol SocketEngineWebsocket : SocketEngineSpec
  • A manager for a socket.io connection.

    A SocketManagerSpec is responsible for multiplexing multiple namespaces through a single SocketEngineSpec.

    Example with SocketManager:

    let manager = SocketManager(socketURL: URL(string:"http://localhost:8080/")!)
    let defaultNamespaceSocket = manager.defaultSocket
    let swiftSocket = manager.socket(forNamespace: "/swift")
    
    // defaultNamespaceSocket and swiftSocket both share a single connection to the server
    

    Sockets created through the manager are retained by the manager. So at the very least, a single strong reference to the manager must be maintained to keep sockets alive.

    To disconnect a socket and remove it from the manager, either call SocketIOClient.disconnect() on the socket, or call one of the disconnectSocket methods on this class.

    See more

    Declaration

    Swift

    public protocol SocketManagerSpec : AnyObject, SocketEngineClient
  • Defines that a type will be able to parse socket.io-protocol messages.

    See more

    Declaration

    Swift

    public protocol SocketParsable : AnyObject
  • Says that a type will be able to buffer binary data before all data for an event has come in.

    See more

    Declaration

    Swift

    public protocol SocketDataBufferable : AnyObject
  • Represents a class will log client events.

    See more

    Declaration

    Swift

    public protocol SocketLogger : AnyObject
  • A marking protocol that says a type can be represented in a socket.io packet.

    Example:

    struct CustomData : SocketData {
       let name: String
       let age: Int
    
       func socketRepresentation() -> SocketData {
           return ["name": name, "age": age]
       }
    }
    
    socket.emit("myEvent", CustomData(name: "Erik", age: 24))
    
    See more

    Declaration

    Swift

    public protocol SocketData
================================================ FILE: docs/Structs/SocketEventHandler.html ================================================ SocketEventHandler Structure Reference

SocketIO 16.0.0 Docs (100% documented)

SocketEventHandler

public struct SocketEventHandler

A wrapper around a handler.

Properties

  • The event for this handler.

    Declaration

    Swift

    public let event: String
  • id

    A unique identifier for this handler.

    Declaration

    Swift

    public let id: UUID
  • The actual handler function.

    Declaration

    Swift

    public let callback: NormalCallback

Methods

  • Causes this handler to be executed.

    Declaration

    Swift

    public func executeCallback(with items: [Any], withAck ack: Int, withSocket socket: SocketIOClient)

    Parameters

    with

    The data that this handler should be called with.

    withAck

    The ack number that this event expects. Pass -1 to say this event doesn’t expect an ack.

    withSocket

    The socket that is calling this event.

================================================ FILE: docs/Structs/SocketIOClientConfiguration.html ================================================ SocketIOClientConfiguration Structure Reference

SocketIO 16.0.0 Docs (100% documented)

SocketIOClientConfiguration

public struct SocketIOClientConfiguration : ExpressibleByArrayLiteral, Collection, MutableCollection

An array-like type that holds SocketIOClientOptions

Typealiases

Properties

  • The start index of this collection.

    Declaration

    Swift

    public var startIndex: Index { get }
  • The end index of this collection.

    Declaration

    Swift

    public var endIndex: Index { get }
  • Whether this collection is empty.

    Declaration

    Swift

    public var isEmpty: Bool { get }
  • The number of elements stored in this collection.

    Declaration

    Swift

    public var count: Index.Stride { get }
  • The first element in this collection.

    Declaration

    Swift

    public var first: Element? { get }
  • Declaration

    Swift

    public subscript(position: Index) -> Element { get set }
  • Declaration

    Swift

    public subscript(bounds: Range<Index>) -> SubSequence { get set }

Initializers

  • Creates a new SocketIOClientConfiguration from an array literal.

    Declaration

    Swift

    public init(arrayLiteral elements: Element...)

    Parameters

    arrayLiteral

    The elements.

Methods

  • Creates an iterator for this collection.

    Declaration

    Swift

    public func makeIterator() -> Iterator

    Return Value

    An iterator over this collection.

  • Declaration

    Swift

    public func index(after i: Index) -> Index

    Return Value

    The index after index.

  • Special method that inserts element into the collection, replacing any other instances of element.

    Declaration

    Swift

    public mutating func insert(_ element: Element, replacing replace: Bool = true)

    Parameters

    element

    The element to insert.

    replacing

    Whether to replace any occurrences of element to the new item. Default is true.

================================================ FILE: docs/Structs/SocketPacket/PacketType.html ================================================ PacketType Enumeration Reference

SocketIO 16.0.0 Docs (100% documented)

PacketType

enum PacketType : Int

The type of packets.

Cases

  • Connect: 0

    Declaration

    Swift

    case connect
  • Disconnect: 1

    Declaration

    Swift

    case disconnect
  • Event: 2

    Declaration

    Swift

    case event
  • ack

    Ack: 3

    Declaration

    Swift

    case ack
  • Error: 4

    Declaration

    Swift

    case error
  • Binary Event: 5

    Declaration

    Swift

    case binaryEvent
  • Binary Ack: 6

    Declaration

    Swift

    case binaryAck

Properties

  • Whether or not this type is binary

    Declaration

    Swift

    public var isBinary: Bool { get }
================================================ FILE: docs/Structs/SocketPacket.html ================================================ SocketPacket Structure Reference

SocketIO 16.0.0 Docs (100% documented)

SocketPacket

public struct SocketPacket : CustomStringConvertible

A struct that represents a socket.io packet.

Properties

  • nsp

    The namespace for this packet.

    Declaration

    Swift

    public let nsp: String
  • id

    If > 0 then this packet is using acking.

    Declaration

    Swift

    public let id: Int
  • The type of this packet.

    Declaration

    Swift

    public let type: PacketType
  • An array of binary data for this packet.

    Declaration

    Swift

    public internal(set) var binary: [Data] { get }
  • The data for this event.

    Note: This includes all data inside of the socket.io packet payload array, which includes the event name for event type packets.

    Declaration

    Swift

    public internal(set) var data: [Any] { get }
  • Returns the payload for this packet, minus the event name if this is an event or binaryEvent type packet.

    Declaration

    Swift

    public var args: [Any] { get }
  • A string representation of this packet.

    Declaration

    Swift

    public var description: String { get }
  • The event name for this packet.

    Declaration

    Swift

    public var event: String { get }
  • A string representation of this packet.

    Declaration

    Swift

    public var packetString: String { get }

PacketType enum

================================================ FILE: docs/Structs.html ================================================ Structures Reference

SocketIO 16.0.0 Docs (100% documented)

================================================ FILE: docs/Typealiases.html ================================================ Type Aliases Reference

SocketIO 16.0.0 Docs (100% documented)

================================================ FILE: docs/css/highlight.css ================================================ /* Credit to https://gist.github.com/wataru420/2048287 */ .highlight { /* Comment */ /* Error */ /* Keyword */ /* Operator */ /* Comment.Multiline */ /* Comment.Preproc */ /* Comment.Single */ /* Comment.Special */ /* Generic.Deleted */ /* Generic.Deleted.Specific */ /* Generic.Emph */ /* Generic.Error */ /* Generic.Heading */ /* Generic.Inserted */ /* Generic.Inserted.Specific */ /* Generic.Output */ /* Generic.Prompt */ /* Generic.Strong */ /* Generic.Subheading */ /* Generic.Traceback */ /* Keyword.Constant */ /* Keyword.Declaration */ /* Keyword.Pseudo */ /* Keyword.Reserved */ /* Keyword.Type */ /* Literal.Number */ /* Literal.String */ /* Name.Attribute */ /* Name.Builtin */ /* Name.Class */ /* Name.Constant */ /* Name.Entity */ /* Name.Exception */ /* Name.Function */ /* Name.Namespace */ /* Name.Tag */ /* Name.Variable */ /* Operator.Word */ /* Text.Whitespace */ /* Literal.Number.Float */ /* Literal.Number.Hex */ /* Literal.Number.Integer */ /* Literal.Number.Oct */ /* Literal.String.Backtick */ /* Literal.String.Char */ /* Literal.String.Doc */ /* Literal.String.Double */ /* Literal.String.Escape */ /* Literal.String.Heredoc */ /* Literal.String.Interpol */ /* Literal.String.Other */ /* Literal.String.Regex */ /* Literal.String.Single */ /* Literal.String.Symbol */ /* Name.Builtin.Pseudo */ /* Name.Variable.Class */ /* Name.Variable.Global */ /* Name.Variable.Instance */ /* Literal.Number.Integer.Long */ } .highlight .c { color: #999988; font-style: italic; } .highlight .err { color: #a61717; background-color: #e3d2d2; } .highlight .k { color: #000000; font-weight: bold; } .highlight .o { color: #000000; font-weight: bold; } .highlight .cm { color: #999988; font-style: italic; } .highlight .cp { color: #999999; font-weight: bold; } .highlight .c1 { color: #999988; font-style: italic; } .highlight .cs { color: #999999; font-weight: bold; font-style: italic; } .highlight .gd { color: #000000; background-color: #ffdddd; } .highlight .gd .x { color: #000000; background-color: #ffaaaa; } .highlight .ge { color: #000000; font-style: italic; } .highlight .gr { color: #aa0000; } .highlight .gh { color: #999999; } .highlight .gi { color: #000000; background-color: #ddffdd; } .highlight .gi .x { color: #000000; background-color: #aaffaa; } .highlight .go { color: #888888; } .highlight .gp { color: #555555; } .highlight .gs { font-weight: bold; } .highlight .gu { color: #aaaaaa; } .highlight .gt { color: #aa0000; } .highlight .kc { color: #000000; font-weight: bold; } .highlight .kd { color: #000000; font-weight: bold; } .highlight .kp { color: #000000; font-weight: bold; } .highlight .kr { color: #000000; font-weight: bold; } .highlight .kt { color: #445588; } .highlight .m { color: #009999; } .highlight .s { color: #d14; } .highlight .na { color: #008080; } .highlight .nb { color: #0086B3; } .highlight .nc { color: #445588; font-weight: bold; } .highlight .no { color: #008080; } .highlight .ni { color: #800080; } .highlight .ne { color: #990000; font-weight: bold; } .highlight .nf { color: #990000; } .highlight .nn { color: #555555; } .highlight .nt { color: #000080; } .highlight .nv { color: #008080; } .highlight .ow { color: #000000; font-weight: bold; } .highlight .w { color: #bbbbbb; } .highlight .mf { color: #009999; } .highlight .mh { color: #009999; } .highlight .mi { color: #009999; } .highlight .mo { color: #009999; } .highlight .sb { color: #d14; } .highlight .sc { color: #d14; } .highlight .sd { color: #d14; } .highlight .s2 { color: #d14; } .highlight .se { color: #d14; } .highlight .sh { color: #d14; } .highlight .si { color: #d14; } .highlight .sx { color: #d14; } .highlight .sr { color: #009926; } .highlight .s1 { color: #d14; } .highlight .ss { color: #990073; } .highlight .bp { color: #999999; } .highlight .vc { color: #008080; } .highlight .vg { color: #008080; } .highlight .vi { color: #008080; } .highlight .il { color: #009999; } ================================================ FILE: docs/css/jazzy.css ================================================ *, *:before, *:after { box-sizing: inherit; } body { margin: 0; background: #fff; color: #333; font: 16px/1.7 "Helvetica Neue", Helvetica, Arial, sans-serif; letter-spacing: .2px; -webkit-font-smoothing: antialiased; box-sizing: border-box; } h1 { font-size: 2rem; font-weight: 700; margin: 1.275em 0 0.6em; } h2 { font-size: 1.75rem; font-weight: 700; margin: 1.275em 0 0.3em; } h3 { font-size: 1.5rem; font-weight: 700; margin: 1em 0 0.3em; } h4 { font-size: 1.25rem; font-weight: 700; margin: 1.275em 0 0.85em; } h5 { font-size: 1rem; font-weight: 700; margin: 1.275em 0 0.85em; } h6 { font-size: 1rem; font-weight: 700; margin: 1.275em 0 0.85em; color: #777; } p { margin: 0 0 1em; } ul, ol { padding: 0 0 0 2em; margin: 0 0 0.85em; } blockquote { margin: 0 0 0.85em; padding: 0 15px; color: #858585; border-left: 4px solid #e5e5e5; } img { max-width: 100%; } a { color: #4183c4; text-decoration: none; } a:hover, a:focus { outline: 0; text-decoration: underline; } a.discouraged { text-decoration: line-through; } a.discouraged:hover, a.discouraged:focus { text-decoration: underline line-through; } table { background: #fff; width: 100%; border-collapse: collapse; border-spacing: 0; overflow: auto; margin: 0 0 0.85em; } tr:nth-child(2n) { background-color: #fbfbfb; } th, td { padding: 6px 13px; border: 1px solid #ddd; } pre { margin: 0 0 1.275em; padding: .85em 1em; overflow: auto; background: #f7f7f7; font-size: .85em; font-family: Consolas, "Liberation Mono", Menlo, Courier, monospace; } code { font-family: Consolas, "Liberation Mono", Menlo, Courier, monospace; } .item-container p > code, .item-container li > code, .top-matter p > code, .top-matter li > code { background: #f7f7f7; padding: .2em; } .item-container p > code:before, .item-container p > code:after, .item-container li > code:before, .item-container li > code:after, .top-matter p > code:before, .top-matter p > code:after, .top-matter li > code:before, .top-matter li > code:after { letter-spacing: -.2em; content: "\00a0"; } pre code { padding: 0; white-space: pre; } .content-wrapper { display: flex; flex-direction: column; } @media (min-width: 768px) { .content-wrapper { flex-direction: row; } } .header { display: flex; padding: 8px; font-size: 0.875em; background: #444; color: #999; } .header-col { margin: 0; padding: 0 8px; } .header-col--primary { flex: 1; } .header-link { color: #fff; } .header-icon { padding-right: 6px; vertical-align: -4px; height: 16px; } .breadcrumbs { font-size: 0.875em; padding: 8px 16px; margin: 0; background: #fbfbfb; border-bottom: 1px solid #ddd; } .carat { height: 10px; margin: 0 5px; } .navigation { order: 2; } @media (min-width: 768px) { .navigation { order: 1; width: 25%; max-width: 300px; padding-bottom: 64px; overflow: hidden; word-wrap: normal; background: #fbfbfb; border-right: 1px solid #ddd; } } .nav-groups { list-style-type: none; padding-left: 0; } .nav-group-name { border-bottom: 1px solid #ddd; padding: 8px 0 8px 16px; } .nav-group-name-link { color: #333; } .nav-group-tasks { margin: 8px 0; padding: 0 0 0 8px; } .nav-group-task { font-size: 1em; list-style-type: none; white-space: nowrap; } .nav-group-task-link { color: #808080; } .main-content { order: 1; } @media (min-width: 768px) { .main-content { order: 2; flex: 1; padding-bottom: 60px; } } .section { padding: 0 32px; border-bottom: 1px solid #ddd; } .section-content { max-width: 834px; margin: 0 auto; padding: 16px 0; } .section-name { color: #666; display: block; } .section-name p { margin-bottom: inherit; } .declaration .highlight { overflow-x: initial; padding: 8px 0; margin: 0; background-color: transparent; border: none; } .task-group-section { border-top: 1px solid #ddd; } .task-group { padding-top: 0px; } .task-name-container a[name]:before { content: ""; display: block; } .section-name-container { position: relative; } .section-name-container .section-name-link { position: absolute; top: 0; left: 0; bottom: 0; right: 0; margin-bottom: 0; } .section-name-container .section-name { position: relative; pointer-events: none; z-index: 1; } .section-name-container .section-name a { pointer-events: auto; } .item-container { padding: 0; } .item { padding-top: 8px; width: 100%; list-style-type: none; } .item a[name]:before { content: ""; display: block; } .item .token, .item .direct-link { display: inline-block; text-indent: -20px; padding-left: 3px; margin-left: 20px; font-size: 1rem; } .item .declaration-note { font-size: .85em; color: #808080; font-style: italic; } .pointer-container { border-bottom: 1px solid #ddd; left: -23px; padding-bottom: 13px; position: relative; width: 110%; } .pointer { left: 21px; top: 7px; display: block; position: absolute; width: 12px; height: 12px; border-left: 1px solid #ddd; border-top: 1px solid #ddd; background: #fff; transform: rotate(45deg); } .height-container { display: none; position: relative; width: 100%; overflow: hidden; } .height-container .section { background: #fff; border: 1px solid #ddd; border-top-width: 0; padding-top: 10px; padding-bottom: 5px; padding: 8px 16px; } .aside, .language { padding: 6px 12px; margin: 12px 0; border-left: 5px solid #dddddd; overflow-y: hidden; } .aside .aside-title, .language .aside-title { font-size: 9px; letter-spacing: 2px; text-transform: uppercase; padding-bottom: 0; margin: 0; color: #aaa; -webkit-user-select: none; } .aside p:last-child, .language p:last-child { margin-bottom: 0; } .language { border-left: 5px solid #cde9f4; } .language .aside-title { color: #4183c4; } .aside-warning, .aside-deprecated, .aside-unavailable { border-left: 5px solid #ff6666; } .aside-warning .aside-title, .aside-deprecated .aside-title, .aside-unavailable .aside-title { color: #ff0000; } .graybox { border-collapse: collapse; width: 100%; } .graybox p { margin: 0; word-break: break-word; min-width: 50px; } .graybox td { border: 1px solid #ddd; padding: 5px 25px 5px 10px; vertical-align: middle; } .graybox tr td:first-of-type { text-align: right; padding: 7px; vertical-align: top; word-break: normal; width: 40px; } .slightly-smaller { font-size: 0.9em; } .footer { padding: 8px 16px; background: #444; color: #ddd; font-size: 0.8em; } .footer p { margin: 8px 0; } .footer a { color: #fff; } html.dash .header, html.dash .breadcrumbs, html.dash .navigation { display: none; } html.dash .height-container { display: block; } form[role=search] input { font: 16px/1.7 "Helvetica Neue", Helvetica, Arial, sans-serif; font-size: 14px; line-height: 24px; padding: 0 10px; margin: 0; border: none; border-radius: 1em; } .loading form[role=search] input { background: white url(../img/spinner.gif) center right 4px no-repeat; } form[role=search] .tt-menu { margin: 0; min-width: 300px; background: #fbfbfb; color: #333; border: 1px solid #ddd; } form[role=search] .tt-highlight { font-weight: bold; } form[role=search] .tt-suggestion { font: 16px/1.7 "Helvetica Neue", Helvetica, Arial, sans-serif; padding: 0 8px; } form[role=search] .tt-suggestion span { display: table-cell; white-space: nowrap; } form[role=search] .tt-suggestion .doc-parent-name { width: 100%; text-align: right; font-weight: normal; font-size: 0.9em; padding-left: 16px; } form[role=search] .tt-suggestion:hover, form[role=search] .tt-suggestion.tt-cursor { cursor: pointer; background-color: #4183c4; color: #fff; } form[role=search] .tt-suggestion:hover .doc-parent-name, form[role=search] .tt-suggestion.tt-cursor .doc-parent-name { color: #fff; } ================================================ FILE: docs/faq.html ================================================ FAQ Reference

SocketIO 16.0.0 Docs (100% documented)

How do I connect to my WebSocket server?

This library is NOT a WebSockets library. This library is only for servers that implement the socket.io protocol, such as socket.io. If you need a plain WebSockets client check out Starscream for Swift and JetFire for Objective-C.

Why isn’t my event handler being called?

One of the most common reasons your event might not be called is if the client is released by ARC.

Take this code for example:

class Manager {
    func addHandlers() {
        let manager = SocketManager(socketURL: URL(string: "http://somesocketioserver.com")!)

        manager.defaultSocket.on("myEvent") {data, ack in
            print(data)
        }
    }

}

This code is incorrect, and the event handler will never be called. Because as soon as this method is called manager will be released, along with the socket, and its memory reclaimed.

A correct way would be:

class Manager {
    let manager = SocketManager(socketURL: URL(string: "http://somesocketioserver.com")!)

    func addHandlers() {
        manager.defaultSocket.on("myEvent") {data, ack in
            print(data)
        }
    }
}

================================================ FILE: docs/index.html ================================================ SocketIO Reference

SocketIO 16.0.0 Docs (100% documented)

Build Status

Socket.IO-Client-Swift

Socket.IO-client for iOS/OS X.

Example

import SocketIO

let manager = SocketManager(socketURL: URL(string: "http://localhost:8080")!, config: [.log(true), .compress])
let socket = manager.defaultSocket

socket.on(clientEvent: .connect) {data, ack in
    print("socket connected")
}

socket.on("currentAmount") {data, ack in
    guard let cur = data[0] as? Double else { return }

    socket.emitWithAck("canUpdate", cur).timingOut(after: 0) {data in
        if data.first as? String ?? "passed" == SocketAckValue.noAck {
            // Handle ack timeout 
        }

        socket.emit("update", ["amount": cur + 2.50])
    }

    ack.with("Got your currentAmount", "dude")
}

socket.connect()

Features

  • Supports socket.io 2.0+/3.0+.
  • Supports Binary
  • Supports Polling and WebSockets
  • Supports TLS/SSL

FAQS

Checkout the FAQs for commonly asked questions.

Checkout the 12to13 guide for migrating to v13+ from v12 below.

Checkout the 15to16 guide for migrating to v16+ from v15.

Installation

Requires Swift 4/5 and Xcode 10.x

Swift Package Manager

Add the project as a dependency to your Package.swift:

// swift-tools-version:4.2

import PackageDescription

let package = Package(
    name: "socket.io-test",
    products: [
        .executable(name: "socket.io-test", targets: ["YourTargetName"])
    ],
    dependencies: [
        .package(url: "https://github.com/socketio/socket.io-client-swift", .upToNextMinor(from: "15.0.0"))
    ],
    targets: [
        .target(name: "YourTargetName", dependencies: ["SocketIO"], path: "./Path/To/Your/Sources")
    ]
)

Then import import SocketIO.

Carthage

Add this line to your Cartfile:

github "socketio/socket.io-client-swift" ~> 15.2.0

Run carthage update --platform ios,macosx.

Add the Starscream and SocketIO frameworks to your projects and follow the usual Carthage process.

CocoaPods 1.0.0 or later

Create Podfile and add pod 'Socket.IO-Client-Swift':

use_frameworks!

target 'YourApp' do
    pod 'Socket.IO-Client-Swift', '~> 15.2.0'
end

Install pods:

$ pod install

Import the module:

Swift:

import SocketIO

Objective-C:

@import SocketIO;

Docs

Detailed Example

A more detailed example can be found here

An example using the Swift Package Manager can be found here

License

MIT

================================================ FILE: docs/js/jazzy.js ================================================ window.jazzy = {'docset': false} if (typeof window.dash != 'undefined') { document.documentElement.className += ' dash' window.jazzy.docset = true } if (navigator.userAgent.match(/xcode/i)) { document.documentElement.className += ' xcode' window.jazzy.docset = true } function toggleItem($link, $content) { var animationDuration = 300; $link.toggleClass('token-open'); $content.slideToggle(animationDuration); } function itemLinkToContent($link) { return $link.parent().parent().next(); } // On doc load + hash-change, open any targetted item function openCurrentItemIfClosed() { if (window.jazzy.docset) { return; } var $link = $(`a[name="${location.hash.substring(1)}"]`).nextAll('.token'); $content = itemLinkToContent($link); if ($content.is(':hidden')) { toggleItem($link, $content); } } $(openCurrentItemIfClosed); $(window).on('hashchange', openCurrentItemIfClosed); // On item link ('token') click, toggle its discussion $('.token').on('click', function(event) { if (window.jazzy.docset) { return; } var $link = $(this); toggleItem($link, itemLinkToContent($link)); // Keeps the document from jumping to the hash. var href = $link.attr('href'); if (history.pushState) { history.pushState({}, '', href); } else { location.hash = href; } event.preventDefault(); }); // Clicks on links to the current, closed, item need to open the item $("a:not('.token')").on('click', function() { if (location == this.href) { openCurrentItemIfClosed(); } }); // KaTeX rendering if ("katex" in window) { $($('.math').each( (_, element) => { katex.render(element.textContent, element, { displayMode: $(element).hasClass('m-block'), throwOnError: false, trust: true }); })) } ================================================ FILE: docs/js/jazzy.search.js ================================================ $(function(){ var $typeahead = $('[data-typeahead]'); var $form = $typeahead.parents('form'); var searchURL = $form.attr('action'); function displayTemplate(result) { return result.name; } function suggestionTemplate(result) { var t = '
'; t += '' + result.name + ''; if (result.parent_name) { t += '' + result.parent_name + ''; } t += '
'; return t; } $typeahead.one('focus', function() { $form.addClass('loading'); $.getJSON(searchURL).then(function(searchData) { const searchIndex = lunr(function() { this.ref('url'); this.field('name'); this.field('abstract'); for (const [url, doc] of Object.entries(searchData)) { this.add({url: url, name: doc.name, abstract: doc.abstract}); } }); $typeahead.typeahead( { highlight: true, minLength: 3, autoselect: true }, { limit: 10, display: displayTemplate, templates: { suggestion: suggestionTemplate }, source: function(query, sync) { const lcSearch = query.toLowerCase(); const results = searchIndex.query(function(q) { q.term(lcSearch, { boost: 100 }); q.term(lcSearch, { boost: 10, wildcard: lunr.Query.wildcard.TRAILING }); }).map(function(result) { var doc = searchData[result.ref]; doc.url = result.ref; return doc; }); sync(results); } } ); $form.removeClass('loading'); $typeahead.trigger('focus'); }); }); var baseURL = searchURL.slice(0, -"search.json".length); $typeahead.on('typeahead:select', function(e, result) { window.location = baseURL + result.url; }); }); ================================================ FILE: docs/js/typeahead.jquery.js ================================================ /*! * typeahead.js 1.3.1 * https://github.com/corejavascript/typeahead.js * Copyright 2013-2020 Twitter, Inc. and other contributors; Licensed MIT */ (function(root, factory) { if (typeof define === "function" && define.amd) { define([ "jquery" ], function(a0) { return factory(a0); }); } else if (typeof module === "object" && module.exports) { module.exports = factory(require("jquery")); } else { factory(root["jQuery"]); } })(this, function($) { var _ = function() { "use strict"; return { isMsie: function() { return /(msie|trident)/i.test(navigator.userAgent) ? navigator.userAgent.match(/(msie |rv:)(\d+(.\d+)?)/i)[2] : false; }, isBlankString: function(str) { return !str || /^\s*$/.test(str); }, escapeRegExChars: function(str) { return str.replace(/[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g, "\\$&"); }, isString: function(obj) { return typeof obj === "string"; }, isNumber: function(obj) { return typeof obj === "number"; }, isArray: $.isArray, isFunction: $.isFunction, isObject: $.isPlainObject, isUndefined: function(obj) { return typeof obj === "undefined"; }, isElement: function(obj) { return !!(obj && obj.nodeType === 1); }, isJQuery: function(obj) { return obj instanceof $; }, toStr: function toStr(s) { return _.isUndefined(s) || s === null ? "" : s + ""; }, bind: $.proxy, each: function(collection, cb) { $.each(collection, reverseArgs); function reverseArgs(index, value) { return cb(value, index); } }, map: $.map, filter: $.grep, every: function(obj, test) { var result = true; if (!obj) { return result; } $.each(obj, function(key, val) { if (!(result = test.call(null, val, key, obj))) { return false; } }); return !!result; }, some: function(obj, test) { var result = false; if (!obj) { return result; } $.each(obj, function(key, val) { if (result = test.call(null, val, key, obj)) { return false; } }); return !!result; }, mixin: $.extend, identity: function(x) { return x; }, clone: function(obj) { return $.extend(true, {}, obj); }, getIdGenerator: function() { var counter = 0; return function() { return counter++; }; }, templatify: function templatify(obj) { return $.isFunction(obj) ? obj : template; function template() { return String(obj); } }, defer: function(fn) { setTimeout(fn, 0); }, debounce: function(func, wait, immediate) { var timeout, result; return function() { var context = this, args = arguments, later, callNow; later = function() { timeout = null; if (!immediate) { result = func.apply(context, args); } }; callNow = immediate && !timeout; clearTimeout(timeout); timeout = setTimeout(later, wait); if (callNow) { result = func.apply(context, args); } return result; }; }, throttle: function(func, wait) { var context, args, timeout, result, previous, later; previous = 0; later = function() { previous = new Date(); timeout = null; result = func.apply(context, args); }; return function() { var now = new Date(), remaining = wait - (now - previous); context = this; args = arguments; if (remaining <= 0) { clearTimeout(timeout); timeout = null; previous = now; result = func.apply(context, args); } else if (!timeout) { timeout = setTimeout(later, remaining); } return result; }; }, stringify: function(val) { return _.isString(val) ? val : JSON.stringify(val); }, guid: function() { function _p8(s) { var p = (Math.random().toString(16) + "000000000").substr(2, 8); return s ? "-" + p.substr(0, 4) + "-" + p.substr(4, 4) : p; } return "tt-" + _p8() + _p8(true) + _p8(true) + _p8(); }, noop: function() {} }; }(); var WWW = function() { "use strict"; var defaultClassNames = { wrapper: "twitter-typeahead", input: "tt-input", hint: "tt-hint", menu: "tt-menu", dataset: "tt-dataset", suggestion: "tt-suggestion", selectable: "tt-selectable", empty: "tt-empty", open: "tt-open", cursor: "tt-cursor", highlight: "tt-highlight" }; return build; function build(o) { var www, classes; classes = _.mixin({}, defaultClassNames, o); www = { css: buildCss(), classes: classes, html: buildHtml(classes), selectors: buildSelectors(classes) }; return { css: www.css, html: www.html, classes: www.classes, selectors: www.selectors, mixin: function(o) { _.mixin(o, www); } }; } function buildHtml(c) { return { wrapper: '', menu: '
' }; } function buildSelectors(classes) { var selectors = {}; _.each(classes, function(v, k) { selectors[k] = "." + v; }); return selectors; } function buildCss() { var css = { wrapper: { position: "relative", display: "inline-block" }, hint: { position: "absolute", top: "0", left: "0", borderColor: "transparent", boxShadow: "none", opacity: "1" }, input: { position: "relative", verticalAlign: "top", backgroundColor: "transparent" }, inputWithNoHint: { position: "relative", verticalAlign: "top" }, menu: { position: "absolute", top: "100%", left: "0", zIndex: "100", display: "none" }, ltr: { left: "0", right: "auto" }, rtl: { left: "auto", right: " 0" } }; if (_.isMsie()) { _.mixin(css.input, { backgroundImage: "url(data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7)" }); } return css; } }(); var EventBus = function() { "use strict"; var namespace, deprecationMap; namespace = "typeahead:"; deprecationMap = { render: "rendered", cursorchange: "cursorchanged", select: "selected", autocomplete: "autocompleted" }; function EventBus(o) { if (!o || !o.el) { $.error("EventBus initialized without el"); } this.$el = $(o.el); } _.mixin(EventBus.prototype, { _trigger: function(type, args) { var $e = $.Event(namespace + type); this.$el.trigger.call(this.$el, $e, args || []); return $e; }, before: function(type) { var args, $e; args = [].slice.call(arguments, 1); $e = this._trigger("before" + type, args); return $e.isDefaultPrevented(); }, trigger: function(type) { var deprecatedType; this._trigger(type, [].slice.call(arguments, 1)); if (deprecatedType = deprecationMap[type]) { this._trigger(deprecatedType, [].slice.call(arguments, 1)); } } }); return EventBus; }(); var EventEmitter = function() { "use strict"; var splitter = /\s+/, nextTick = getNextTick(); return { onSync: onSync, onAsync: onAsync, off: off, trigger: trigger }; function on(method, types, cb, context) { var type; if (!cb) { return this; } types = types.split(splitter); cb = context ? bindContext(cb, context) : cb; this._callbacks = this._callbacks || {}; while (type = types.shift()) { this._callbacks[type] = this._callbacks[type] || { sync: [], async: [] }; this._callbacks[type][method].push(cb); } return this; } function onAsync(types, cb, context) { return on.call(this, "async", types, cb, context); } function onSync(types, cb, context) { return on.call(this, "sync", types, cb, context); } function off(types) { var type; if (!this._callbacks) { return this; } types = types.split(splitter); while (type = types.shift()) { delete this._callbacks[type]; } return this; } function trigger(types) { var type, callbacks, args, syncFlush, asyncFlush; if (!this._callbacks) { return this; } types = types.split(splitter); args = [].slice.call(arguments, 1); while ((type = types.shift()) && (callbacks = this._callbacks[type])) { syncFlush = getFlush(callbacks.sync, this, [ type ].concat(args)); asyncFlush = getFlush(callbacks.async, this, [ type ].concat(args)); syncFlush() && nextTick(asyncFlush); } return this; } function getFlush(callbacks, context, args) { return flush; function flush() { var cancelled; for (var i = 0, len = callbacks.length; !cancelled && i < len; i += 1) { cancelled = callbacks[i].apply(context, args) === false; } return !cancelled; } } function getNextTick() { var nextTickFn; if (window.setImmediate) { nextTickFn = function nextTickSetImmediate(fn) { setImmediate(function() { fn(); }); }; } else { nextTickFn = function nextTickSetTimeout(fn) { setTimeout(function() { fn(); }, 0); }; } return nextTickFn; } function bindContext(fn, context) { return fn.bind ? fn.bind(context) : function() { fn.apply(context, [].slice.call(arguments, 0)); }; } }(); var highlight = function(doc) { "use strict"; var defaults = { node: null, pattern: null, tagName: "strong", className: null, wordsOnly: false, caseSensitive: false, diacriticInsensitive: false }; var accented = { A: "[AaªÀ-Åà-åĀ-ąǍǎȀ-ȃȦȧᴬᵃḀḁẚẠ-ảₐ℀℁℻⒜Ⓐⓐ㍱-㍴㎀-㎄㎈㎉㎩-㎯㏂㏊㏟㏿Aa]", B: "[BbᴮᵇḂ-ḇℬ⒝Ⓑⓑ㍴㎅-㎇㏃㏈㏔㏝Bb]", C: "[CcÇçĆ-čᶜ℀ℂ℃℅℆ℭⅭⅽ⒞Ⓒⓒ㍶㎈㎉㎝㎠㎤㏄-㏇Cc]", D: "[DdĎďDŽ-džDZ-dzᴰᵈḊ-ḓⅅⅆⅮⅾ⒟Ⓓⓓ㋏㍲㍷-㍹㎗㎭-㎯㏅㏈Dd]", E: "[EeÈ-Ëè-ëĒ-ěȄ-ȇȨȩᴱᵉḘ-ḛẸ-ẽₑ℡ℯℰⅇ⒠Ⓔⓔ㉐㋍㋎Ee]", F: "[FfᶠḞḟ℉ℱ℻⒡Ⓕⓕ㎊-㎌㎙ff-fflFf]", G: "[GgĜ-ģǦǧǴǵᴳᵍḠḡℊ⒢Ⓖⓖ㋌㋍㎇㎍-㎏㎓㎬㏆㏉㏒㏿Gg]", H: "[HhĤĥȞȟʰᴴḢ-ḫẖℋ-ℎ⒣Ⓗⓗ㋌㍱㎐-㎔㏊㏋㏗Hh]", I: "[IiÌ-Ïì-ïĨ-İIJijǏǐȈ-ȋᴵᵢḬḭỈ-ịⁱℐℑℹⅈⅠ-ⅣⅥ-ⅨⅪⅫⅰ-ⅳⅵ-ⅸⅺⅻ⒤Ⓘⓘ㍺㏌㏕fiffiIi]", J: "[JjIJ-ĵLJ-njǰʲᴶⅉ⒥ⒿⓙⱼJj]", K: "[KkĶķǨǩᴷᵏḰ-ḵK⒦Ⓚⓚ㎄㎅㎉㎏㎑㎘㎞㎢㎦㎪㎸㎾㏀㏆㏍-㏏Kk]", L: "[LlĹ-ŀLJ-ljˡᴸḶḷḺ-ḽℒℓ℡Ⅼⅼ⒧Ⓛⓛ㋏㎈㎉㏐-㏓㏕㏖㏿flfflLl]", M: "[MmᴹᵐḾ-ṃ℠™ℳⅯⅿ⒨Ⓜⓜ㍷-㍹㎃㎆㎎㎒㎖㎙-㎨㎫㎳㎷㎹㎽㎿㏁㏂㏎㏐㏔-㏖㏘㏙㏞㏟Mm]", N: "[NnÑñŃ-ʼnNJ-njǸǹᴺṄ-ṋⁿℕ№⒩Ⓝⓝ㎁㎋㎚㎱㎵㎻㏌㏑Nn]", O: "[OoºÒ-Öò-öŌ-őƠơǑǒǪǫȌ-ȏȮȯᴼᵒỌ-ỏₒ℅№ℴ⒪Ⓞⓞ㍵㏇㏒㏖Oo]", P: "[PpᴾᵖṔ-ṗℙ⒫Ⓟⓟ㉐㍱㍶㎀㎊㎩-㎬㎰㎴㎺㏋㏗-㏚Pp]", Q: "[Qqℚ⒬Ⓠⓠ㏃Qq]", R: "[RrŔ-řȐ-ȓʳᴿᵣṘ-ṛṞṟ₨ℛ-ℝ⒭Ⓡⓡ㋍㍴㎭-㎯㏚㏛Rr]", S: "[SsŚ-šſȘșˢṠ-ṣ₨℁℠⒮Ⓢⓢ㎧㎨㎮-㎳㏛㏜stSs]", T: "[TtŢ-ťȚțᵀᵗṪ-ṱẗ℡™⒯Ⓣⓣ㉐㋏㎔㏏ſtstTt]", U: "[UuÙ-Üù-üŨ-ųƯưǓǔȔ-ȗᵁᵘᵤṲ-ṷỤ-ủ℆⒰Ⓤⓤ㍳㍺Uu]", V: "[VvᵛᵥṼ-ṿⅣ-Ⅷⅳ-ⅷ⒱Ⓥⓥⱽ㋎㍵㎴-㎹㏜㏞Vv]", W: "[WwŴŵʷᵂẀ-ẉẘ⒲Ⓦⓦ㎺-㎿㏝Ww]", X: "[XxˣẊ-ẍₓ℻Ⅸ-Ⅻⅸ-ⅻ⒳Ⓧⓧ㏓Xx]", Y: "[YyÝýÿŶ-ŸȲȳʸẎẏẙỲ-ỹ⒴Ⓨⓨ㏉Yy]", Z: "[ZzŹ-žDZ-dzᶻẐ-ẕℤℨ⒵Ⓩⓩ㎐-㎔Zz]" }; return function hightlight(o) { var regex; o = _.mixin({}, defaults, o); if (!o.node || !o.pattern) { return; } o.pattern = _.isArray(o.pattern) ? o.pattern : [ o.pattern ]; regex = getRegex(o.pattern, o.caseSensitive, o.wordsOnly, o.diacriticInsensitive); traverse(o.node, hightlightTextNode); function hightlightTextNode(textNode) { var match, patternNode, wrapperNode; if (match = regex.exec(textNode.data)) { wrapperNode = doc.createElement(o.tagName); o.className && (wrapperNode.className = o.className); patternNode = textNode.splitText(match.index); patternNode.splitText(match[0].length); wrapperNode.appendChild(patternNode.cloneNode(true)); textNode.parentNode.replaceChild(wrapperNode, patternNode); } return !!match; } function traverse(el, hightlightTextNode) { var childNode, TEXT_NODE_TYPE = 3; for (var i = 0; i < el.childNodes.length; i++) { childNode = el.childNodes[i]; if (childNode.nodeType === TEXT_NODE_TYPE) { i += hightlightTextNode(childNode) ? 1 : 0; } else { traverse(childNode, hightlightTextNode); } } } }; function accent_replacer(chr) { return accented[chr.toUpperCase()] || chr; } function getRegex(patterns, caseSensitive, wordsOnly, diacriticInsensitive) { var escapedPatterns = [], regexStr; for (var i = 0, len = patterns.length; i < len; i++) { var escapedWord = _.escapeRegExChars(patterns[i]); if (diacriticInsensitive) { escapedWord = escapedWord.replace(/\S/g, accent_replacer); } escapedPatterns.push(escapedWord); } regexStr = wordsOnly ? "\\b(" + escapedPatterns.join("|") + ")\\b" : "(" + escapedPatterns.join("|") + ")"; return caseSensitive ? new RegExp(regexStr) : new RegExp(regexStr, "i"); } }(window.document); var Input = function() { "use strict"; var specialKeyCodeMap; specialKeyCodeMap = { 9: "tab", 27: "esc", 37: "left", 39: "right", 13: "enter", 38: "up", 40: "down" }; function Input(o, www) { var id; o = o || {}; if (!o.input) { $.error("input is missing"); } www.mixin(this); this.$hint = $(o.hint); this.$input = $(o.input); this.$menu = $(o.menu); id = this.$input.attr("id") || _.guid(); this.$menu.attr("id", id + "_listbox"); this.$hint.attr({ "aria-hidden": true }); this.$input.attr({ "aria-owns": id + "_listbox", role: "combobox", "aria-autocomplete": "list", "aria-expanded": false }); this.query = this.$input.val(); this.queryWhenFocused = this.hasFocus() ? this.query : null; this.$overflowHelper = buildOverflowHelper(this.$input); this._checkLanguageDirection(); if (this.$hint.length === 0) { this.setHint = this.getHint = this.clearHint = this.clearHintIfInvalid = _.noop; } this.onSync("cursorchange", this._updateDescendent); } Input.normalizeQuery = function(str) { return _.toStr(str).replace(/^\s*/g, "").replace(/\s{2,}/g, " "); }; _.mixin(Input.prototype, EventEmitter, { _onBlur: function onBlur() { this.resetInputValue(); this.trigger("blurred"); }, _onFocus: function onFocus() { this.queryWhenFocused = this.query; this.trigger("focused"); }, _onKeydown: function onKeydown($e) { var keyName = specialKeyCodeMap[$e.which || $e.keyCode]; this._managePreventDefault(keyName, $e); if (keyName && this._shouldTrigger(keyName, $e)) { this.trigger(keyName + "Keyed", $e); } }, _onInput: function onInput() { this._setQuery(this.getInputValue()); this.clearHintIfInvalid(); this._checkLanguageDirection(); }, _managePreventDefault: function managePreventDefault(keyName, $e) { var preventDefault; switch (keyName) { case "up": case "down": preventDefault = !withModifier($e); break; default: preventDefault = false; } preventDefault && $e.preventDefault(); }, _shouldTrigger: function shouldTrigger(keyName, $e) { var trigger; switch (keyName) { case "tab": trigger = !withModifier($e); break; default: trigger = true; } return trigger; }, _checkLanguageDirection: function checkLanguageDirection() { var dir = (this.$input.css("direction") || "ltr").toLowerCase(); if (this.dir !== dir) { this.dir = dir; this.$hint.attr("dir", dir); this.trigger("langDirChanged", dir); } }, _setQuery: function setQuery(val, silent) { var areEquivalent, hasDifferentWhitespace; areEquivalent = areQueriesEquivalent(val, this.query); hasDifferentWhitespace = areEquivalent ? this.query.length !== val.length : false; this.query = val; if (!silent && !areEquivalent) { this.trigger("queryChanged", this.query); } else if (!silent && hasDifferentWhitespace) { this.trigger("whitespaceChanged", this.query); } }, _updateDescendent: function updateDescendent(event, id) { this.$input.attr("aria-activedescendant", id); }, bind: function() { var that = this, onBlur, onFocus, onKeydown, onInput; onBlur = _.bind(this._onBlur, this); onFocus = _.bind(this._onFocus, this); onKeydown = _.bind(this._onKeydown, this); onInput = _.bind(this._onInput, this); this.$input.on("blur.tt", onBlur).on("focus.tt", onFocus).on("keydown.tt", onKeydown); if (!_.isMsie() || _.isMsie() > 9) { this.$input.on("input.tt", onInput); } else { this.$input.on("keydown.tt keypress.tt cut.tt paste.tt", function($e) { if (specialKeyCodeMap[$e.which || $e.keyCode]) { return; } _.defer(_.bind(that._onInput, that, $e)); }); } return this; }, focus: function focus() { this.$input.focus(); }, blur: function blur() { this.$input.blur(); }, getLangDir: function getLangDir() { return this.dir; }, getQuery: function getQuery() { return this.query || ""; }, setQuery: function setQuery(val, silent) { this.setInputValue(val); this._setQuery(val, silent); }, hasQueryChangedSinceLastFocus: function hasQueryChangedSinceLastFocus() { return this.query !== this.queryWhenFocused; }, getInputValue: function getInputValue() { return this.$input.val(); }, setInputValue: function setInputValue(value) { this.$input.val(value); this.clearHintIfInvalid(); this._checkLanguageDirection(); }, resetInputValue: function resetInputValue() { this.setInputValue(this.query); }, getHint: function getHint() { return this.$hint.val(); }, setHint: function setHint(value) { this.$hint.val(value); }, clearHint: function clearHint() { this.setHint(""); }, clearHintIfInvalid: function clearHintIfInvalid() { var val, hint, valIsPrefixOfHint, isValid; val = this.getInputValue(); hint = this.getHint(); valIsPrefixOfHint = val !== hint && hint.indexOf(val) === 0; isValid = val !== "" && valIsPrefixOfHint && !this.hasOverflow(); !isValid && this.clearHint(); }, hasFocus: function hasFocus() { return this.$input.is(":focus"); }, hasOverflow: function hasOverflow() { var constraint = this.$input.width() - 2; this.$overflowHelper.text(this.getInputValue()); return this.$overflowHelper.width() >= constraint; }, isCursorAtEnd: function() { var valueLength, selectionStart, range; valueLength = this.$input.val().length; selectionStart = this.$input[0].selectionStart; if (_.isNumber(selectionStart)) { return selectionStart === valueLength; } else if (document.selection) { range = document.selection.createRange(); range.moveStart("character", -valueLength); return valueLength === range.text.length; } return true; }, destroy: function destroy() { this.$hint.off(".tt"); this.$input.off(".tt"); this.$overflowHelper.remove(); this.$hint = this.$input = this.$overflowHelper = $("
"); }, setAriaExpanded: function setAriaExpanded(value) { this.$input.attr("aria-expanded", value); } }); return Input; function buildOverflowHelper($input) { return $('').css({ position: "absolute", visibility: "hidden", whiteSpace: "pre", fontFamily: $input.css("font-family"), fontSize: $input.css("font-size"), fontStyle: $input.css("font-style"), fontVariant: $input.css("font-variant"), fontWeight: $input.css("font-weight"), wordSpacing: $input.css("word-spacing"), letterSpacing: $input.css("letter-spacing"), textIndent: $input.css("text-indent"), textRendering: $input.css("text-rendering"), textTransform: $input.css("text-transform") }).insertAfter($input); } function areQueriesEquivalent(a, b) { return Input.normalizeQuery(a) === Input.normalizeQuery(b); } function withModifier($e) { return $e.altKey || $e.ctrlKey || $e.metaKey || $e.shiftKey; } }(); var Dataset = function() { "use strict"; var keys, nameGenerator; keys = { dataset: "tt-selectable-dataset", val: "tt-selectable-display", obj: "tt-selectable-object" }; nameGenerator = _.getIdGenerator(); function Dataset(o, www) { o = o || {}; o.templates = o.templates || {}; o.templates.notFound = o.templates.notFound || o.templates.empty; if (!o.source) { $.error("missing source"); } if (!o.node) { $.error("missing node"); } if (o.name && !isValidName(o.name)) { $.error("invalid dataset name: " + o.name); } www.mixin(this); this.highlight = !!o.highlight; this.name = _.toStr(o.name || nameGenerator()); this.limit = o.limit || 5; this.displayFn = getDisplayFn(o.display || o.displayKey); this.templates = getTemplates(o.templates, this.displayFn); this.source = o.source.__ttAdapter ? o.source.__ttAdapter() : o.source; this.async = _.isUndefined(o.async) ? this.source.length > 2 : !!o.async; this._resetLastSuggestion(); this.$el = $(o.node).attr("role", "presentation").addClass(this.classes.dataset).addClass(this.classes.dataset + "-" + this.name); } Dataset.extractData = function extractData(el) { var $el = $(el); if ($el.data(keys.obj)) { return { dataset: $el.data(keys.dataset) || "", val: $el.data(keys.val) || "", obj: $el.data(keys.obj) || null }; } return null; }; _.mixin(Dataset.prototype, EventEmitter, { _overwrite: function overwrite(query, suggestions) { suggestions = suggestions || []; if (suggestions.length) { this._renderSuggestions(query, suggestions); } else if (this.async && this.templates.pending) { this._renderPending(query); } else if (!this.async && this.templates.notFound) { this._renderNotFound(query); } else { this._empty(); } this.trigger("rendered", suggestions, false, this.name); }, _append: function append(query, suggestions) { suggestions = suggestions || []; if (suggestions.length && this.$lastSuggestion.length) { this._appendSuggestions(query, suggestions); } else if (suggestions.length) { this._renderSuggestions(query, suggestions); } else if (!this.$lastSuggestion.length && this.templates.notFound) { this._renderNotFound(query); } this.trigger("rendered", suggestions, true, this.name); }, _renderSuggestions: function renderSuggestions(query, suggestions) { var $fragment; $fragment = this._getSuggestionsFragment(query, suggestions); this.$lastSuggestion = $fragment.children().last(); this.$el.html($fragment).prepend(this._getHeader(query, suggestions)).append(this._getFooter(query, suggestions)); }, _appendSuggestions: function appendSuggestions(query, suggestions) { var $fragment, $lastSuggestion; $fragment = this._getSuggestionsFragment(query, suggestions); $lastSuggestion = $fragment.children().last(); this.$lastSuggestion.after($fragment); this.$lastSuggestion = $lastSuggestion; }, _renderPending: function renderPending(query) { var template = this.templates.pending; this._resetLastSuggestion(); template && this.$el.html(template({ query: query, dataset: this.name })); }, _renderNotFound: function renderNotFound(query) { var template = this.templates.notFound; this._resetLastSuggestion(); template && this.$el.html(template({ query: query, dataset: this.name })); }, _empty: function empty() { this.$el.empty(); this._resetLastSuggestion(); }, _getSuggestionsFragment: function getSuggestionsFragment(query, suggestions) { var that = this, fragment; fragment = document.createDocumentFragment(); _.each(suggestions, function getSuggestionNode(suggestion) { var $el, context; context = that._injectQuery(query, suggestion); $el = $(that.templates.suggestion(context)).data(keys.dataset, that.name).data(keys.obj, suggestion).data(keys.val, that.displayFn(suggestion)).addClass(that.classes.suggestion + " " + that.classes.selectable); fragment.appendChild($el[0]); }); this.highlight && highlight({ className: this.classes.highlight, node: fragment, pattern: query }); return $(fragment); }, _getFooter: function getFooter(query, suggestions) { return this.templates.footer ? this.templates.footer({ query: query, suggestions: suggestions, dataset: this.name }) : null; }, _getHeader: function getHeader(query, suggestions) { return this.templates.header ? this.templates.header({ query: query, suggestions: suggestions, dataset: this.name }) : null; }, _resetLastSuggestion: function resetLastSuggestion() { this.$lastSuggestion = $(); }, _injectQuery: function injectQuery(query, obj) { return _.isObject(obj) ? _.mixin({ _query: query }, obj) : obj; }, update: function update(query) { var that = this, canceled = false, syncCalled = false, rendered = 0; this.cancel(); this.cancel = function cancel() { canceled = true; that.cancel = $.noop; that.async && that.trigger("asyncCanceled", query, that.name); }; this.source(query, sync, async); !syncCalled && sync([]); function sync(suggestions) { if (syncCalled) { return; } syncCalled = true; suggestions = (suggestions || []).slice(0, that.limit); rendered = suggestions.length; that._overwrite(query, suggestions); if (rendered < that.limit && that.async) { that.trigger("asyncRequested", query, that.name); } } function async(suggestions) { suggestions = suggestions || []; if (!canceled && rendered < that.limit) { that.cancel = $.noop; var idx = Math.abs(rendered - that.limit); rendered += idx; that._append(query, suggestions.slice(0, idx)); that.async && that.trigger("asyncReceived", query, that.name); } } }, cancel: $.noop, clear: function clear() { this._empty(); this.cancel(); this.trigger("cleared"); }, isEmpty: function isEmpty() { return this.$el.is(":empty"); }, destroy: function destroy() { this.$el = $("
"); } }); return Dataset; function getDisplayFn(display) { display = display || _.stringify; return _.isFunction(display) ? display : displayFn; function displayFn(obj) { return obj[display]; } } function getTemplates(templates, displayFn) { return { notFound: templates.notFound && _.templatify(templates.notFound), pending: templates.pending && _.templatify(templates.pending), header: templates.header && _.templatify(templates.header), footer: templates.footer && _.templatify(templates.footer), suggestion: templates.suggestion ? userSuggestionTemplate : suggestionTemplate }; function userSuggestionTemplate(context) { var template = templates.suggestion; return $(template(context)).attr("id", _.guid()); } function suggestionTemplate(context) { return $('
').attr("id", _.guid()).text(displayFn(context)); } } function isValidName(str) { return /^[_a-zA-Z0-9-]+$/.test(str); } }(); var Menu = function() { "use strict"; function Menu(o, www) { var that = this; o = o || {}; if (!o.node) { $.error("node is required"); } www.mixin(this); this.$node = $(o.node); this.query = null; this.datasets = _.map(o.datasets, initializeDataset); function initializeDataset(oDataset) { var node = that.$node.find(oDataset.node).first(); oDataset.node = node.length ? node : $("
").appendTo(that.$node); return new Dataset(oDataset, www); } } _.mixin(Menu.prototype, EventEmitter, { _onSelectableClick: function onSelectableClick($e) { this.trigger("selectableClicked", $($e.currentTarget)); }, _onRendered: function onRendered(type, dataset, suggestions, async) { this.$node.toggleClass(this.classes.empty, this._allDatasetsEmpty()); this.trigger("datasetRendered", dataset, suggestions, async); }, _onCleared: function onCleared() { this.$node.toggleClass(this.classes.empty, this._allDatasetsEmpty()); this.trigger("datasetCleared"); }, _propagate: function propagate() { this.trigger.apply(this, arguments); }, _allDatasetsEmpty: function allDatasetsEmpty() { return _.every(this.datasets, _.bind(function isDatasetEmpty(dataset) { var isEmpty = dataset.isEmpty(); this.$node.attr("aria-expanded", !isEmpty); return isEmpty; }, this)); }, _getSelectables: function getSelectables() { return this.$node.find(this.selectors.selectable); }, _removeCursor: function _removeCursor() { var $selectable = this.getActiveSelectable(); $selectable && $selectable.removeClass(this.classes.cursor); }, _ensureVisible: function ensureVisible($el) { var elTop, elBottom, nodeScrollTop, nodeHeight; elTop = $el.position().top; elBottom = elTop + $el.outerHeight(true); nodeScrollTop = this.$node.scrollTop(); nodeHeight = this.$node.height() + parseInt(this.$node.css("paddingTop"), 10) + parseInt(this.$node.css("paddingBottom"), 10); if (elTop < 0) { this.$node.scrollTop(nodeScrollTop + elTop); } else if (nodeHeight < elBottom) { this.$node.scrollTop(nodeScrollTop + (elBottom - nodeHeight)); } }, bind: function() { var that = this, onSelectableClick; onSelectableClick = _.bind(this._onSelectableClick, this); this.$node.on("click.tt", this.selectors.selectable, onSelectableClick); this.$node.on("mouseover", this.selectors.selectable, function() { that.setCursor($(this)); }); this.$node.on("mouseleave", function() { that._removeCursor(); }); _.each(this.datasets, function(dataset) { dataset.onSync("asyncRequested", that._propagate, that).onSync("asyncCanceled", that._propagate, that).onSync("asyncReceived", that._propagate, that).onSync("rendered", that._onRendered, that).onSync("cleared", that._onCleared, that); }); return this; }, isOpen: function isOpen() { return this.$node.hasClass(this.classes.open); }, open: function open() { this.$node.scrollTop(0); this.$node.addClass(this.classes.open); }, close: function close() { this.$node.attr("aria-expanded", false); this.$node.removeClass(this.classes.open); this._removeCursor(); }, setLanguageDirection: function setLanguageDirection(dir) { this.$node.attr("dir", dir); }, selectableRelativeToCursor: function selectableRelativeToCursor(delta) { var $selectables, $oldCursor, oldIndex, newIndex; $oldCursor = this.getActiveSelectable(); $selectables = this._getSelectables(); oldIndex = $oldCursor ? $selectables.index($oldCursor) : -1; newIndex = oldIndex + delta; newIndex = (newIndex + 1) % ($selectables.length + 1) - 1; newIndex = newIndex < -1 ? $selectables.length - 1 : newIndex; return newIndex === -1 ? null : $selectables.eq(newIndex); }, setCursor: function setCursor($selectable) { this._removeCursor(); if ($selectable = $selectable && $selectable.first()) { $selectable.addClass(this.classes.cursor); this._ensureVisible($selectable); } }, getSelectableData: function getSelectableData($el) { return $el && $el.length ? Dataset.extractData($el) : null; }, getActiveSelectable: function getActiveSelectable() { var $selectable = this._getSelectables().filter(this.selectors.cursor).first(); return $selectable.length ? $selectable : null; }, getTopSelectable: function getTopSelectable() { var $selectable = this._getSelectables().first(); return $selectable.length ? $selectable : null; }, update: function update(query) { var isValidUpdate = query !== this.query; if (isValidUpdate) { this.query = query; _.each(this.datasets, updateDataset); } return isValidUpdate; function updateDataset(dataset) { dataset.update(query); } }, empty: function empty() { _.each(this.datasets, clearDataset); this.query = null; this.$node.addClass(this.classes.empty); function clearDataset(dataset) { dataset.clear(); } }, destroy: function destroy() { this.$node.off(".tt"); this.$node = $("
"); _.each(this.datasets, destroyDataset); function destroyDataset(dataset) { dataset.destroy(); } } }); return Menu; }(); var Status = function() { "use strict"; function Status(options) { this.$el = $("", { role: "status", "aria-live": "polite" }).css({ position: "absolute", padding: "0", border: "0", height: "1px", width: "1px", "margin-bottom": "-1px", "margin-right": "-1px", overflow: "hidden", clip: "rect(0 0 0 0)", "white-space": "nowrap" }); options.$input.after(this.$el); _.each(options.menu.datasets, _.bind(function(dataset) { if (dataset.onSync) { dataset.onSync("rendered", _.bind(this.update, this)); dataset.onSync("cleared", _.bind(this.cleared, this)); } }, this)); } _.mixin(Status.prototype, { update: function update(event, suggestions) { var length = suggestions.length; var words; if (length === 1) { words = { result: "result", is: "is" }; } else { words = { result: "results", is: "are" }; } this.$el.text(length + " " + words.result + " " + words.is + " available, use up and down arrow keys to navigate."); }, cleared: function() { this.$el.text(""); } }); return Status; }(); var DefaultMenu = function() { "use strict"; var s = Menu.prototype; function DefaultMenu() { Menu.apply(this, [].slice.call(arguments, 0)); } _.mixin(DefaultMenu.prototype, Menu.prototype, { open: function open() { !this._allDatasetsEmpty() && this._show(); return s.open.apply(this, [].slice.call(arguments, 0)); }, close: function close() { this._hide(); return s.close.apply(this, [].slice.call(arguments, 0)); }, _onRendered: function onRendered() { if (this._allDatasetsEmpty()) { this._hide(); } else { this.isOpen() && this._show(); } return s._onRendered.apply(this, [].slice.call(arguments, 0)); }, _onCleared: function onCleared() { if (this._allDatasetsEmpty()) { this._hide(); } else { this.isOpen() && this._show(); } return s._onCleared.apply(this, [].slice.call(arguments, 0)); }, setLanguageDirection: function setLanguageDirection(dir) { this.$node.css(dir === "ltr" ? this.css.ltr : this.css.rtl); return s.setLanguageDirection.apply(this, [].slice.call(arguments, 0)); }, _hide: function hide() { this.$node.hide(); }, _show: function show() { this.$node.css("display", "block"); } }); return DefaultMenu; }(); var Typeahead = function() { "use strict"; function Typeahead(o, www) { var onFocused, onBlurred, onEnterKeyed, onTabKeyed, onEscKeyed, onUpKeyed, onDownKeyed, onLeftKeyed, onRightKeyed, onQueryChanged, onWhitespaceChanged; o = o || {}; if (!o.input) { $.error("missing input"); } if (!o.menu) { $.error("missing menu"); } if (!o.eventBus) { $.error("missing event bus"); } www.mixin(this); this.eventBus = o.eventBus; this.minLength = _.isNumber(o.minLength) ? o.minLength : 1; this.input = o.input; this.menu = o.menu; this.enabled = true; this.autoselect = !!o.autoselect; this.active = false; this.input.hasFocus() && this.activate(); this.dir = this.input.getLangDir(); this._hacks(); this.menu.bind().onSync("selectableClicked", this._onSelectableClicked, this).onSync("asyncRequested", this._onAsyncRequested, this).onSync("asyncCanceled", this._onAsyncCanceled, this).onSync("asyncReceived", this._onAsyncReceived, this).onSync("datasetRendered", this._onDatasetRendered, this).onSync("datasetCleared", this._onDatasetCleared, this); onFocused = c(this, "activate", "open", "_onFocused"); onBlurred = c(this, "deactivate", "_onBlurred"); onEnterKeyed = c(this, "isActive", "isOpen", "_onEnterKeyed"); onTabKeyed = c(this, "isActive", "isOpen", "_onTabKeyed"); onEscKeyed = c(this, "isActive", "_onEscKeyed"); onUpKeyed = c(this, "isActive", "open", "_onUpKeyed"); onDownKeyed = c(this, "isActive", "open", "_onDownKeyed"); onLeftKeyed = c(this, "isActive", "isOpen", "_onLeftKeyed"); onRightKeyed = c(this, "isActive", "isOpen", "_onRightKeyed"); onQueryChanged = c(this, "_openIfActive", "_onQueryChanged"); onWhitespaceChanged = c(this, "_openIfActive", "_onWhitespaceChanged"); this.input.bind().onSync("focused", onFocused, this).onSync("blurred", onBlurred, this).onSync("enterKeyed", onEnterKeyed, this).onSync("tabKeyed", onTabKeyed, this).onSync("escKeyed", onEscKeyed, this).onSync("upKeyed", onUpKeyed, this).onSync("downKeyed", onDownKeyed, this).onSync("leftKeyed", onLeftKeyed, this).onSync("rightKeyed", onRightKeyed, this).onSync("queryChanged", onQueryChanged, this).onSync("whitespaceChanged", onWhitespaceChanged, this).onSync("langDirChanged", this._onLangDirChanged, this); } _.mixin(Typeahead.prototype, { _hacks: function hacks() { var $input, $menu; $input = this.input.$input || $("
"); $menu = this.menu.$node || $("
"); $input.on("blur.tt", function($e) { var active, isActive, hasActive; active = document.activeElement; isActive = $menu.is(active); hasActive = $menu.has(active).length > 0; if (_.isMsie() && (isActive || hasActive)) { $e.preventDefault(); $e.stopImmediatePropagation(); _.defer(function() { $input.focus(); }); } }); $menu.on("mousedown.tt", function($e) { $e.preventDefault(); }); }, _onSelectableClicked: function onSelectableClicked(type, $el) { this.select($el); }, _onDatasetCleared: function onDatasetCleared() { this._updateHint(); }, _onDatasetRendered: function onDatasetRendered(type, suggestions, async, dataset) { this._updateHint(); if (this.autoselect) { var cursorClass = this.selectors.cursor.substr(1); this.menu.$node.find(this.selectors.suggestion).first().addClass(cursorClass); } this.eventBus.trigger("render", suggestions, async, dataset); }, _onAsyncRequested: function onAsyncRequested(type, dataset, query) { this.eventBus.trigger("asyncrequest", query, dataset); }, _onAsyncCanceled: function onAsyncCanceled(type, dataset, query) { this.eventBus.trigger("asynccancel", query, dataset); }, _onAsyncReceived: function onAsyncReceived(type, dataset, query) { this.eventBus.trigger("asyncreceive", query, dataset); }, _onFocused: function onFocused() { this._minLengthMet() && this.menu.update(this.input.getQuery()); }, _onBlurred: function onBlurred() { if (this.input.hasQueryChangedSinceLastFocus()) { this.eventBus.trigger("change", this.input.getQuery()); } }, _onEnterKeyed: function onEnterKeyed(type, $e) { var $selectable; if ($selectable = this.menu.getActiveSelectable()) { if (this.select($selectable)) { $e.preventDefault(); $e.stopPropagation(); } } else if (this.autoselect) { if (this.select(this.menu.getTopSelectable())) { $e.preventDefault(); $e.stopPropagation(); } } }, _onTabKeyed: function onTabKeyed(type, $e) { var $selectable; if ($selectable = this.menu.getActiveSelectable()) { this.select($selectable) && $e.preventDefault(); } else if (this.autoselect) { if ($selectable = this.menu.getTopSelectable()) { this.autocomplete($selectable) && $e.preventDefault(); } } }, _onEscKeyed: function onEscKeyed() { this.close(); }, _onUpKeyed: function onUpKeyed() { this.moveCursor(-1); }, _onDownKeyed: function onDownKeyed() { this.moveCursor(+1); }, _onLeftKeyed: function onLeftKeyed() { if (this.dir === "rtl" && this.input.isCursorAtEnd()) { this.autocomplete(this.menu.getActiveSelectable() || this.menu.getTopSelectable()); } }, _onRightKeyed: function onRightKeyed() { if (this.dir === "ltr" && this.input.isCursorAtEnd()) { this.autocomplete(this.menu.getActiveSelectable() || this.menu.getTopSelectable()); } }, _onQueryChanged: function onQueryChanged(e, query) { this._minLengthMet(query) ? this.menu.update(query) : this.menu.empty(); }, _onWhitespaceChanged: function onWhitespaceChanged() { this._updateHint(); }, _onLangDirChanged: function onLangDirChanged(e, dir) { if (this.dir !== dir) { this.dir = dir; this.menu.setLanguageDirection(dir); } }, _openIfActive: function openIfActive() { this.isActive() && this.open(); }, _minLengthMet: function minLengthMet(query) { query = _.isString(query) ? query : this.input.getQuery() || ""; return query.length >= this.minLength; }, _updateHint: function updateHint() { var $selectable, data, val, query, escapedQuery, frontMatchRegEx, match; $selectable = this.menu.getTopSelectable(); data = this.menu.getSelectableData($selectable); val = this.input.getInputValue(); if (data && !_.isBlankString(val) && !this.input.hasOverflow()) { query = Input.normalizeQuery(val); escapedQuery = _.escapeRegExChars(query); frontMatchRegEx = new RegExp("^(?:" + escapedQuery + ")(.+$)", "i"); match = frontMatchRegEx.exec(data.val); match && this.input.setHint(val + match[1]); } else { this.input.clearHint(); } }, isEnabled: function isEnabled() { return this.enabled; }, enable: function enable() { this.enabled = true; }, disable: function disable() { this.enabled = false; }, isActive: function isActive() { return this.active; }, activate: function activate() { if (this.isActive()) { return true; } else if (!this.isEnabled() || this.eventBus.before("active")) { return false; } else { this.active = true; this.eventBus.trigger("active"); return true; } }, deactivate: function deactivate() { if (!this.isActive()) { return true; } else if (this.eventBus.before("idle")) { return false; } else { this.active = false; this.close(); this.eventBus.trigger("idle"); return true; } }, isOpen: function isOpen() { return this.menu.isOpen(); }, open: function open() { if (!this.isOpen() && !this.eventBus.before("open")) { this.input.setAriaExpanded(true); this.menu.open(); this._updateHint(); this.eventBus.trigger("open"); } return this.isOpen(); }, close: function close() { if (this.isOpen() && !this.eventBus.before("close")) { this.input.setAriaExpanded(false); this.menu.close(); this.input.clearHint(); this.input.resetInputValue(); this.eventBus.trigger("close"); } return !this.isOpen(); }, setVal: function setVal(val) { this.input.setQuery(_.toStr(val)); }, getVal: function getVal() { return this.input.getQuery(); }, select: function select($selectable) { var data = this.menu.getSelectableData($selectable); if (data && !this.eventBus.before("select", data.obj, data.dataset)) { this.input.setQuery(data.val, true); this.eventBus.trigger("select", data.obj, data.dataset); this.close(); return true; } return false; }, autocomplete: function autocomplete($selectable) { var query, data, isValid; query = this.input.getQuery(); data = this.menu.getSelectableData($selectable); isValid = data && query !== data.val; if (isValid && !this.eventBus.before("autocomplete", data.obj, data.dataset)) { this.input.setQuery(data.val); this.eventBus.trigger("autocomplete", data.obj, data.dataset); return true; } return false; }, moveCursor: function moveCursor(delta) { var query, $candidate, data, suggestion, datasetName, cancelMove, id; query = this.input.getQuery(); $candidate = this.menu.selectableRelativeToCursor(delta); data = this.menu.getSelectableData($candidate); suggestion = data ? data.obj : null; datasetName = data ? data.dataset : null; id = $candidate ? $candidate.attr("id") : null; this.input.trigger("cursorchange", id); cancelMove = this._minLengthMet() && this.menu.update(query); if (!cancelMove && !this.eventBus.before("cursorchange", suggestion, datasetName)) { this.menu.setCursor($candidate); if (data) { if (typeof data.val === "string") { this.input.setInputValue(data.val); } } else { this.input.resetInputValue(); this._updateHint(); } this.eventBus.trigger("cursorchange", suggestion, datasetName); return true; } return false; }, destroy: function destroy() { this.input.destroy(); this.menu.destroy(); } }); return Typeahead; function c(ctx) { var methods = [].slice.call(arguments, 1); return function() { var args = [].slice.call(arguments); _.each(methods, function(method) { return ctx[method].apply(ctx, args); }); }; } }(); (function() { "use strict"; var old, keys, methods; old = $.fn.typeahead; keys = { www: "tt-www", attrs: "tt-attrs", typeahead: "tt-typeahead" }; methods = { initialize: function initialize(o, datasets) { var www; datasets = _.isArray(datasets) ? datasets : [].slice.call(arguments, 1); o = o || {}; www = WWW(o.classNames); return this.each(attach); function attach() { var $input, $wrapper, $hint, $menu, defaultHint, defaultMenu, eventBus, input, menu, status, typeahead, MenuConstructor; _.each(datasets, function(d) { d.highlight = !!o.highlight; }); $input = $(this); $wrapper = $(www.html.wrapper); $hint = $elOrNull(o.hint); $menu = $elOrNull(o.menu); defaultHint = o.hint !== false && !$hint; defaultMenu = o.menu !== false && !$menu; defaultHint && ($hint = buildHintFromInput($input, www)); defaultMenu && ($menu = $(www.html.menu).css(www.css.menu)); $hint && $hint.val(""); $input = prepInput($input, www); if (defaultHint || defaultMenu) { $wrapper.css(www.css.wrapper); $input.css(defaultHint ? www.css.input : www.css.inputWithNoHint); $input.wrap($wrapper).parent().prepend(defaultHint ? $hint : null).append(defaultMenu ? $menu : null); } MenuConstructor = defaultMenu ? DefaultMenu : Menu; eventBus = new EventBus({ el: $input }); input = new Input({ hint: $hint, input: $input, menu: $menu }, www); menu = new MenuConstructor({ node: $menu, datasets: datasets }, www); status = new Status({ $input: $input, menu: menu }); typeahead = new Typeahead({ input: input, menu: menu, eventBus: eventBus, minLength: o.minLength, autoselect: o.autoselect }, www); $input.data(keys.www, www); $input.data(keys.typeahead, typeahead); } }, isEnabled: function isEnabled() { var enabled; ttEach(this.first(), function(t) { enabled = t.isEnabled(); }); return enabled; }, enable: function enable() { ttEach(this, function(t) { t.enable(); }); return this; }, disable: function disable() { ttEach(this, function(t) { t.disable(); }); return this; }, isActive: function isActive() { var active; ttEach(this.first(), function(t) { active = t.isActive(); }); return active; }, activate: function activate() { ttEach(this, function(t) { t.activate(); }); return this; }, deactivate: function deactivate() { ttEach(this, function(t) { t.deactivate(); }); return this; }, isOpen: function isOpen() { var open; ttEach(this.first(), function(t) { open = t.isOpen(); }); return open; }, open: function open() { ttEach(this, function(t) { t.open(); }); return this; }, close: function close() { ttEach(this, function(t) { t.close(); }); return this; }, select: function select(el) { var success = false, $el = $(el); ttEach(this.first(), function(t) { success = t.select($el); }); return success; }, autocomplete: function autocomplete(el) { var success = false, $el = $(el); ttEach(this.first(), function(t) { success = t.autocomplete($el); }); return success; }, moveCursor: function moveCursoe(delta) { var success = false; ttEach(this.first(), function(t) { success = t.moveCursor(delta); }); return success; }, val: function val(newVal) { var query; if (!arguments.length) { ttEach(this.first(), function(t) { query = t.getVal(); }); return query; } else { ttEach(this, function(t) { t.setVal(_.toStr(newVal)); }); return this; } }, destroy: function destroy() { ttEach(this, function(typeahead, $input) { revert($input); typeahead.destroy(); }); return this; } }; $.fn.typeahead = function(method) { if (methods[method]) { return methods[method].apply(this, [].slice.call(arguments, 1)); } else { return methods.initialize.apply(this, arguments); } }; $.fn.typeahead.noConflict = function noConflict() { $.fn.typeahead = old; return this; }; function ttEach($els, fn) { $els.each(function() { var $input = $(this), typeahead; (typeahead = $input.data(keys.typeahead)) && fn(typeahead, $input); }); } function buildHintFromInput($input, www) { return $input.clone().addClass(www.classes.hint).removeData().css(www.css.hint).css(getBackgroundStyles($input)).prop({ readonly: true, required: false }).removeAttr("id name placeholder").removeClass("required").attr({ spellcheck: "false", tabindex: -1 }); } function prepInput($input, www) { $input.data(keys.attrs, { dir: $input.attr("dir"), autocomplete: $input.attr("autocomplete"), spellcheck: $input.attr("spellcheck"), style: $input.attr("style") }); $input.addClass(www.classes.input).attr({ spellcheck: false }); try { !$input.attr("dir") && $input.attr("dir", "auto"); } catch (e) {} return $input; } function getBackgroundStyles($el) { return { backgroundAttachment: $el.css("background-attachment"), backgroundClip: $el.css("background-clip"), backgroundColor: $el.css("background-color"), backgroundImage: $el.css("background-image"), backgroundOrigin: $el.css("background-origin"), backgroundPosition: $el.css("background-position"), backgroundRepeat: $el.css("background-repeat"), backgroundSize: $el.css("background-size") }; } function revert($input) { var www, $wrapper; www = $input.data(keys.www); $wrapper = $input.parent().filter(www.selectors.wrapper); _.each($input.data(keys.attrs), function(val, key) { _.isUndefined(val) ? $input.removeAttr(key) : $input.attr(key, val); }); $input.removeData(keys.typeahead).removeData(keys.www).removeData(keys.attr).removeClass(www.classes.input); if ($wrapper.length) { $input.detach().insertAfter($wrapper); $wrapper.remove(); } } function $elOrNull(obj) { var isValid, $el; isValid = _.isJQuery(obj) || _.isElement(obj); $el = isValid ? $(obj).first() : []; return $el.length ? $el : null; } })(); }); ================================================ FILE: docs/search.json ================================================ {"Typealiases.html#/s:8SocketIO11AckCallbacka":{"name":"AckCallback","abstract":"

A typealias for an ack callback.

"},"Typealiases.html#/s:8SocketIO14NormalCallbacka":{"name":"NormalCallback","abstract":"

A typealias for a normal callback.

"},"Typealiases.html#/s:8SocketIO4Posta":{"name":"Post","abstract":"

A typealias for a queued POST

"},"Structs/SocketPacket/PacketType.html#/s:8SocketIO0A6PacketV0C4TypeO7connectyA2EmF":{"name":"connect","abstract":"

Connect: 0

","parent_name":"PacketType"},"Structs/SocketPacket/PacketType.html#/s:8SocketIO0A6PacketV0C4TypeO10disconnectyA2EmF":{"name":"disconnect","abstract":"

Disconnect: 1

","parent_name":"PacketType"},"Structs/SocketPacket/PacketType.html#/s:8SocketIO0A6PacketV0C4TypeO5eventyA2EmF":{"name":"event","abstract":"

Event: 2

","parent_name":"PacketType"},"Structs/SocketPacket/PacketType.html#/s:8SocketIO0A6PacketV0C4TypeO3ackyA2EmF":{"name":"ack","abstract":"

Ack: 3

","parent_name":"PacketType"},"Structs/SocketPacket/PacketType.html#/s:8SocketIO0A6PacketV0C4TypeO5erroryA2EmF":{"name":"error","abstract":"

Error: 4

","parent_name":"PacketType"},"Structs/SocketPacket/PacketType.html#/s:8SocketIO0A6PacketV0C4TypeO11binaryEventyA2EmF":{"name":"binaryEvent","abstract":"

Binary Event: 5

","parent_name":"PacketType"},"Structs/SocketPacket/PacketType.html#/s:8SocketIO0A6PacketV0C4TypeO9binaryAckyA2EmF":{"name":"binaryAck","abstract":"

Binary Ack: 6

","parent_name":"PacketType"},"Structs/SocketPacket/PacketType.html#/s:8SocketIO0A6PacketV0C4TypeO8isBinarySbvp":{"name":"isBinary","abstract":"

Whether or not this type is binary

","parent_name":"PacketType"},"Structs/SocketPacket.html#/s:8SocketIO0A6PacketV3nspSSvp":{"name":"nsp","abstract":"

The namespace for this packet.

","parent_name":"SocketPacket"},"Structs/SocketPacket.html#/s:8SocketIO0A6PacketV2idSivp":{"name":"id","abstract":"

If > 0 then this packet is using acking.

","parent_name":"SocketPacket"},"Structs/SocketPacket.html#/s:8SocketIO0A6PacketV4typeAC0C4TypeOvp":{"name":"type","abstract":"

The type of this packet.

","parent_name":"SocketPacket"},"Structs/SocketPacket.html#/s:8SocketIO0A6PacketV6binarySay10Foundation4DataVGvp":{"name":"binary","abstract":"

An array of binary data for this packet.

","parent_name":"SocketPacket"},"Structs/SocketPacket.html#/s:8SocketIO0A6PacketV4dataSayypGvp":{"name":"data","abstract":"

The data for this event.

","parent_name":"SocketPacket"},"Structs/SocketPacket.html#/s:8SocketIO0A6PacketV4argsSayypGvp":{"name":"args","abstract":"

Returns the payload for this packet, minus the event name if this is an event or binaryEvent type packet.

","parent_name":"SocketPacket"},"Structs/SocketPacket.html#/s:8SocketIO0A6PacketV11descriptionSSvp":{"name":"description","abstract":"

A string representation of this packet.

","parent_name":"SocketPacket"},"Structs/SocketPacket.html#/s:8SocketIO0A6PacketV5eventSSvp":{"name":"event","abstract":"

The event name for this packet.

","parent_name":"SocketPacket"},"Structs/SocketPacket.html#/s:8SocketIO0A6PacketV12packetStringSSvp":{"name":"packetString","abstract":"

A string representation of this packet.

","parent_name":"SocketPacket"},"Structs/SocketPacket/PacketType.html":{"name":"PacketType","abstract":"

The type of packets.

","parent_name":"SocketPacket"},"Structs/SocketIOClientConfiguration.html#/s:8SocketIO0A21IOClientConfigurationV7Elementa":{"name":"Element","abstract":"

Type of element stored.

","parent_name":"SocketIOClientConfiguration"},"Structs/SocketIOClientConfiguration.html#/s:8SocketIO0A21IOClientConfigurationV5Indexa":{"name":"Index","abstract":"

Index type.

","parent_name":"SocketIOClientConfiguration"},"Structs/SocketIOClientConfiguration.html#/s:8SocketIO0A21IOClientConfigurationV8Iteratora":{"name":"Iterator","abstract":"

Iterator type.

","parent_name":"SocketIOClientConfiguration"},"Structs/SocketIOClientConfiguration.html#/s:8SocketIO0A21IOClientConfigurationV11SubSequencea":{"name":"SubSequence","abstract":"

SubSequence type.

","parent_name":"SocketIOClientConfiguration"},"Structs/SocketIOClientConfiguration.html#/s:8SocketIO0A21IOClientConfigurationV10startIndexSivp":{"name":"startIndex","abstract":"

The start index of this collection.

","parent_name":"SocketIOClientConfiguration"},"Structs/SocketIOClientConfiguration.html#/s:8SocketIO0A21IOClientConfigurationV8endIndexSivp":{"name":"endIndex","abstract":"

The end index of this collection.

","parent_name":"SocketIOClientConfiguration"},"Structs/SocketIOClientConfiguration.html#/s:8SocketIO0A21IOClientConfigurationV7isEmptySbvp":{"name":"isEmpty","abstract":"

Whether this collection is empty.

","parent_name":"SocketIOClientConfiguration"},"Structs/SocketIOClientConfiguration.html#/s:8SocketIO0A21IOClientConfigurationV5countSivp":{"name":"count","abstract":"

The number of elements stored in this collection.

","parent_name":"SocketIOClientConfiguration"},"Structs/SocketIOClientConfiguration.html#/s:8SocketIO0A21IOClientConfigurationV5firstAA0aC6OptionOSgvp":{"name":"first","abstract":"

The first element in this collection.

","parent_name":"SocketIOClientConfiguration"},"Structs/SocketIOClientConfiguration.html#/s:Sly7ElementQz5IndexQzcip":{"name":"subscript(_:)","parent_name":"SocketIOClientConfiguration"},"Structs/SocketIOClientConfiguration.html#/s:Sly11SubSequenceQzSny5IndexQzGcip":{"name":"subscript(_:)","parent_name":"SocketIOClientConfiguration"},"Structs/SocketIOClientConfiguration.html#/s:8SocketIO0A21IOClientConfigurationV12arrayLiteralAcA0aC6OptionOd_tcfc":{"name":"init(arrayLiteral:)","abstract":"

Creates a new SocketIOClientConfiguration from an array literal.

","parent_name":"SocketIOClientConfiguration"},"Structs/SocketIOClientConfiguration.html#/s:8SocketIO0A21IOClientConfigurationV12makeIterators08IndexingF0VySayAA0aC6OptionOGGyF":{"name":"makeIterator()","abstract":"

Creates an iterator for this collection.

","parent_name":"SocketIOClientConfiguration"},"Structs/SocketIOClientConfiguration.html#/s:8SocketIO0A21IOClientConfigurationV5index5afterS2i_tF":{"name":"index(after:)","parent_name":"SocketIOClientConfiguration"},"Structs/SocketIOClientConfiguration.html#/s:8SocketIO0A21IOClientConfigurationV6insert_9replacingyAA0aC6OptionO_SbtF":{"name":"insert(_:replacing:)","abstract":"

Special method that inserts element into the collection, replacing any other instances of element.

","parent_name":"SocketIOClientConfiguration"},"Structs/SocketEventHandler.html#/s:8SocketIO0A12EventHandlerV5eventSSvp":{"name":"event","abstract":"

The event for this handler.

","parent_name":"SocketEventHandler"},"Structs/SocketEventHandler.html#/s:8SocketIO0A12EventHandlerV2id10Foundation4UUIDVvp":{"name":"id","abstract":"

A unique identifier for this handler.

","parent_name":"SocketEventHandler"},"Structs/SocketEventHandler.html#/s:8SocketIO0A12EventHandlerV8callbackyySayypG_AA0A10AckEmitterCtcvp":{"name":"callback","abstract":"

The actual handler function.

","parent_name":"SocketEventHandler"},"Structs/SocketEventHandler.html#/s:8SocketIO0A12EventHandlerV15executeCallback4with0G3Ack0gA0ySayypG_SiAA0A8IOClientCtF":{"name":"executeCallback(with:withAck:withSocket:)","abstract":"

Causes this handler to be executed.

","parent_name":"SocketEventHandler"},"Structs/SocketEventHandler.html":{"name":"SocketEventHandler","abstract":"

A wrapper around a handler.

"},"Structs/SocketIOClientConfiguration.html":{"name":"SocketIOClientConfiguration","abstract":"

An array-like type that holds SocketIOClientOptions

"},"Structs/SocketPacket.html":{"name":"SocketPacket","abstract":"

A struct that represents a socket.io packet.

"},"Protocols/SocketData.html#/s:8SocketIO0A4DataP20socketRepresentationAaB_pyKF":{"name":"socketRepresentation()","abstract":"

A representation of self that can sent over socket.io.

","parent_name":"SocketData"},"Protocols/SocketLogger.html#/s:8SocketIO0A6LoggerP3logSbvp":{"name":"log","abstract":"

Whether to log or not

","parent_name":"SocketLogger"},"Protocols/SocketLogger.html#/s:8SocketIO0A6LoggerP3log_4typeySSyXK_SStF":{"name":"log(_:type:)","abstract":"

Normal log messages

","parent_name":"SocketLogger"},"Protocols/SocketLogger.html#/s:8SocketIO0A6LoggerP5error_4typeySSyXK_SStF":{"name":"error(_:type:)","abstract":"

Error Messages

","parent_name":"SocketLogger"},"Protocols/SocketDataBufferable.html#/s:8SocketIO0A14DataBufferableP14waitingPacketsSayAA0A6PacketVGvp":{"name":"waitingPackets","abstract":"

A list of packets that are waiting for binary data.

","parent_name":"SocketDataBufferable"},"Protocols/SocketParsable.html#/s:8SocketIO0A8ParsableP15parseBinaryDatayAA0A6PacketVSg10Foundation0F0VF":{"name":"parseBinaryData(_:)","abstract":"

Called when the engine has received some binary data that should be attached to a packet.

","parent_name":"SocketParsable"},"Protocols/SocketParsable.html#/s:8SocketIO0A8ParsableP05parseA7MessageyAA0A6PacketVSgSSF":{"name":"parseSocketMessage(_:)","abstract":"

Called when the engine has received a string that should be parsed into a socket.io packet.

","parent_name":"SocketParsable"},"Protocols/SocketParsable.html#/s:8SocketIO0A8ParsablePA2A0A14DataBufferableRzAA0A11ManagerSpecRzrlE05parseA7MessageyAA0A6PacketVSgSSF":{"name":"parseSocketMessage(_:)","parent_name":"SocketParsable"},"Protocols/SocketParsable.html#/s:8SocketIO0A8ParsablePA2A0A14DataBufferableRzAA0A11ManagerSpecRzrlE011parseBinaryD0yAA0A6PacketVSg10Foundation0D0VF":{"name":"parseBinaryData(_:)","parent_name":"SocketParsable"},"Protocols/SocketManagerSpec.html#/s:8SocketIO0A11ManagerSpecP07defaultA0AA0A8IOClientCvp":{"name":"defaultSocket","abstract":"

Returns the socket associated with the default namespace (“/”).

","parent_name":"SocketManagerSpec"},"Protocols/SocketManagerSpec.html#/s:8SocketIO0A11ManagerSpecP6engineAA0a6EngineD0_pSgvp":{"name":"engine","abstract":"

The engine for this manager.

","parent_name":"SocketManagerSpec"},"Protocols/SocketManagerSpec.html#/s:8SocketIO0A11ManagerSpecP8forceNewSbvp":{"name":"forceNew","abstract":"

If true then every time connect is called, a new engine will be created.

","parent_name":"SocketManagerSpec"},"Protocols/SocketManagerSpec.html#/s:8SocketIO0A11ManagerSpecP11handleQueueSo17OS_dispatch_queueCvp":{"name":"handleQueue","abstract":"

The queue that all interaction with the client should occur on. This is the queue that event handlers are","parent_name":"SocketManagerSpec"},"Protocols/SocketManagerSpec.html#/s:8SocketIO0A11ManagerSpecP4nspsSDySSAA0A8IOClientCGvp":{"name":"nsps","abstract":"

The sockets in this manager indexed by namespace.

","parent_name":"SocketManagerSpec"},"Protocols/SocketManagerSpec.html#/s:8SocketIO0A11ManagerSpecP10reconnectsSbvp":{"name":"reconnects","abstract":"

If true, this manager will try and reconnect on any disconnects.

","parent_name":"SocketManagerSpec"},"Protocols/SocketManagerSpec.html#/s:8SocketIO0A11ManagerSpecP13reconnectWaitSivp":{"name":"reconnectWait","abstract":"

The minimum number of seconds to wait before attempting to reconnect.

","parent_name":"SocketManagerSpec"},"Protocols/SocketManagerSpec.html#/s:8SocketIO0A11ManagerSpecP16reconnectWaitMaxSivp":{"name":"reconnectWaitMax","abstract":"

The maximum number of seconds to wait before attempting to reconnect.

","parent_name":"SocketManagerSpec"},"Protocols/SocketManagerSpec.html#/s:8SocketIO0A11ManagerSpecP19randomizationFactorSdvp":{"name":"randomizationFactor","abstract":"

The randomization factor for calculating reconnect jitter.

","parent_name":"SocketManagerSpec"},"Protocols/SocketManagerSpec.html#/s:8SocketIO0A11ManagerSpecP9socketURL10Foundation0F0Vvp":{"name":"socketURL","abstract":"

The URL of the socket.io server.

","parent_name":"SocketManagerSpec"},"Protocols/SocketManagerSpec.html#/s:8SocketIO0A11ManagerSpecP6statusAA0A8IOStatusOvp":{"name":"status","abstract":"

The status of this manager.

","parent_name":"SocketManagerSpec"},"Protocols/SocketManagerSpec.html#/s:8SocketIO0A11ManagerSpecP7versionAA0A9IOVersionOvp":{"name":"version","abstract":"

The version of socket.io in use.

","parent_name":"SocketManagerSpec"},"Protocols/SocketManagerSpec.html#/s:8SocketIO0A11ManagerSpecP7connectyyF":{"name":"connect()","abstract":"

Connects the underlying transport.

","parent_name":"SocketManagerSpec"},"Protocols/SocketManagerSpec.html#/s:8SocketIO0A11ManagerSpecP07connectA0_11withPayloadyAA0A8IOClientC_SDySSypGSgtF":{"name":"connectSocket(_:withPayload:)","abstract":"

Connects a socket through this manager’s engine.

","parent_name":"SocketManagerSpec"},"Protocols/SocketManagerSpec.html#/s:8SocketIO0A11ManagerSpecP13didDisconnect6reasonySS_tF":{"name":"didDisconnect(reason:)","abstract":"

Called when the manager has disconnected from socket.io.

","parent_name":"SocketManagerSpec"},"Protocols/SocketManagerSpec.html#/s:8SocketIO0A11ManagerSpecP10disconnectyyF":{"name":"disconnect()","abstract":"

Disconnects the manager and all associated sockets.

","parent_name":"SocketManagerSpec"},"Protocols/SocketManagerSpec.html#/s:8SocketIO0A11ManagerSpecP010disconnectA0yyAA0A8IOClientCF":{"name":"disconnectSocket(_:)","abstract":"

Disconnects the given socket.

","parent_name":"SocketManagerSpec"},"Protocols/SocketManagerSpec.html#/s:8SocketIO0A11ManagerSpecP010disconnectA012forNamespaceySS_tF":{"name":"disconnectSocket(forNamespace:)","abstract":"

Disconnects the socket associated with forNamespace.

","parent_name":"SocketManagerSpec"},"Protocols/SocketManagerSpec.html#/s:8SocketIO0A11ManagerSpecP7emitAllyySS_AA0A4Data_pdtF":{"name":"emitAll(_:_:)","abstract":"

Sends an event to the server on all namespaces in this manager.

","parent_name":"SocketManagerSpec"},"Protocols/SocketManagerSpec.html#/s:8SocketIO0A11ManagerSpecP9reconnectyyF":{"name":"reconnect()","abstract":"

Tries to reconnect to the server.

","parent_name":"SocketManagerSpec"},"Protocols/SocketManagerSpec.html#/s:8SocketIO0A11ManagerSpecP06removeA0yAA0A8IOClientCSgAFF":{"name":"removeSocket(_:)","abstract":"

Removes the socket from the manager’s control.","parent_name":"SocketManagerSpec"},"Protocols/SocketManagerSpec.html#/s:8SocketIO0A11ManagerSpecP6socket12forNamespaceAA0A8IOClientCSS_tF":{"name":"socket(forNamespace:)","abstract":"

Returns a SocketIOClient for the given namespace. This socket shares a transport with the manager.

","parent_name":"SocketManagerSpec"},"Protocols/SocketEngineWebsocket.html#/s:8SocketIO0A15EngineWebsocketP11wsConnectedSbvp":{"name":"wsConnected","abstract":"

Whether or not the ws is connected

","parent_name":"SocketEngineWebsocket"},"Protocols/SocketEngineWebsocket.html#/s:8SocketIO0A15EngineWebsocketP07sendWebA7Message_8withType0H4Data10completionySS_AA0ac6PacketI0OSay10Foundation0J0VGyycSgtF":{"name":"sendWebSocketMessage(_:withType:withData:completion:)","abstract":"

Sends an engine.io message through the WebSocket transport.

","parent_name":"SocketEngineWebsocket"},"Protocols/SocketEngineSpec.html#/s:8SocketIO0A10EngineSpecP6clientAA0aC6Client_pSgvp":{"name":"client","abstract":"

The client for this engine.

","parent_name":"SocketEngineSpec"},"Protocols/SocketEngineSpec.html#/s:8SocketIO0A10EngineSpecP6closedSbvp":{"name":"closed","abstract":"

true if this engine is closed.

","parent_name":"SocketEngineSpec"},"Protocols/SocketEngineSpec.html#/s:8SocketIO0A10EngineSpecP8compressSbvp":{"name":"compress","abstract":"

If true the engine will attempt to use WebSocket compression.

","parent_name":"SocketEngineSpec"},"Protocols/SocketEngineSpec.html#/s:8SocketIO0A10EngineSpecP9connectedSbvp":{"name":"connected","abstract":"

true if this engine is connected. Connected means that the initial poll connect has succeeded.

","parent_name":"SocketEngineSpec"},"Protocols/SocketEngineSpec.html#/s:8SocketIO0A10EngineSpecP13connectParamsSDySSypGSgvp":{"name":"connectParams","abstract":"

The connect parameters sent during a connect.

","parent_name":"SocketEngineSpec"},"Protocols/SocketEngineSpec.html#/s:8SocketIO0A10EngineSpecP7cookiesSaySo12NSHTTPCookieCGSgvp":{"name":"cookies","abstract":"

An array of HTTPCookies that are sent during the connection.

","parent_name":"SocketEngineSpec"},"Protocols/SocketEngineSpec.html#/s:8SocketIO0A10EngineSpecP11engineQueueSo17OS_dispatch_queueCvp":{"name":"engineQueue","abstract":"

The queue that all engine actions take place on.

","parent_name":"SocketEngineSpec"},"Protocols/SocketEngineSpec.html#/s:8SocketIO0A10EngineSpecP12extraHeadersSDyS2SGSgvp":{"name":"extraHeaders","abstract":"

A dictionary of extra http headers that will be set during connection.

","parent_name":"SocketEngineSpec"},"Protocols/SocketEngineSpec.html#/s:8SocketIO0A10EngineSpecP11fastUpgradeSbvp":{"name":"fastUpgrade","abstract":"

When true, the engine is in the process of switching to WebSockets.

","parent_name":"SocketEngineSpec"},"Protocols/SocketEngineSpec.html#/s:8SocketIO0A10EngineSpecP12forcePollingSbvp":{"name":"forcePolling","abstract":"

When true, the engine will only use HTTP long-polling as a transport.

","parent_name":"SocketEngineSpec"},"Protocols/SocketEngineSpec.html#/s:8SocketIO0A10EngineSpecP15forceWebsocketsSbvp":{"name":"forceWebsockets","abstract":"

When true, the engine will only use WebSockets as a transport.

","parent_name":"SocketEngineSpec"},"Protocols/SocketEngineSpec.html#/s:8SocketIO0A10EngineSpecP7pollingSbvp":{"name":"polling","abstract":"

If true, the engine is currently in HTTP long-polling mode.

","parent_name":"SocketEngineSpec"},"Protocols/SocketEngineSpec.html#/s:8SocketIO0A10EngineSpecP7probingSbvp":{"name":"probing","abstract":"

If true, the engine is currently seeing whether it can upgrade to WebSockets.

","parent_name":"SocketEngineSpec"},"Protocols/SocketEngineSpec.html#/s:8SocketIO0A10EngineSpecP3sidSSvp":{"name":"sid","abstract":"

The session id for this engine.

","parent_name":"SocketEngineSpec"},"Protocols/SocketEngineSpec.html#/s:8SocketIO0A10EngineSpecP10socketPathSSvp":{"name":"socketPath","abstract":"

The path to engine.io.

","parent_name":"SocketEngineSpec"},"Protocols/SocketEngineSpec.html#/s:8SocketIO0A10EngineSpecP10urlPolling10Foundation3URLVvp":{"name":"urlPolling","abstract":"

The url for polling.

","parent_name":"SocketEngineSpec"},"Protocols/SocketEngineSpec.html#/s:8SocketIO0A10EngineSpecP06urlWebA010Foundation3URLVvp":{"name":"urlWebSocket","abstract":"

The url for WebSockets.

","parent_name":"SocketEngineSpec"},"Protocols/SocketEngineSpec.html#/s:8SocketIO0A10EngineSpecP7versionAA0A9IOVersionOvp":{"name":"version","abstract":"

The version of engine.io being used. Default is three.

","parent_name":"SocketEngineSpec"},"Protocols/SocketEngineSpec.html#/s:8SocketIO0A10EngineSpecP9websocketSbvp":{"name":"websocket","abstract":"

If true, then the engine is currently in WebSockets mode.

","parent_name":"SocketEngineSpec"},"Protocols/SocketEngineSpec.html#/s:8SocketIO0A10EngineSpecP2ws10Starscream03WebA0CSgvp":{"name":"ws","abstract":"

The WebSocket for this engine.

","parent_name":"SocketEngineSpec"},"Protocols/SocketEngineSpec.html#/s:8SocketIO0A10EngineSpecP6client3url7optionsxAA0aC6Client_p_10Foundation3URLVSDySSypGSgtcfc":{"name":"init(client:url:options:)","abstract":"

Creates a new engine.

","parent_name":"SocketEngineSpec"},"Protocols/SocketEngineSpec.html#/s:8SocketIO0A10EngineSpecP7connectyyF":{"name":"connect()","abstract":"

Starts the connection to the server.

","parent_name":"SocketEngineSpec"},"Protocols/SocketEngineSpec.html#/s:8SocketIO0A10EngineSpecP8didError6reasonySS_tF":{"name":"didError(reason:)","abstract":"

Called when an error happens during execution. Causes a disconnection.

","parent_name":"SocketEngineSpec"},"Protocols/SocketEngineSpec.html#/s:8SocketIO0A10EngineSpecP10disconnect6reasonySS_tF":{"name":"disconnect(reason:)","abstract":"

Disconnects from the server.

","parent_name":"SocketEngineSpec"},"Protocols/SocketEngineSpec.html#/s:8SocketIO0A10EngineSpecP13doFastUpgradeyyF":{"name":"doFastUpgrade()","abstract":"

Called to switch from HTTP long-polling to WebSockets. After calling this method the engine will be in","parent_name":"SocketEngineSpec"},"Protocols/SocketEngineSpec.html#/s:8SocketIO0A10EngineSpecP024flushWaitingForPostToWebA0yyF":{"name":"flushWaitingForPostToWebSocket()","abstract":"

Causes any packets that were waiting for POSTing to be sent through the WebSocket. This happens because when","parent_name":"SocketEngineSpec"},"Protocols/SocketEngineSpec.html#/s:8SocketIO0A10EngineSpecP05parseC4Datayy10Foundation0F0VF":{"name":"parseEngineData(_:)","abstract":"

Parses raw binary received from engine.io.

","parent_name":"SocketEngineSpec"},"Protocols/SocketEngineSpec.html#/s:8SocketIO0A10EngineSpecP05parseC7MessageyySSF":{"name":"parseEngineMessage(_:)","abstract":"

Parses a raw engine.io packet.

","parent_name":"SocketEngineSpec"},"Protocols/SocketEngineSpec.html#/s:8SocketIO0A10EngineSpecP5write_8withType0F4Data10completionySS_AA0ac6PacketG0OSay10Foundation0H0VGyycSgtF":{"name":"write(_:withType:withData:completion:)","abstract":"

Writes a message to engine.io, independent of transport.

","parent_name":"SocketEngineSpec"},"Protocols/SocketEnginePollable.html#/s:8SocketIO0A14EnginePollableP11invalidatedSbvp":{"name":"invalidated","abstract":"

true If engine’s session has been invalidated.

","parent_name":"SocketEnginePollable"},"Protocols/SocketEnginePollable.html#/s:8SocketIO0A14EnginePollableP8postWaitSaySS3msg_yycSg10completiontGvp":{"name":"postWait","abstract":"

A queue of engine.io messages waiting for POSTing

","parent_name":"SocketEnginePollable"},"Protocols/SocketEnginePollable.html#/s:8SocketIO0A14EnginePollableP7sessionSo12NSURLSessionCSgvp":{"name":"session","abstract":"

The URLSession that will be used for polling.

","parent_name":"SocketEnginePollable"},"Protocols/SocketEnginePollable.html#/s:8SocketIO0A14EnginePollableP14waitingForPollSbvp":{"name":"waitingForPoll","abstract":"

true if there is an outstanding poll. Trying to poll before the first is done will cause socket.io to","parent_name":"SocketEnginePollable"},"Protocols/SocketEnginePollable.html#/s:8SocketIO0A14EnginePollableP14waitingForPostSbvp":{"name":"waitingForPost","abstract":"

true if there is an outstanding post. Trying to post before the first is done will cause socket.io to","parent_name":"SocketEnginePollable"},"Protocols/SocketEnginePollable.html#/s:8SocketIO0A14EnginePollableP6doPollyyF":{"name":"doPoll()","abstract":"

Call to send a long-polling request.

","parent_name":"SocketEnginePollable"},"Protocols/SocketEnginePollable.html#/s:8SocketIO0A14EnginePollableP15sendPollMessage_8withType0H4Data10completionySS_AA0ac6PacketI0OSay10Foundation0J0VGyycSgtF":{"name":"sendPollMessage(_:withType:withData:completion:)","abstract":"

Sends an engine.io message through the polling transport.

","parent_name":"SocketEnginePollable"},"Protocols/SocketEnginePollable.html#/s:8SocketIO0A14EnginePollableP11stopPollingyyF":{"name":"stopPolling()","abstract":"

Call to stop polling and invalidate the URLSession.

","parent_name":"SocketEnginePollable"},"Protocols/SocketEngineClient.html#/c:@M@SocketIO@objc(pl)SocketEngineClient(im)engineDidErrorWithReason:":{"name":"engineDidError(reason:)","abstract":"

Called when the engine errors.

","parent_name":"SocketEngineClient"},"Protocols/SocketEngineClient.html#/c:@M@SocketIO@objc(pl)SocketEngineClient(im)engineDidCloseWithReason:":{"name":"engineDidClose(reason:)","abstract":"

Called when the engine closes.

","parent_name":"SocketEngineClient"},"Protocols/SocketEngineClient.html#/c:@M@SocketIO@objc(pl)SocketEngineClient(im)engineDidOpenWithReason:":{"name":"engineDidOpen(reason:)","abstract":"

Called when the engine opens.

","parent_name":"SocketEngineClient"},"Protocols/SocketEngineClient.html#/c:@M@SocketIO@objc(pl)SocketEngineClient(im)engineDidReceivePing":{"name":"engineDidReceivePing()","abstract":"

Called when the engine receives a ping message. Only called in socket.io >3.

","parent_name":"SocketEngineClient"},"Protocols/SocketEngineClient.html#/c:@M@SocketIO@objc(pl)SocketEngineClient(im)engineDidReceivePong":{"name":"engineDidReceivePong()","abstract":"

Called when the engine receives a pong message. Only called in socket.io 2.

","parent_name":"SocketEngineClient"},"Protocols/SocketEngineClient.html#/c:@M@SocketIO@objc(pl)SocketEngineClient(im)engineDidSendPing":{"name":"engineDidSendPing()","abstract":"

Called when the engine sends a ping to the server. Only called in socket.io 2.

","parent_name":"SocketEngineClient"},"Protocols/SocketEngineClient.html#/c:@M@SocketIO@objc(pl)SocketEngineClient(im)engineDidSendPong":{"name":"engineDidSendPong()","abstract":"

Called when the engine sends a pong to the server. Only called in socket.io >3.

","parent_name":"SocketEngineClient"},"Protocols/SocketEngineClient.html#/c:@M@SocketIO@objc(pl)SocketEngineClient(im)parseEngineMessage:":{"name":"parseEngineMessage(_:)","abstract":"

Called when the engine has a message that must be parsed.

","parent_name":"SocketEngineClient"},"Protocols/SocketEngineClient.html#/c:@M@SocketIO@objc(pl)SocketEngineClient(im)parseEngineBinaryData:":{"name":"parseEngineBinaryData(_:)","abstract":"

Called when the engine receives binary data.

","parent_name":"SocketEngineClient"},"Protocols/SocketEngineClient.html#/c:@M@SocketIO@objc(pl)SocketEngineClient(im)engineDidWebsocketUpgradeWithHeaders:":{"name":"engineDidWebsocketUpgrade(headers:)","abstract":"

Called when when upgrading the http connection to a websocket connection.

","parent_name":"SocketEngineClient"},"Protocols/SocketIOClientSpec.html#/s:8SocketIO0A12IOClientSpecP10anyHandleryAA0A8AnyEventCcSgvp":{"name":"anyHandler","abstract":"

A handler that will be called on any event.

","parent_name":"SocketIOClientSpec"},"Protocols/SocketIOClientSpec.html#/s:8SocketIO0A12IOClientSpecP8handlersSayAA0A12EventHandlerVGvp":{"name":"handlers","abstract":"

The array of handlers for this socket.

","parent_name":"SocketIOClientSpec"},"Protocols/SocketIOClientSpec.html#/s:8SocketIO0A12IOClientSpecP7managerAA0a7ManagerD0_pSgvp":{"name":"manager","abstract":"

The manager for this socket.

","parent_name":"SocketIOClientSpec"},"Protocols/SocketIOClientSpec.html#/s:8SocketIO0A12IOClientSpecP3nspSSvp":{"name":"nsp","abstract":"

The namespace that this socket is currently connected to.

","parent_name":"SocketIOClientSpec"},"Protocols/SocketIOClientSpec.html#/s:8SocketIO0A12IOClientSpecP11rawEmitViewAA0a3RawG0Cvp":{"name":"rawEmitView","abstract":"

A view into this socket where emits do not check for binary data.

","parent_name":"SocketIOClientSpec"},"Protocols/SocketIOClientSpec.html#/s:8SocketIO0A12IOClientSpecP3sidSSSgvp":{"name":"sid","abstract":"

The id of this socket.io connect. This is different from the sid of the engine.io connection.

","parent_name":"SocketIOClientSpec"},"Protocols/SocketIOClientSpec.html#/s:8SocketIO0A12IOClientSpecP6statusAA0A8IOStatusOvp":{"name":"status","abstract":"

The status of this client.

","parent_name":"SocketIOClientSpec"},"Protocols/SocketIOClientSpec.html#/s:8SocketIO0A12IOClientSpecP7connect11withPayloadySDySSypGSg_tF":{"name":"connect(withPayload:)","abstract":"

Connect to the server. The same as calling connect(timeoutAfter:withHandler:) with a timeout of 0.

","parent_name":"SocketIOClientSpec"},"Protocols/SocketIOClientSpec.html#/s:8SocketIO0A12IOClientSpecP7connect11withPayload12timeoutAfter0F7HandlerySDySSypGSg_SdyycSgtF":{"name":"connect(withPayload:timeoutAfter:withHandler:)","abstract":"

Connect to the server. If we aren’t connected after timeoutAfter seconds, then withHandler is called.

","parent_name":"SocketIOClientSpec"},"Protocols/SocketIOClientSpec.html#/s:8SocketIO0A12IOClientSpecP10didConnect11toNamespace7payloadySS_SDySSypGSgtF":{"name":"didConnect(toNamespace:payload:)","abstract":"

Called when the client connects to a namespace. If the client was created with a namespace upfront,","parent_name":"SocketIOClientSpec"},"Protocols/SocketIOClientSpec.html#/s:8SocketIO0A12IOClientSpecP13didDisconnect6reasonySS_tF":{"name":"didDisconnect(reason:)","abstract":"

Called when the client has disconnected from socket.io.

","parent_name":"SocketIOClientSpec"},"Protocols/SocketIOClientSpec.html#/s:8SocketIO0A12IOClientSpecP8didError6reasonySS_tF":{"name":"didError(reason:)","abstract":"

Called when the client encounters an error.

","parent_name":"SocketIOClientSpec"},"Protocols/SocketIOClientSpec.html#/s:8SocketIO0A12IOClientSpecP10disconnectyyF":{"name":"disconnect()","abstract":"

Disconnects the socket.

","parent_name":"SocketIOClientSpec"},"Protocols/SocketIOClientSpec.html#/s:8SocketIO0A12IOClientSpecP4emit__10completionySS_AA0A4Data_pdyycSgtF":{"name":"emit(_:_:completion:)","abstract":"

Send an event to the server, with optional data items and optional write completion handler.

","parent_name":"SocketIOClientSpec"},"Protocols/SocketIOClientSpec.html#/s:8SocketIO0A12IOClientSpecP7emitAck_4withySi_SayypGtF":{"name":"emitAck(_:with:)","abstract":"

Call when you wish to tell the server that you’ve received the event for ack.

","parent_name":"SocketIOClientSpec"},"Protocols/SocketIOClientSpec.html#/s:8SocketIO0A12IOClientSpecP11emitWithAckyAA02OnG8CallbackCSS_AA0A4Data_pdtF":{"name":"emitWithAck(_:_:)","abstract":"

Sends a message to the server, requesting an ack.

","parent_name":"SocketIOClientSpec"},"Protocols/SocketIOClientSpec.html#/s:8SocketIO0A12IOClientSpecP9handleAck_4dataySi_SayypGtF":{"name":"handleAck(_:data:)","abstract":"

Called when socket.io has acked one of our emits. Causes the corresponding ack callback to be called.

","parent_name":"SocketIOClientSpec"},"Protocols/SocketIOClientSpec.html#/s:8SocketIO0A12IOClientSpecP17handleClientEvent_4datayAA0afG0O_SayypGtF":{"name":"handleClientEvent(_:data:)","abstract":"

Called on socket.io specific events.

","parent_name":"SocketIOClientSpec"},"Protocols/SocketIOClientSpec.html#/s:8SocketIO0A12IOClientSpecP11handleEvent_4data17isInternalMessage7withAckySS_SayypGSbSitF":{"name":"handleEvent(_:data:isInternalMessage:withAck:)","abstract":"

Called when we get an event from socket.io.

","parent_name":"SocketIOClientSpec"},"Protocols/SocketIOClientSpec.html#/s:8SocketIO0A12IOClientSpecP12handlePacketyyAA0aF0VF":{"name":"handlePacket(_:)","abstract":"

Causes a client to handle a socket.io packet. The namespace for the packet must match the namespace of the","parent_name":"SocketIOClientSpec"},"Protocols/SocketIOClientSpec.html#/s:8SocketIO0A12IOClientSpecP14leaveNamespaceyyF":{"name":"leaveNamespace()","abstract":"

Call when you wish to leave a namespace and disconnect this socket.

","parent_name":"SocketIOClientSpec"},"Protocols/SocketIOClientSpec.html#/s:8SocketIO0A12IOClientSpecP13joinNamespace11withPayloadySDySSypGSg_tF":{"name":"joinNamespace(withPayload:)","abstract":"

Joins nsp. You shouldn’t need to call this directly, instead call connect.

","parent_name":"SocketIOClientSpec"},"Protocols/SocketIOClientSpec.html#/s:8SocketIO0A12IOClientSpecP3off11clientEventyAA0a6ClientG0O_tF":{"name":"off(clientEvent:)","abstract":"

Removes handler(s) for a client event.

","parent_name":"SocketIOClientSpec"},"Protocols/SocketIOClientSpec.html#/s:8SocketIO0A12IOClientSpecP3offyySSF":{"name":"off(_:)","abstract":"

Removes handler(s) based on an event name.

","parent_name":"SocketIOClientSpec"},"Protocols/SocketIOClientSpec.html#/s:8SocketIO0A12IOClientSpecP3off2idy10Foundation4UUIDV_tF":{"name":"off(id:)","abstract":"

Removes a handler with the specified UUID gotten from an on or once

","parent_name":"SocketIOClientSpec"},"Protocols/SocketIOClientSpec.html#/s:8SocketIO0A12IOClientSpecP2on_8callback10Foundation4UUIDVSS_ySayypG_AA0A10AckEmitterCtctF":{"name":"on(_:callback:)","abstract":"

Adds a handler for an event.

","parent_name":"SocketIOClientSpec"},"Protocols/SocketIOClientSpec.html#/s:8SocketIO0A12IOClientSpecP2on11clientEvent8callback10Foundation4UUIDVAA0a6ClientG0O_ySayypG_AA0A10AckEmitterCtctF":{"name":"on(clientEvent:callback:)","abstract":"

Adds a handler for a client event.

","parent_name":"SocketIOClientSpec"},"Protocols/SocketIOClientSpec.html#/s:8SocketIO0A12IOClientSpecP4once11clientEvent8callback10Foundation4UUIDVAA0a6ClientG0O_ySayypG_AA0A10AckEmitterCtctF":{"name":"once(clientEvent:callback:)","abstract":"

Adds a single-use handler for a client event.

","parent_name":"SocketIOClientSpec"},"Protocols/SocketIOClientSpec.html#/s:8SocketIO0A12IOClientSpecP4once_8callback10Foundation4UUIDVSS_ySayypG_AA0A10AckEmitterCtctF":{"name":"once(_:callback:)","abstract":"

Adds a single-use handler for an event.

","parent_name":"SocketIOClientSpec"},"Protocols/SocketIOClientSpec.html#/s:8SocketIO0A12IOClientSpecP5onAnyyyyAA0aF5EventCcF":{"name":"onAny(_:)","abstract":"

Adds a handler that will be called on every event.

","parent_name":"SocketIOClientSpec"},"Protocols/SocketIOClientSpec.html#/s:8SocketIO0A12IOClientSpecP17removeAllHandlersyyF":{"name":"removeAllHandlers()","abstract":"

Removes all handlers.

","parent_name":"SocketIOClientSpec"},"Protocols/SocketIOClientSpec.html#/s:8SocketIO0A12IOClientSpecP15setReconnecting6reasonySS_tF":{"name":"setReconnecting(reason:)","abstract":"

Puts the socket back into the connecting state.","parent_name":"SocketIOClientSpec"},"Protocols/ConfigSettable.html#/s:8SocketIO14ConfigSettableP10setConfigsyyAA0A21IOClientConfigurationVF":{"name":"setConfigs(_:)","abstract":"

Called when an ConfigSettable should set/update its configs from a given configuration.

","parent_name":"ConfigSettable"},"Protocols/ConfigSettable.html":{"name":"ConfigSettable","abstract":"

Declares that a type can set configs from a SocketIOClientConfiguration.

"},"Protocols/SocketIOClientSpec.html":{"name":"SocketIOClientSpec","abstract":"

Defines the interface for a SocketIOClient.

"},"Protocols/SocketEngineClient.html":{"name":"SocketEngineClient","abstract":"

Declares that a type will be a delegate to an engine.

"},"Protocols/SocketEnginePollable.html":{"name":"SocketEnginePollable","abstract":"

Protocol that is used to implement socket.io polling support

"},"Protocols/SocketEngineSpec.html":{"name":"SocketEngineSpec","abstract":"

Specifies a SocketEngine.

"},"Protocols/SocketEngineWebsocket.html":{"name":"SocketEngineWebsocket","abstract":"

Protocol that is used to implement socket.io WebSocket support

"},"Protocols/SocketManagerSpec.html":{"name":"SocketManagerSpec","abstract":"

A manager for a socket.io connection.

"},"Protocols/SocketParsable.html":{"name":"SocketParsable","abstract":"

Defines that a type will be able to parse socket.io-protocol messages.

"},"Protocols/SocketDataBufferable.html":{"name":"SocketDataBufferable","abstract":"

Says that a type will be able to buffer binary data before all data for an event has come in.

"},"Protocols/SocketLogger.html":{"name":"SocketLogger","abstract":"

Represents a class will log client events.

"},"Protocols/SocketData.html":{"name":"SocketData","abstract":"

A marking protocol that says a type can be represented in a socket.io packet.

"},"Extensions.html#/s:Sa":{"name":"Array"},"Extensions.html#/s:Sb":{"name":"Bool"},"Extensions.html#/s:SD":{"name":"Dictionary"},"Extensions.html#/s:Sd":{"name":"Double"},"Extensions.html#/s:Si":{"name":"Int"},"Extensions.html#/c:objc(cs)NSArray":{"name":"NSArray"},"Extensions.html#/s:10Foundation4DataV":{"name":"Data"},"Extensions.html#/c:objc(cs)NSData":{"name":"NSData"},"Extensions.html#/c:objc(cs)NSDictionary":{"name":"NSDictionary"},"Extensions.html#/c:objc(cs)NSString":{"name":"NSString"},"Extensions.html#/c:objc(cs)NSNull":{"name":"NSNull"},"Extensions.html#/s:SS":{"name":"String"},"Enums/SocketParsableError.html#/s:8SocketIO0A13ParsableErrorO16invalidDataArrayyA2CmF":{"name":"invalidDataArray","abstract":"

Thrown when a packet received has an invalid data array, or is missing the data array.

","parent_name":"SocketParsableError"},"Enums/SocketParsableError.html#/s:8SocketIO0A13ParsableErrorO13invalidPacketyA2CmF":{"name":"invalidPacket","abstract":"

Thrown when an malformed packet is received.

","parent_name":"SocketParsableError"},"Enums/SocketParsableError.html#/s:8SocketIO0A13ParsableErrorO17invalidPacketTypeyA2CmF":{"name":"invalidPacketType","abstract":"

Thrown when the parser receives an unknown packet type.

","parent_name":"SocketParsableError"},"Enums/SocketEnginePacketType.html#/c:@M@SocketIO@E@SocketEnginePacketType@SocketEnginePacketTypeOpen":{"name":"open","abstract":"

Open message.

","parent_name":"SocketEnginePacketType"},"Enums/SocketEnginePacketType.html#/c:@M@SocketIO@E@SocketEnginePacketType@SocketEnginePacketTypeClose":{"name":"close","abstract":"

Close message.

","parent_name":"SocketEnginePacketType"},"Enums/SocketEnginePacketType.html#/c:@M@SocketIO@E@SocketEnginePacketType@SocketEnginePacketTypePing":{"name":"ping","abstract":"

Ping message.

","parent_name":"SocketEnginePacketType"},"Enums/SocketEnginePacketType.html#/c:@M@SocketIO@E@SocketEnginePacketType@SocketEnginePacketTypePong":{"name":"pong","abstract":"

Pong message.

","parent_name":"SocketEnginePacketType"},"Enums/SocketEnginePacketType.html#/c:@M@SocketIO@E@SocketEnginePacketType@SocketEnginePacketTypeMessage":{"name":"message","abstract":"

Regular message.

","parent_name":"SocketEnginePacketType"},"Enums/SocketEnginePacketType.html#/c:@M@SocketIO@E@SocketEnginePacketType@SocketEnginePacketTypeUpgrade":{"name":"upgrade","abstract":"

Upgrade message.

","parent_name":"SocketEnginePacketType"},"Enums/SocketEnginePacketType.html#/c:@M@SocketIO@E@SocketEnginePacketType@SocketEnginePacketTypeNoop":{"name":"noop","abstract":"

NOOP.

","parent_name":"SocketEnginePacketType"},"Enums/SocketIOStatus.html#/c:@M@SocketIO@E@SocketIOStatus@SocketIOStatusNotConnected":{"name":"notConnected","abstract":"

The client/manager has never been connected. Or the client has been reset.

","parent_name":"SocketIOStatus"},"Enums/SocketIOStatus.html#/c:@M@SocketIO@E@SocketIOStatus@SocketIOStatusDisconnected":{"name":"disconnected","abstract":"

The client/manager was once connected, but not anymore.

","parent_name":"SocketIOStatus"},"Enums/SocketIOStatus.html#/c:@M@SocketIO@E@SocketIOStatus@SocketIOStatusConnecting":{"name":"connecting","abstract":"

The client/manager is in the process of connecting.

","parent_name":"SocketIOStatus"},"Enums/SocketIOStatus.html#/c:@M@SocketIO@E@SocketIOStatus@SocketIOStatusConnected":{"name":"connected","abstract":"

The client/manager is currently connected.

","parent_name":"SocketIOStatus"},"Enums/SocketIOStatus.html#/s:8SocketIO0A8IOStatusO6activeSbvp":{"name":"active","parent_name":"SocketIOStatus"},"Enums/SocketIOStatus.html#/s:s23CustomStringConvertibleP11descriptionSSvp":{"name":"description","parent_name":"SocketIOStatus"},"Enums/SocketClientEvent.html#/s:8SocketIO0A11ClientEventO7connectyA2CmF":{"name":"connect","abstract":"

Emitted when the client connects. This is also called on a successful reconnection. A connect event gets one","parent_name":"SocketClientEvent"},"Enums/SocketClientEvent.html#/s:8SocketIO0A11ClientEventO10disconnectyA2CmF":{"name":"disconnect","abstract":"

Emitted when the socket has disconnected and will not attempt to try to reconnect.

","parent_name":"SocketClientEvent"},"Enums/SocketClientEvent.html#/s:8SocketIO0A11ClientEventO5erroryA2CmF":{"name":"error","abstract":"

Emitted when an error occurs.

","parent_name":"SocketClientEvent"},"Enums/SocketClientEvent.html#/s:8SocketIO0A11ClientEventO4pingyA2CmF":{"name":"ping","abstract":"

Emitted whenever the engine sends a ping.

","parent_name":"SocketClientEvent"},"Enums/SocketClientEvent.html#/s:8SocketIO0A11ClientEventO4pongyA2CmF":{"name":"pong","abstract":"

Emitted whenever the engine gets a pong.

","parent_name":"SocketClientEvent"},"Enums/SocketClientEvent.html#/s:8SocketIO0A11ClientEventO9reconnectyA2CmF":{"name":"reconnect","abstract":"

Emitted when the client begins the reconnection process.

","parent_name":"SocketClientEvent"},"Enums/SocketClientEvent.html#/s:8SocketIO0A11ClientEventO16reconnectAttemptyA2CmF":{"name":"reconnectAttempt","abstract":"

Emitted each time the client tries to reconnect to the server.

","parent_name":"SocketClientEvent"},"Enums/SocketClientEvent.html#/s:8SocketIO0A11ClientEventO12statusChangeyA2CmF":{"name":"statusChange","abstract":"

Emitted every time there is a change in the client’s status.

","parent_name":"SocketClientEvent"},"Enums/SocketClientEvent.html#/s:8SocketIO0A11ClientEventO16websocketUpgradeyA2CmF":{"name":"websocketUpgrade","abstract":"

Emitted when when upgrading the http connection to a websocket connection.

","parent_name":"SocketClientEvent"},"Enums/SocketIOClientOption.html#/s:8SocketIO0A14IOClientOptionO8compressyA2CmF":{"name":"compress","abstract":"

If given, the WebSocket transport will attempt to use compression.

","parent_name":"SocketIOClientOption"},"Enums/SocketIOClientOption.html#/s:8SocketIO0A14IOClientOptionO13connectParamsyACSDySSypGcACmF":{"name":"connectParams(_:)","abstract":"

A dictionary of GET parameters that will be included in the connect url.

","parent_name":"SocketIOClientOption"},"Enums/SocketIOClientOption.html#/s:8SocketIO0A14IOClientOptionO7cookiesyACSaySo12NSHTTPCookieCGcACmF":{"name":"cookies(_:)","abstract":"

An array of cookies that will be sent during the initial connection.

","parent_name":"SocketIOClientOption"},"Enums/SocketIOClientOption.html#/s:8SocketIO0A14IOClientOptionO12extraHeadersyACSDyS2SGcACmF":{"name":"extraHeaders(_:)","abstract":"

Any extra HTTP headers that should be sent during the initial connection.

","parent_name":"SocketIOClientOption"},"Enums/SocketIOClientOption.html#/s:8SocketIO0A14IOClientOptionO8forceNewyACSbcACmF":{"name":"forceNew(_:)","abstract":"

If passed true, will cause the client to always create a new engine. Useful for debugging,","parent_name":"SocketIOClientOption"},"Enums/SocketIOClientOption.html#/s:8SocketIO0A14IOClientOptionO12forcePollingyACSbcACmF":{"name":"forcePolling(_:)","abstract":"

If passed true, the only transport that will be used will be HTTP long-polling.

","parent_name":"SocketIOClientOption"},"Enums/SocketIOClientOption.html#/s:8SocketIO0A14IOClientOptionO15forceWebsocketsyACSbcACmF":{"name":"forceWebsockets(_:)","abstract":"

If passed true, the only transport that will be used will be WebSockets.

","parent_name":"SocketIOClientOption"},"Enums/SocketIOClientOption.html#/s:8SocketIO0A14IOClientOptionO16enableSOCKSProxyyACSbcACmF":{"name":"enableSOCKSProxy(_:)","abstract":"

If passed true, the WebSocket stream will be configured with the enableSOCKSProxy true.

","parent_name":"SocketIOClientOption"},"Enums/SocketIOClientOption.html#/s:8SocketIO0A14IOClientOptionO11handleQueueyACSo17OS_dispatch_queueCcACmF":{"name":"handleQueue(_:)","abstract":"

The queue that all interaction with the client should occur on. This is the queue that event handlers are","parent_name":"SocketIOClientOption"},"Enums/SocketIOClientOption.html#/s:8SocketIO0A14IOClientOptionO3logyACSbcACmF":{"name":"log(_:)","abstract":"

If passed true, the client will log debug information. This should be turned off in production code.

","parent_name":"SocketIOClientOption"},"Enums/SocketIOClientOption.html#/s:8SocketIO0A14IOClientOptionO6loggeryAcA0A6Logger_pcACmF":{"name":"logger(_:)","abstract":"

Used to pass in a custom logger.

","parent_name":"SocketIOClientOption"},"Enums/SocketIOClientOption.html#/s:8SocketIO0A14IOClientOptionO4pathyACSScACmF":{"name":"path(_:)","abstract":"

A custom path to socket.io. Only use this if the socket.io server is configured to look for this path.

","parent_name":"SocketIOClientOption"},"Enums/SocketIOClientOption.html#/s:8SocketIO0A14IOClientOptionO10reconnectsyACSbcACmF":{"name":"reconnects(_:)","abstract":"

If passed false, the client will not reconnect when it loses connection. Useful if you want full control","parent_name":"SocketIOClientOption"},"Enums/SocketIOClientOption.html#/s:8SocketIO0A14IOClientOptionO17reconnectAttemptsyACSicACmF":{"name":"reconnectAttempts(_:)","abstract":"

The number of times to try and reconnect before giving up. Pass -1 to never give up.

","parent_name":"SocketIOClientOption"},"Enums/SocketIOClientOption.html#/s:8SocketIO0A14IOClientOptionO13reconnectWaityACSicACmF":{"name":"reconnectWait(_:)","abstract":"

The minimum number of seconds to wait before reconnect attempts.

","parent_name":"SocketIOClientOption"},"Enums/SocketIOClientOption.html#/s:8SocketIO0A14IOClientOptionO16reconnectWaitMaxyACSicACmF":{"name":"reconnectWaitMax(_:)","abstract":"

The maximum number of seconds to wait before reconnect attempts.

","parent_name":"SocketIOClientOption"},"Enums/SocketIOClientOption.html#/s:8SocketIO0A14IOClientOptionO19randomizationFactoryACSdcACmF":{"name":"randomizationFactor(_:)","abstract":"

The randomization factor for calculating reconnect jitter.

","parent_name":"SocketIOClientOption"},"Enums/SocketIOClientOption.html#/s:8SocketIO0A14IOClientOptionO6secureyACSbcACmF":{"name":"secure(_:)","abstract":"

Set true if your server is using secure transports.

","parent_name":"SocketIOClientOption"},"Enums/SocketIOClientOption.html#/s:8SocketIO0A14IOClientOptionO8securityyAC10Starscream18CertificatePinning_pcACmF":{"name":"security(_:)","abstract":"

Allows you to set which certs are valid. Useful for SSL pinning.

","parent_name":"SocketIOClientOption"},"Enums/SocketIOClientOption.html#/s:8SocketIO0A14IOClientOptionO10selfSignedyACSbcACmF":{"name":"selfSigned(_:)","abstract":"

If you’re using a self-signed set. Only use for development.

","parent_name":"SocketIOClientOption"},"Enums/SocketIOClientOption.html#/s:8SocketIO0A14IOClientOptionO15sessionDelegateyACSo012NSURLSessionF0_pcACmF":{"name":"sessionDelegate(_:)","abstract":"

Sets an NSURLSessionDelegate for the underlying engine. Useful if you need to handle self-signed certs.

","parent_name":"SocketIOClientOption"},"Enums/SocketIOClientOption.html#/s:8SocketIO0A14IOClientOptionO7versionyAcA0A9IOVersionOcACmF":{"name":"version(_:)","abstract":"

The version of socket.io being used. This should match the server version. Default is 3.

","parent_name":"SocketIOClientOption"},"Enums/SocketIOClientOption.html#/s:8SocketIO0A14IOClientOptionO11descriptionSSvp":{"name":"description","abstract":"

The description of this option.

","parent_name":"SocketIOClientOption"},"Enums/SocketIOClientOption.html#/s:8SocketIO0A14IOClientOptionO2eeoiySbAC_ACtFZ":{"name":"==(_:_:)","abstract":"

Compares whether two options are the same.

","parent_name":"SocketIOClientOption"},"Enums/SocketIOVersion.html#/s:8SocketIO0A9IOVersionO3twoyA2CmF":{"name":"two","abstract":"

socket.io 2, engine.io 3

","parent_name":"SocketIOVersion"},"Enums/SocketIOVersion.html#/s:8SocketIO0A9IOVersionO5threeyA2CmF":{"name":"three","abstract":"

socket.io 3, engine.io 4

","parent_name":"SocketIOVersion"},"Enums/SocketAckStatus.html#/s:8SocketIO0A9AckStatusO02noC0yA2CmF":{"name":"noAck","abstract":"

The ack timed out.

","parent_name":"SocketAckStatus"},"Enums/SocketAckStatus.html#/s:8SocketIO0A9AckStatusO2eeoiySbSS_ACtFZ":{"name":"==(_:_:)","abstract":"

Tests whether a string is equal to a given SocketAckStatus

","parent_name":"SocketAckStatus"},"Enums/SocketAckStatus.html#/s:8SocketIO0A9AckStatusO2eeoiySbAC_SStFZ":{"name":"==(_:_:)","abstract":"

Tests whether a string is equal to a given SocketAckStatus

","parent_name":"SocketAckStatus"},"Enums/SocketAckStatus.html":{"name":"SocketAckStatus","abstract":"

The status of an ack.

"},"Enums/SocketIOVersion.html":{"name":"SocketIOVersion","abstract":"

The socket.io version being used.

"},"Enums/SocketIOClientOption.html":{"name":"SocketIOClientOption","abstract":"

The options for a client.

"},"Enums/SocketClientEvent.html":{"name":"SocketClientEvent","abstract":"

The set of events that are generated by the client.

"},"Enums/SocketIOStatus.html":{"name":"SocketIOStatus","abstract":"

Represents state of a manager or client.

"},"Enums/SocketEnginePacketType.html":{"name":"SocketEnginePacketType","abstract":"

Represents the type of engine.io packet types.

"},"Enums/SocketParsableError.html":{"name":"SocketParsableError","abstract":"

Errors that can be thrown during parsing.

"},"Classes/SocketManager.html#/s:8SocketIO0A7ManagerC07defaultA0AA0A8IOClientCvp":{"name":"defaultSocket","abstract":"

The socket associated with the default namespace (“/”).

","parent_name":"SocketManager"},"Classes/SocketManager.html#/s:8SocketIO0A7ManagerC9socketURL10Foundation0E0Vvp":{"name":"socketURL","abstract":"

The URL of the socket.io server.

","parent_name":"SocketManager"},"Classes/SocketManager.html#/s:8SocketIO0A7ManagerC6configAA0A21IOClientConfigurationVvp":{"name":"config","abstract":"

The configuration for this client.

","parent_name":"SocketManager"},"Classes/SocketManager.html#/s:8SocketIO0A7ManagerC6engineAA0A10EngineSpec_pSgvp":{"name":"engine","abstract":"

The engine for this manager.

","parent_name":"SocketManager"},"Classes/SocketManager.html#/s:8SocketIO0A7ManagerC8forceNewSbvp":{"name":"forceNew","abstract":"

If true then every time connect is called, a new engine will be created.

","parent_name":"SocketManager"},"Classes/SocketManager.html#/s:8SocketIO0A7ManagerC11handleQueueSo17OS_dispatch_queueCvp":{"name":"handleQueue","abstract":"

The queue that all interaction with the client should occur on. This is the queue that event handlers are","parent_name":"SocketManager"},"Classes/SocketManager.html#/s:8SocketIO0A7ManagerC4nspsSDySSAA0A8IOClientCGvp":{"name":"nsps","abstract":"

The sockets in this manager indexed by namespace.

","parent_name":"SocketManager"},"Classes/SocketManager.html#/s:8SocketIO0A7ManagerC10reconnectsSbvp":{"name":"reconnects","abstract":"

If true, this client will try and reconnect on any disconnects.

","parent_name":"SocketManager"},"Classes/SocketManager.html#/s:8SocketIO0A7ManagerC13reconnectWaitSivp":{"name":"reconnectWait","abstract":"

The minimum number of seconds to wait before attempting to reconnect.

","parent_name":"SocketManager"},"Classes/SocketManager.html#/s:8SocketIO0A7ManagerC16reconnectWaitMaxSivp":{"name":"reconnectWaitMax","abstract":"

The maximum number of seconds to wait before attempting to reconnect.

","parent_name":"SocketManager"},"Classes/SocketManager.html#/s:8SocketIO0A7ManagerC19randomizationFactorSdvp":{"name":"randomizationFactor","abstract":"

The randomization factor for calculating reconnect jitter.

","parent_name":"SocketManager"},"Classes/SocketManager.html#/s:8SocketIO0A7ManagerC6statusAA0A8IOStatusOvp":{"name":"status","abstract":"

The status of this manager.

","parent_name":"SocketManager"},"Classes/SocketManager.html#/s:8SocketIO0A11ManagerSpecP7versionAA0A9IOVersionOvp":{"name":"version","parent_name":"SocketManager"},"Classes/SocketManager.html#/s:8SocketIO0A7ManagerC14waitingPacketsSayAA0A6PacketVGvp":{"name":"waitingPackets","abstract":"

A list of packets that are waiting for binary data.

","parent_name":"SocketManager"},"Classes/SocketManager.html#/s:8SocketIO0A7ManagerC9socketURL6configAC10Foundation0E0V_AA0A21IOClientConfigurationVtcfc":{"name":"init(socketURL:config:)","abstract":"

Type safe way to create a new SocketIOClient. opts can be omitted.

","parent_name":"SocketManager"},"Classes/SocketManager.html#/c:@M@SocketIO@objc(cs)SocketManager(im)initWithSocketURL:config:":{"name":"init(socketURL:config:)","abstract":"

Not so type safe way to create a SocketIOClient, meant for Objective-C compatiblity.","parent_name":"SocketManager"},"Classes/SocketManager.html#/s:8SocketIO0A7ManagerC7connectyyF":{"name":"connect()","abstract":"

Connects the underlying transport and the default namespace socket.

","parent_name":"SocketManager"},"Classes/SocketManager.html#/s:8SocketIO0A7ManagerC07connectA0_11withPayloadyAA0A8IOClientC_SDySSypGSgtF":{"name":"connectSocket(_:withPayload:)","abstract":"

Connects a socket through this manager’s engine.

","parent_name":"SocketManager"},"Classes/SocketManager.html#/s:8SocketIO0A7ManagerC13didDisconnect6reasonySS_tF":{"name":"didDisconnect(reason:)","abstract":"

Called when the manager has disconnected from socket.io.

","parent_name":"SocketManager"},"Classes/SocketManager.html#/s:8SocketIO0A7ManagerC10disconnectyyF":{"name":"disconnect()","abstract":"

Disconnects the manager and all associated sockets.

","parent_name":"SocketManager"},"Classes/SocketManager.html#/s:8SocketIO0A7ManagerC010disconnectA0yyAA0A8IOClientCF":{"name":"disconnectSocket(_:)","abstract":"

Disconnects the given socket.

","parent_name":"SocketManager"},"Classes/SocketManager.html#/s:8SocketIO0A7ManagerC010disconnectA012forNamespaceySS_tF":{"name":"disconnectSocket(forNamespace:)","abstract":"

Disconnects the socket associated with forNamespace.

","parent_name":"SocketManager"},"Classes/SocketManager.html#/s:8SocketIO0A7ManagerC7emitAll11clientEvent4datayAA0a6ClientG0O_SayypGtF":{"name":"emitAll(clientEvent:data:)","abstract":"

Sends a client event to all sockets in nsps

","parent_name":"SocketManager"},"Classes/SocketManager.html#/s:8SocketIO0A7ManagerC7emitAllyySS_AA0A4Data_pdtF":{"name":"emitAll(_:_:)","abstract":"

Sends an event to the server on all namespaces in this manager.

","parent_name":"SocketManager"},"Classes/SocketManager.html#/c:@M@SocketIO@objc(cs)SocketManager(im)engineDidCloseWithReason:":{"name":"engineDidClose(reason:)","abstract":"

Called when the engine closes.

","parent_name":"SocketManager"},"Classes/SocketManager.html#/c:@M@SocketIO@objc(cs)SocketManager(im)engineDidErrorWithReason:":{"name":"engineDidError(reason:)","abstract":"

Called when the engine errors.

","parent_name":"SocketManager"},"Classes/SocketManager.html#/c:@M@SocketIO@objc(cs)SocketManager(im)engineDidOpenWithReason:":{"name":"engineDidOpen(reason:)","abstract":"

Called when the engine opens.

","parent_name":"SocketManager"},"Classes/SocketManager.html#/c:@M@SocketIO@objc(cs)SocketManager(im)engineDidReceivePing":{"name":"engineDidReceivePing()","abstract":"

Called when the engine receives a ping message.

","parent_name":"SocketManager"},"Classes/SocketManager.html#/c:@M@SocketIO@objc(cs)SocketManager(im)engineDidSendPing":{"name":"engineDidSendPing()","abstract":"

Called when the sends a ping to the server.

","parent_name":"SocketManager"},"Classes/SocketManager.html#/c:@M@SocketIO@objc(cs)SocketManager(im)engineDidReceivePong":{"name":"engineDidReceivePong()","abstract":"

Called when the engine receives a pong message.

","parent_name":"SocketManager"},"Classes/SocketManager.html#/c:@M@SocketIO@objc(cs)SocketManager(im)engineDidSendPong":{"name":"engineDidSendPong()","abstract":"

Called when the sends a pong to the server.

","parent_name":"SocketManager"},"Classes/SocketManager.html#/c:@M@SocketIO@objc(cs)SocketManager(im)engineDidWebsocketUpgradeWithHeaders:":{"name":"engineDidWebsocketUpgrade(headers:)","abstract":"

Called when when upgrading the http connection to a websocket connection.

","parent_name":"SocketManager"},"Classes/SocketManager.html#/c:@M@SocketIO@objc(cs)SocketManager(im)parseEngineMessage:":{"name":"parseEngineMessage(_:)","abstract":"

Called when the engine has a message that must be parsed.

","parent_name":"SocketManager"},"Classes/SocketManager.html#/c:@M@SocketIO@objc(cs)SocketManager(im)parseEngineBinaryData:":{"name":"parseEngineBinaryData(_:)","abstract":"

Called when the engine receives binary data.

","parent_name":"SocketManager"},"Classes/SocketManager.html#/s:8SocketIO0A7ManagerC9reconnectyyF":{"name":"reconnect()","abstract":"

Tries to reconnect to the server.

","parent_name":"SocketManager"},"Classes/SocketManager.html#/s:8SocketIO0A7ManagerC06removeA0yAA0A8IOClientCSgAFF":{"name":"removeSocket(_:)","abstract":"

Removes the socket from the manager’s control. One of the disconnect methods should be called before calling this","parent_name":"SocketManager"},"Classes/SocketManager.html#/s:8SocketIO0A7ManagerC10setConfigsyyAA0A21IOClientConfigurationVF":{"name":"setConfigs(_:)","abstract":"

Sets manager specific configs.

","parent_name":"SocketManager"},"Classes/SocketManager.html#/s:8SocketIO0A7ManagerC6socket12forNamespaceAA0A8IOClientCSS_tF":{"name":"socket(forNamespace:)","abstract":"

Returns a SocketIOClient for the given namespace. This socket shares a transport with the manager.

","parent_name":"SocketManager"},"Classes/SocketEngine.html#/s:8SocketIO0A6EngineC11engineQueueSo17OS_dispatch_queueCvp":{"name":"engineQueue","abstract":"

The queue that all engine actions take place on.

","parent_name":"SocketEngine"},"Classes/SocketEngine.html#/s:8SocketIO0A6EngineC13connectParamsSDySSypGSgvp":{"name":"connectParams","abstract":"

The connect parameters sent during a connect.

","parent_name":"SocketEngine"},"Classes/SocketEngine.html#/s:8SocketIO0A6EngineC12extraHeadersSDyS2SGSgvp":{"name":"extraHeaders","abstract":"

A dictionary of extra http headers that will be set during connection.

","parent_name":"SocketEngine"},"Classes/SocketEngine.html#/s:8SocketIO0A6EngineC8postWaitSaySS3msg_yycSg10completiontGvp":{"name":"postWait","abstract":"

A queue of engine.io messages waiting for POSTing

","parent_name":"SocketEngine"},"Classes/SocketEngine.html#/s:8SocketIO0A6EngineC14waitingForPollSbvp":{"name":"waitingForPoll","abstract":"

true if there is an outstanding poll. Trying to poll before the first is done will cause socket.io to","parent_name":"SocketEngine"},"Classes/SocketEngine.html#/s:8SocketIO0A6EngineC14waitingForPostSbvp":{"name":"waitingForPost","abstract":"

true if there is an outstanding post. Trying to post before the first is done will cause socket.io to","parent_name":"SocketEngine"},"Classes/SocketEngine.html#/s:8SocketIO0A6EngineC6closedSbvp":{"name":"closed","abstract":"

true if this engine is closed.

","parent_name":"SocketEngine"},"Classes/SocketEngine.html#/s:8SocketIO0A6EngineC8compressSbvp":{"name":"compress","abstract":"

If true the engine will attempt to use WebSocket compression.

","parent_name":"SocketEngine"},"Classes/SocketEngine.html#/s:8SocketIO0A6EngineC9connectedSbvp":{"name":"connected","abstract":"

true if this engine is connected. Connected means that the initial poll connect has succeeded.

","parent_name":"SocketEngine"},"Classes/SocketEngine.html#/s:8SocketIO0A6EngineC7cookiesSaySo12NSHTTPCookieCGSgvp":{"name":"cookies","abstract":"

An array of HTTPCookies that are sent during the connection.

","parent_name":"SocketEngine"},"Classes/SocketEngine.html#/s:8SocketIO0A6EngineC11fastUpgradeSbvp":{"name":"fastUpgrade","abstract":"

When true, the engine is in the process of switching to WebSockets.

","parent_name":"SocketEngine"},"Classes/SocketEngine.html#/s:8SocketIO0A6EngineC12forcePollingSbvp":{"name":"forcePolling","abstract":"

When true, the engine will only use HTTP long-polling as a transport.

","parent_name":"SocketEngine"},"Classes/SocketEngine.html#/s:8SocketIO0A6EngineC15forceWebsocketsSbvp":{"name":"forceWebsockets","abstract":"

When true, the engine will only use WebSockets as a transport.

","parent_name":"SocketEngine"},"Classes/SocketEngine.html#/s:8SocketIO0A6EngineC11invalidatedSbvp":{"name":"invalidated","abstract":"

true If engine’s session has been invalidated.

","parent_name":"SocketEngine"},"Classes/SocketEngine.html#/s:8SocketIO0A6EngineC7pollingSbvp":{"name":"polling","abstract":"

If true, the engine is currently in HTTP long-polling mode.

","parent_name":"SocketEngine"},"Classes/SocketEngine.html#/s:8SocketIO0A6EngineC7probingSbvp":{"name":"probing","abstract":"

If true, the engine is currently seeing whether it can upgrade to WebSockets.

","parent_name":"SocketEngine"},"Classes/SocketEngine.html#/s:8SocketIO0A6EngineC7sessionSo12NSURLSessionCSgvp":{"name":"session","abstract":"

The URLSession that will be used for polling.

","parent_name":"SocketEngine"},"Classes/SocketEngine.html#/s:8SocketIO0A6EngineC3sidSSvp":{"name":"sid","abstract":"

The session id for this engine.

","parent_name":"SocketEngine"},"Classes/SocketEngine.html#/s:8SocketIO0A6EngineC10socketPathSSvp":{"name":"socketPath","abstract":"

The path to engine.io.

","parent_name":"SocketEngine"},"Classes/SocketEngine.html#/s:8SocketIO0A6EngineC10urlPolling10Foundation3URLVvp":{"name":"urlPolling","abstract":"

The url for polling.

","parent_name":"SocketEngine"},"Classes/SocketEngine.html#/s:8SocketIO0A6EngineC06urlWebA010Foundation3URLVvp":{"name":"urlWebSocket","abstract":"

The url for WebSockets.

","parent_name":"SocketEngine"},"Classes/SocketEngine.html#/s:8SocketIO0A6EngineC7versionAA0A9IOVersionOvp":{"name":"version","abstract":"

The version of engine.io being used. Default is three.

","parent_name":"SocketEngine"},"Classes/SocketEngine.html#/s:8SocketIO0A6EngineC9websocketSbvp":{"name":"websocket","abstract":"

If true, then the engine is currently in WebSockets mode.

","parent_name":"SocketEngine"},"Classes/SocketEngine.html#/s:8SocketIO0A6EngineC16enableSOCKSProxySbvp":{"name":"enableSOCKSProxy","abstract":"

When true, the WebSocket stream will be configured with the enableSOCKSProxy true.

","parent_name":"SocketEngine"},"Classes/SocketEngine.html#/s:8SocketIO0A6EngineC2ws10Starscream03WebA0CSgvp":{"name":"ws","abstract":"

The WebSocket for this engine.

","parent_name":"SocketEngine"},"Classes/SocketEngine.html#/s:8SocketIO0A6EngineC11wsConnectedSbvp":{"name":"wsConnected","abstract":"

Whether or not the WebSocket is currently connected.

","parent_name":"SocketEngine"},"Classes/SocketEngine.html#/s:8SocketIO0A6EngineC6clientAA0aC6Client_pSgvp":{"name":"client","abstract":"

The client for this engine.

","parent_name":"SocketEngine"},"Classes/SocketEngine.html#/s:8SocketIO0A6EngineC6client3url6configAcA0aC6Client_p_10Foundation3URLVAA0A21IOClientConfigurationVtcfc":{"name":"init(client:url:config:)","abstract":"

Creates a new engine.

","parent_name":"SocketEngine"},"Classes/SocketEngine.html#/s:8SocketIO0A6EngineC6client3url7optionsAcA0aC6Client_p_10Foundation3URLVSDySSypGSgtcfc":{"name":"init(client:url:options:)","abstract":"

Creates a new engine.

","parent_name":"SocketEngine"},"Classes/SocketEngine.html#/s:8SocketIO0A6EngineC7connectyyF":{"name":"connect()","abstract":"

Starts the connection to the server.

","parent_name":"SocketEngine"},"Classes/SocketEngine.html#/s:8SocketIO0A6EngineC8didError6reasonySS_tF":{"name":"didError(reason:)","abstract":"

Called when an error happens during execution. Causes a disconnection.

","parent_name":"SocketEngine"},"Classes/SocketEngine.html#/s:8SocketIO0A6EngineC10disconnect6reasonySS_tF":{"name":"disconnect(reason:)","abstract":"

Disconnects from the server.

","parent_name":"SocketEngine"},"Classes/SocketEngine.html#/s:8SocketIO0A6EngineC13doFastUpgradeyyF":{"name":"doFastUpgrade()","abstract":"

Called to switch from HTTP long-polling to WebSockets. After calling this method the engine will be in","parent_name":"SocketEngine"},"Classes/SocketEngine.html#/s:8SocketIO0A6EngineC024flushWaitingForPostToWebA0yyF":{"name":"flushWaitingForPostToWebSocket()","abstract":"

Causes any packets that were waiting for POSTing to be sent through the WebSocket. This happens because when","parent_name":"SocketEngine"},"Classes/SocketEngine.html#/s:8SocketIO0A6EngineC05parseC4Datayy10Foundation0E0VF":{"name":"parseEngineData(_:)","abstract":"

Parses raw binary received from engine.io.

","parent_name":"SocketEngine"},"Classes/SocketEngine.html#/s:8SocketIO0A6EngineC05parseC7MessageyySSF":{"name":"parseEngineMessage(_:)","abstract":"

Parses a raw engine.io packet.

","parent_name":"SocketEngine"},"Classes/SocketEngine.html#/s:8SocketIO0A6EngineC10setConfigsyyAA0A21IOClientConfigurationVF":{"name":"setConfigs(_:)","abstract":"

Called when the engine should set/update its configs from a given configuration.

","parent_name":"SocketEngine"},"Classes/SocketEngine.html#/s:8SocketIO0A6EngineC5write_8withType0E4Data10completionySS_AA0ac6PacketF0OSay10Foundation0G0VGyycSgtF":{"name":"write(_:withType:withData:completion:)","abstract":"

Writes a message to engine.io, independent of transport.

","parent_name":"SocketEngine"},"Classes/SocketEngine.html#/s:8SocketIO0A6EngineC10URLSession7session25didBecomeInvalidWithErrorySo12NSURLSessionC_So7NSErrorCSgtF":{"name":"URLSession(session:didBecomeInvalidWithError:)","abstract":"

Delegate called when the session becomes invalid.

","parent_name":"SocketEngine"},"Classes/SocketEngine.html#/s:8SocketIO0A6EngineC10didReceive5event6clienty10Starscream03WebA5EventO_AG0iA0CtF":{"name":"didReceive(event:client:)","abstract":"

Delegate method for WebSocketDelegate.

","parent_name":"SocketEngine"},"Classes/SocketRawAckView.html#/s:8SocketIO0A10RawAckViewC4withyyAA0A4Data_pd_tF":{"name":"with(_:)","abstract":"

Call to ack receiving this event.

","parent_name":"SocketRawAckView"},"Classes/SocketRawAckView.html#/c:@M@SocketIO@objc(cs)SocketRawAckView(im)with:":{"name":"with(_:)","abstract":"

Call to ack receiving this event.

","parent_name":"SocketRawAckView"},"Classes/SocketRawView.html#/s:8SocketIO0A7RawViewC4emityySS_AA0A4Data_pdtF":{"name":"emit(_:_:)","abstract":"

Send an event to the server, with optional data items.

","parent_name":"SocketRawView"},"Classes/SocketRawView.html#/c:@M@SocketIO@objc(cs)SocketRawView(im)emit:with:":{"name":"emit(_:with:)","abstract":"

Same as emit, but meant for Objective-C

","parent_name":"SocketRawView"},"Classes/SocketRawView.html#/s:8SocketIO0A7RawViewC11emitWithAckyAA02OnG8CallbackCSS_AA0A4Data_pdtF":{"name":"emitWithAck(_:_:)","abstract":"

Sends a message to the server, requesting an ack.

","parent_name":"SocketRawView"},"Classes/SocketRawView.html#/c:@M@SocketIO@objc(cs)SocketRawView(im)emitWithAck:with:":{"name":"emitWithAck(_:with:)","abstract":"

Same as emitWithAck, but for Objective-C

","parent_name":"SocketRawView"},"Classes/SocketIOClient.html#/s:8SocketIO0A8IOClientC3nspSSvp":{"name":"nsp","abstract":"

The namespace that this socket is currently connected to.

","parent_name":"SocketIOClient"},"Classes/SocketIOClient.html#/s:8SocketIO0A8IOClientC10anyHandleryAA0A8AnyEventCcSgvp":{"name":"anyHandler","abstract":"

A handler that will be called on any event.

","parent_name":"SocketIOClient"},"Classes/SocketIOClient.html#/s:8SocketIO0A8IOClientC8handlersSayAA0A12EventHandlerVGvp":{"name":"handlers","abstract":"

The array of handlers for this socket.

","parent_name":"SocketIOClient"},"Classes/SocketIOClient.html#/s:8SocketIO0A8IOClientC7managerAA0A11ManagerSpec_pSgvp":{"name":"manager","abstract":"

The manager for this socket.

","parent_name":"SocketIOClient"},"Classes/SocketIOClient.html#/s:8SocketIO0A8IOClientC11rawEmitViewAA0a3RawF0Cvp":{"name":"rawEmitView","abstract":"

A view into this socket where emits do not check for binary data.

","parent_name":"SocketIOClient"},"Classes/SocketIOClient.html#/s:8SocketIO0A8IOClientC6statusAA0A8IOStatusOvp":{"name":"status","abstract":"

The status of this client.

","parent_name":"SocketIOClient"},"Classes/SocketIOClient.html#/s:8SocketIO0A8IOClientC3sidSSSgvp":{"name":"sid","abstract":"

The id of this socket.io connect. This is different from the sid of the engine.io connection.

","parent_name":"SocketIOClient"},"Classes/SocketIOClient.html#/s:8SocketIO0A8IOClientC7manager3nspAcA0A11ManagerSpec_p_SStcfc":{"name":"init(manager:nsp:)","abstract":"

Type safe way to create a new SocketIOClient. opts can be omitted.

","parent_name":"SocketIOClient"},"Classes/SocketIOClient.html#/s:8SocketIO0A8IOClientC7connect11withPayloadySDySSypGSg_tF":{"name":"connect(withPayload:)","abstract":"

Connect to the server. The same as calling connect(timeoutAfter:withHandler:) with a timeout of 0.

","parent_name":"SocketIOClient"},"Classes/SocketIOClient.html#/s:8SocketIO0A8IOClientC7connect11withPayload12timeoutAfter0E7HandlerySDySSypGSg_SdyycSgtF":{"name":"connect(withPayload:timeoutAfter:withHandler:)","abstract":"

Connect to the server. If we aren’t connected after timeoutAfter seconds, then withHandler is called.

","parent_name":"SocketIOClient"},"Classes/SocketIOClient.html#/s:8SocketIO0A8IOClientC10didConnect11toNamespace7payloadySS_SDySSypGSgtF":{"name":"didConnect(toNamespace:payload:)","abstract":"

Called when the client connects to a namespace. If the client was created with a namespace upfront,","parent_name":"SocketIOClient"},"Classes/SocketIOClient.html#/s:8SocketIO0A8IOClientC13didDisconnect6reasonySS_tF":{"name":"didDisconnect(reason:)","abstract":"

Called when the client has disconnected from socket.io.

","parent_name":"SocketIOClient"},"Classes/SocketIOClient.html#/s:8SocketIO0A8IOClientC10disconnectyyF":{"name":"disconnect()","abstract":"

Disconnects the socket.

","parent_name":"SocketIOClient"},"Classes/SocketIOClient.html#/s:8SocketIO0A8IOClientC4emit__10completionySS_AA0A4Data_pdyycSgtF":{"name":"emit(_:_:completion:)","abstract":"

Send an event to the server, with optional data items and optional write completion handler.

","parent_name":"SocketIOClient"},"Classes/SocketIOClient.html#/s:8SocketIO0A8IOClientC11emitWithAckyAA02OnF8CallbackCSS_AA0A4Data_pdtF":{"name":"emitWithAck(_:_:)","abstract":"

Sends a message to the server, requesting an ack.

","parent_name":"SocketIOClient"},"Classes/SocketIOClient.html#/s:8SocketIO0A8IOClientC7emitAck_4withySi_SayypGtF":{"name":"emitAck(_:with:)","abstract":"

Call when you wish to tell the server that you’ve received the event for ack.

","parent_name":"SocketIOClient"},"Classes/SocketIOClient.html#/s:8SocketIO0A8IOClientC9handleAck_4dataySi_SayypGtF":{"name":"handleAck(_:data:)","abstract":"

Called when socket.io has acked one of our emits. Causes the corresponding ack callback to be called.

","parent_name":"SocketIOClient"},"Classes/SocketIOClient.html#/s:8SocketIO0A8IOClientC17handleClientEvent_4datayAA0aeF0O_SayypGtF":{"name":"handleClientEvent(_:data:)","abstract":"

Called on socket.io specific events.

","parent_name":"SocketIOClient"},"Classes/SocketIOClient.html#/s:8SocketIO0A8IOClientC11handleEvent_4data17isInternalMessage7withAckySS_SayypGSbSitF":{"name":"handleEvent(_:data:isInternalMessage:withAck:)","abstract":"

Called when we get an event from socket.io.

","parent_name":"SocketIOClient"},"Classes/SocketIOClient.html#/s:8SocketIO0A8IOClientC12handlePacketyyAA0aE0VF":{"name":"handlePacket(_:)","abstract":"

Causes a client to handle a socket.io packet. The namespace for the packet must match the namespace of the","parent_name":"SocketIOClient"},"Classes/SocketIOClient.html#/s:8SocketIO0A8IOClientC14leaveNamespaceyyF":{"name":"leaveNamespace()","abstract":"

Call when you wish to leave a namespace and disconnect this socket.

","parent_name":"SocketIOClient"},"Classes/SocketIOClient.html#/s:8SocketIO0A8IOClientC13joinNamespace11withPayloadySDySSypGSg_tF":{"name":"joinNamespace(withPayload:)","abstract":"

Joins nsp. You shouldn’t need to call this directly, instead call connect.

","parent_name":"SocketIOClient"},"Classes/SocketIOClient.html#/s:8SocketIO0A8IOClientC3off11clientEventyAA0a6ClientF0O_tF":{"name":"off(clientEvent:)","abstract":"

Removes handler(s) for a client event.

","parent_name":"SocketIOClient"},"Classes/SocketIOClient.html#/s:8SocketIO0A8IOClientC3offyySSF":{"name":"off(_:)","abstract":"

Removes handler(s) based on an event name.

","parent_name":"SocketIOClient"},"Classes/SocketIOClient.html#/s:8SocketIO0A8IOClientC3off2idy10Foundation4UUIDV_tF":{"name":"off(id:)","abstract":"

Removes a handler with the specified UUID gotten from an on or once

","parent_name":"SocketIOClient"},"Classes/SocketIOClient.html#/s:8SocketIO0A8IOClientC2on_8callback10Foundation4UUIDVSS_ySayypG_AA0A10AckEmitterCtctF":{"name":"on(_:callback:)","abstract":"

Adds a handler for an event.

","parent_name":"SocketIOClient"},"Classes/SocketIOClient.html#/s:8SocketIO0A8IOClientC2on11clientEvent8callback10Foundation4UUIDVAA0a6ClientF0O_ySayypG_AA0A10AckEmitterCtctF":{"name":"on(clientEvent:callback:)","abstract":"

Adds a handler for a client event.

","parent_name":"SocketIOClient"},"Classes/SocketIOClient.html#/s:8SocketIO0A8IOClientC4once11clientEvent8callback10Foundation4UUIDVAA0a6ClientF0O_ySayypG_AA0A10AckEmitterCtctF":{"name":"once(clientEvent:callback:)","abstract":"

Adds a single-use handler for a client event.

","parent_name":"SocketIOClient"},"Classes/SocketIOClient.html#/s:8SocketIO0A8IOClientC4once_8callback10Foundation4UUIDVSS_ySayypG_AA0A10AckEmitterCtctF":{"name":"once(_:callback:)","abstract":"

Adds a single-use handler for an event.

","parent_name":"SocketIOClient"},"Classes/SocketIOClient.html#/s:8SocketIO0A8IOClientC5onAnyyyyAA0aE5EventCcF":{"name":"onAny(_:)","abstract":"

Adds a handler that will be called on every event.

","parent_name":"SocketIOClient"},"Classes/SocketIOClient.html#/s:8SocketIO0A8IOClientC17removeAllHandlersyyF":{"name":"removeAllHandlers()","abstract":"

Removes all handlers.

","parent_name":"SocketIOClient"},"Classes/SocketIOClient.html#/s:8SocketIO0A8IOClientC15setReconnecting6reasonySS_tF":{"name":"setReconnecting(reason:)","abstract":"

Puts the socket back into the connecting state.","parent_name":"SocketIOClient"},"Classes/SocketAnyEvent.html#/c:@M@SocketIO@objc(cs)SocketAnyEvent(py)event":{"name":"event","abstract":"

The event name.

","parent_name":"SocketAnyEvent"},"Classes/SocketAnyEvent.html#/c:@M@SocketIO@objc(cs)SocketAnyEvent(py)items":{"name":"items","abstract":"

The data items for this event.

","parent_name":"SocketAnyEvent"},"Classes/SocketAnyEvent.html#/c:@M@SocketIO@objc(cs)SocketAnyEvent(py)description":{"name":"description","abstract":"

The description of this event.

","parent_name":"SocketAnyEvent"},"Classes/OnAckCallback.html#/c:@M@SocketIO@objc(cs)OnAckCallback(im)timingOutAfter:callback:":{"name":"timingOut(after:callback:)","abstract":"

Completes an emitWithAck. If this isn’t called, the emit never happens.

","parent_name":"OnAckCallback"},"Classes/SocketAckEmitter.html#/c:@M@SocketIO@objc(cs)SocketAckEmitter(py)rawEmitView":{"name":"rawEmitView","abstract":"

A view into this emitter where emits do not check for binary data.

","parent_name":"SocketAckEmitter"},"Classes/SocketAckEmitter.html#/s:8SocketIO0A10AckEmitterC8expectedSbvp":{"name":"expected","abstract":"

If true, this handler is expecting to be acked. Call with(_: SocketData...) to ack.

","parent_name":"SocketAckEmitter"},"Classes/SocketAckEmitter.html#/s:8SocketIO0A10AckEmitterC6socket6ackNumAcA0A8IOClientC_Sitcfc":{"name":"init(socket:ackNum:)","abstract":"

Creates a new SocketAckEmitter.

","parent_name":"SocketAckEmitter"},"Classes/SocketAckEmitter.html#/s:8SocketIO0A10AckEmitterC4withyyAA0A4Data_pd_tF":{"name":"with(_:)","abstract":"

Call to ack receiving this event.

","parent_name":"SocketAckEmitter"},"Classes/SocketAckEmitter.html#/c:@M@SocketIO@objc(cs)SocketAckEmitter(im)with:":{"name":"with(_:)","abstract":"

Call to ack receiving this event.

","parent_name":"SocketAckEmitter"},"Classes/SocketAckEmitter.html":{"name":"SocketAckEmitter","abstract":"

A class that represents a waiting ack call.

"},"Classes/OnAckCallback.html":{"name":"OnAckCallback","abstract":"

A class that represents an emit that will request an ack that has not yet been sent."},"Classes/SocketAnyEvent.html":{"name":"SocketAnyEvent","abstract":"

Represents some event that was received.

"},"Classes/SocketIOClient.html":{"name":"SocketIOClient","abstract":"

Represents a socket.io-client.

"},"Classes/SocketRawView.html":{"name":"SocketRawView","abstract":"

Class that gives a backwards compatible way to cause an emit not to recursively check for Data objects.

"},"Classes/SocketRawAckView.html":{"name":"SocketRawAckView","abstract":"

Class that gives a backwards compatible way to cause an emit not to recursively check for Data objects.

"},"Classes/SocketEngine.html":{"name":"SocketEngine","abstract":"

The class that handles the engine.io protocol and transports."},"Classes/SocketManager.html":{"name":"SocketManager","abstract":"

A manager for a socket.io connection.

"},"15to16.html":{"name":"15to16"},"faq.html":{"name":"FAQ"},"12to13.html":{"name":"12to13"},"Guides.html":{"name":"Guides","abstract":"

The following guides are available globally.

"},"Classes.html":{"name":"Classes","abstract":"

The following classes are available globally.

"},"Enums.html":{"name":"Enumerations","abstract":"

The following enumerations are available globally.

"},"Extensions.html":{"name":"Extensions","abstract":"

The following extensions are available globally.

"},"Protocols.html":{"name":"Protocols","abstract":"

The following protocols are available globally.

"},"Structs.html":{"name":"Structures","abstract":"

The following structures are available globally.

"},"Typealiases.html":{"name":"Type Aliases","abstract":"

The following type aliases are available globally.

"}}