├── .gitignore ├── .swift-version ├── ACKNOWLEDGEMENTS.md ├── LICENSE.md ├── Podfile ├── Podfile.lock ├── README.md ├── SignalR-Swift-Example ├── Podfile ├── Podfile.lock ├── SignalR-Swift-Example.xcodeproj │ ├── project.pbxproj │ └── xcuserdata │ │ └── jordan_camara.xcuserdatad │ │ └── xcschemes │ │ ├── SignalR-Swift-Example.xcscheme │ │ └── xcschememanagement.plist ├── SignalR-Swift-Example.xcworkspace │ └── contents.xcworkspacedata ├── SignalR-Swift-Example │ ├── AppDelegate.swift │ ├── Assets.xcassets │ │ └── AppIcon.appiconset │ │ │ └── Contents.json │ ├── Base.lproj │ │ ├── LaunchScreen.storyboard │ │ └── Main.storyboard │ ├── Info.plist │ └── ViewController.swift └── SignalR-Swift-ExampleTests │ ├── Info.plist │ └── SignalR_Swift_ExampleTests.swift ├── SignalR-Swift.xcodeproj ├── project.pbxproj └── xcuserdata │ └── jordan_camara.xcuserdatad │ └── xcschemes │ ├── SignalR-Swift.xcscheme │ └── xcschememanagement.plist ├── SignalR-Swift.xcworkspace └── contents.xcworkspacedata ├── SignalR-Swift ├── Client │ ├── Connection.swift │ ├── Enums │ │ └── ConnectionState.swift │ ├── HeartbeatMonitor.swift │ ├── Hubs │ │ ├── HubConnection.swift │ │ ├── HubInvocation.swift │ │ ├── HubProxy.swift │ │ └── HubResult.swift │ ├── Infrastructure │ │ ├── ExceptionHelper.swift │ │ └── Version.swift │ ├── KeepAliveData.swift │ ├── NegotiationResponse.swift │ └── Protocols │ │ ├── ClientTransportProtocol.swift │ │ ├── ConnectionDelegate.swift │ │ ├── ConnectionProtocol.swift │ │ ├── HubConnectionProtocol.swift │ │ └── HubProxyProtocol.swift ├── Extensions │ ├── Dictionary+Json.swift │ └── String+Dictionary.swift ├── Info.plist ├── Models │ └── ReceivedMessage.swift ├── SignalR-Swift.h └── Transports │ ├── AutoTransport.swift │ ├── HttpTransport.swift │ ├── LongPollingTransport.swift │ ├── ServerSentEvents │ ├── ChunkBuffer.swift │ └── ServerSentEvent.swift │ ├── ServerSentEventsTransport.swift │ ├── WebSocketTransport.swift │ └── WebSockets │ └── WebSocketConnectionInfo.swift ├── SignalR-SwiftTests ├── Connection │ └── ConnectionTests.swift ├── Info.plist └── Mocks │ └── MockClientTransport.swift └── SignalRSwift.podspec /.gitignore: -------------------------------------------------------------------------------- 1 | # Xcode 2 | # 3 | # gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore 4 | 5 | ## Build generated 6 | build/ 7 | DerivedData/ 8 | 9 | ## Various settings 10 | *.pbxuser 11 | !default.pbxuser 12 | *.mode1v3 13 | !default.mode1v3 14 | *.mode2v3 15 | !default.mode2v3 16 | *.perspectivev3 17 | !default.perspectivev3 18 | xcuserdata/ 19 | 20 | ## Other 21 | *.moved-aside 22 | *.xccheckout 23 | *.xcscmblueprint 24 | 25 | ## Obj-C/Swift specific 26 | *.hmap 27 | *.ipa 28 | *.dSYM.zip 29 | *.dSYM 30 | 31 | ## Playgrounds 32 | timeline.xctimeline 33 | playground.xcworkspace 34 | 35 | # Swift Package Manager 36 | # 37 | # Add this line if you want to avoid checking in source code from Swift Package Manager dependencies. 38 | # Packages/ 39 | # Package.pins 40 | .build/ 41 | 42 | # CocoaPods 43 | # 44 | # We recommend against adding the Pods directory to your .gitignore. However 45 | # you should judge for yourself, the pros and cons are mentioned at: 46 | # https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control 47 | # 48 | Pods/ 49 | 50 | # Carthage 51 | # 52 | # Add this line if you want to avoid checking in source code from Carthage dependencies. 53 | # Carthage/Checkouts 54 | 55 | Carthage/Build 56 | 57 | # fastlane 58 | # 59 | # It is recommended to not store the screenshots in the git repo. Instead, use fastlane to re-generate the 60 | # screenshots whenever they are needed. 61 | # For more information about the recommended setup visit: 62 | # https://docs.fastlane.tools/best-practices/source-control/#source-control 63 | 64 | fastlane/report.xml 65 | fastlane/Preview.html 66 | fastlane/screenshots 67 | fastlane/test_output 68 | .idea/modules.xml 69 | .idea/SignalR-Swift.iml 70 | .idea/vcs.xml 71 | .idea/workspace.xml 72 | .idea/xcode.xml 73 | -------------------------------------------------------------------------------- /.swift-version: -------------------------------------------------------------------------------- 1 | 3.0 2 | -------------------------------------------------------------------------------- /ACKNOWLEDGEMENTS.md: -------------------------------------------------------------------------------- 1 | ### Includes AlamoFire by the Alamofire Software Foundation. 2 | 3 | Copyright (c) 2014-2016 Alamofire Software Foundation (http://alamofire.org/) 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. 22 | 23 | ### Includes SwiftWebSocket by tidwall. 24 | 25 | Permission is hereby granted, free of charge, to any person obtaining a copy 26 | of this software and associated documentation files (the "Software"), to deal 27 | in the Software without restriction, including without limitation the rights 28 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 29 | copies of the Software, and to permit persons to whom the Software is 30 | furnished to do so, subject to the following conditions: 31 | 32 | The above copyright notice and this permission notice shall be included in 33 | all copies or substantial portions of the Software. 34 | 35 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 36 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 37 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 38 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 39 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 40 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 41 | THE SOFTWARE. 42 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 Autosoft 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /Podfile: -------------------------------------------------------------------------------- 1 | # Uncomment the next line to define a global platform for your project 2 | platform :ios, '10.3' 3 | 4 | target 'SignalRSwift' do 5 | # Comment the next line if you're not using Swift and don't want to use dynamic frameworks 6 | use_frameworks! 7 | 8 | # Pods for SignalR-Swift 9 | pod 'Alamofire', '~> 4.2' 10 | pod 'Starscream', '~> 3.0' 11 | 12 | target 'SignalR-SwiftTests' do 13 | inherit! :search_paths 14 | # Pods for testing 15 | pod 'Quick' 16 | pod 'Nimble' 17 | pod 'Mockit', '~> 1.3' 18 | end 19 | 20 | end 21 | 22 | post_install do |installer| 23 | installer.pods_project.targets.each do |target| 24 | if target.name == 'Mockit' 25 | target.build_configurations.each do |config| 26 | config.build_settings['SWIFT_VERSION'] = '3.2' 27 | end 28 | end 29 | end 30 | end -------------------------------------------------------------------------------- /Podfile.lock: -------------------------------------------------------------------------------- 1 | PODS: 2 | - Alamofire (4.6.0) 3 | - Mockit (1.3.4) 4 | - Nimble (7.0.3) 5 | - Quick (1.2.0) 6 | - Starscream (3.0.3) 7 | 8 | DEPENDENCIES: 9 | - Alamofire (~> 4.2) 10 | - Mockit (~> 1.3) 11 | - Nimble 12 | - Quick 13 | - Starscream (~> 3.0) 14 | 15 | SPEC CHECKSUMS: 16 | Alamofire: f41a599bd63041760b26d393ec1069d9d7b917f4 17 | Mockit: 1b253f229dde898b268f8bd5dffcf2fd0151ff75 18 | Nimble: 7f5a9c447a33002645a071bddafbfb24ea70e0ac 19 | Quick: 58d203b1c5e27fff7229c4c1ae445ad7069a7a08 20 | Starscream: dfb1b3f39506717ee52b67fb48de0c8269504cbc 21 | 22 | PODFILE CHECKSUM: 79d0be02a68490c1ac445f39448ee90e8f37cad7 23 | 24 | COCOAPODS: 1.3.1 25 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | SignalR-Swift is a client library for iOS based on https://github.com/DyKnow/SignalR-ObjC by DyKnow. It's built on top of [AlamoFire](https://github.com/Alamofire/Alamofire) and [Starscream](https://github.com/daltoniam/Starscream). 3 | SignalR-Swift is intended to be used along side ASP.NET SignalR, a new library for ASP.NET developers that makes it incredibly simple to add real-time functionality to your applications. What is "real-time web" functionality? It's the ability to have your server-side code push content to the connected clients as it happens, in real-time. 4 | 5 | ## Installation 6 | 7 | ### Installation with CocoaPods 8 | 9 | [CocoaPods](https://cocoapods.org/) is a dependency manager for Objective-C, which automates and simplifies the process of using 3rd-party libraries like SignalR-Swift in your projects. See the ["Getting Started" guide for more information](https://guides.cocoapods.org/using/getting-started.html). You can install it with the following command: 10 | 11 | ``` 12 | $ gem install cocoapods 13 | ``` 14 | 15 | #### Podfile 16 | 17 | To integrate SignalR-Swift into your Xcode project using CocoaPods, specify it in your Podfile: 18 | 19 | ``` 20 | source 'https://github.com/CocoaPods/Specs.git' 21 | platform :ios, '8.0' 22 | 23 | pod 'SignalRSwift', '~> 2.0.2' 24 | ``` 25 | 26 | Then, run the following command: 27 | 28 | ``` 29 | $ pod install 30 | ``` 31 | 32 | ## Example Usage 33 | ### Persistent Connection 34 | ```c# 35 | using System.Threading.Tasks; 36 | using Microsoft.AspNet.SignalR; 37 | 38 | //Server 39 | public class MyConnection : PersistentConnection 40 | { 41 | protected override Task OnReceived(IRequest request, string connectionId, string data) 42 | { 43 | // Broadcast data to all clients 44 | return Connection.Broadcast(data); 45 | } 46 | } 47 | ``` 48 | 49 | ```swift 50 | import SignalRSwift 51 | 52 | //Client 53 | var connection = Connection(withUrl: "http://localhost/endpoint/"); 54 | 55 | // register for connection lifecycle events 56 | connection.started = { 57 | print("Connected") 58 | } 59 | 60 | connection.reconnecting = { 61 | print("Reconnecting...") 62 | } 63 | 64 | connection.reconnected = { 65 | print("Reconnected.") 66 | } 67 | 68 | connection.closed = { 69 | print("Disconnected") 70 | } 71 | 72 | connection.connectionSlow = { print("Connection slow...") } 73 | 74 | connection.error = { error in 75 | print("Error") 76 | } 77 | 78 | connection.start() 79 | 80 | ``` 81 | ### Hubs 82 | ```c# 83 | //Server 84 | public class Chat : Hub 85 | { 86 | public void Send(string message) 87 | { 88 | // Call the addMessage method on all clients 89 | Clients.All.addMessage(message); 90 | } 91 | } 92 | ``` 93 | 94 | ```Swift 95 | //Client 96 | import SignalRSwift 97 | 98 | // Connect to the service 99 | var hubConnection = HubConnection(withUrl: "http://localhost/endpoint") 100 | 101 | var chat = hubConnection.createHubProxy(hubName: "chat") 102 | 103 | chat.on(eventName: "addMessage") { (args) in 104 | if let message = args[0] { 105 | print("Message: \(message)") 106 | } 107 | } 108 | 109 | // register for connection lifecycle events 110 | hubConnection.started = { 111 | print("Connected") 112 | } 113 | 114 | hubConnection.reconnecting = { 115 | print("Reconnecting...") 116 | } 117 | 118 | hubConnection.reconnected = { 119 | print("Reconnected.") 120 | } 121 | 122 | hubConnection.closed = { 123 | print("Disconnected") 124 | } 125 | 126 | hubConnection.connectionSlow = { print("Connection slow...") } 127 | 128 | hubConnection.error = { error in 129 | print("Error") 130 | } 131 | 132 | hubConnection.start() 133 | ``` 134 | 135 | ### Customizing Query Params 136 | 137 | #### Persistent Connections 138 | ```Swift 139 | let qs = [ 140 | "param1": "1", 141 | "param2": "another" 142 | } 143 | var connection = Connection(withUrl: "http://localhost/endpoint", queryString: qs) 144 | ``` 145 | 146 | #### Hub Connections 147 | ```Swift 148 | let qs = [ 149 | "param1": "1", 150 | "param2": "another" 151 | } 152 | var hubConnection = HubConnection(withUrl: "http://localhost/endpoint", queryString: qs) 153 | ``` 154 | 155 | ### Customizing Request Headers 156 | 157 | #### Persistent Connections 158 | ```Swift 159 | let headers = [ 160 | "param1": "1", 161 | "param2": "another" 162 | ] 163 | var connection = Connection(withUrl: "http://localhost/endpoint", queryString: qs) 164 | connection.headers = headers 165 | 166 | // alternative usage 167 | var connection = Connection(withUrl: "http://localhost/endpoint", queryString: qs) 168 | connection.addValue(value: "1", forHttpHeaderField: "param1") 169 | connection.addValue(value: "another", forHttpHeaderField: "param2") 170 | ``` 171 | 172 | #### Hub Connections 173 | ```Swift 174 | let headers = [ 175 | "param1": "1", 176 | "param2": "another" 177 | } 178 | var hubConnection = HubConnection(withUrl: "http://localhost/endpoint", queryString: qs) 179 | hubConnection.headers = headers 180 | 181 | // alternative usage 182 | var hubConnection = HubConnection(withUrl: "http://localhost/endpoint", queryString: qs) 183 | hubConnection.addValue(value: "1", forHttpHeaderField: "param1") 184 | hubConnection.addValue(value: "another", forHttpHeaderField: "param2") 185 | ``` 186 | 187 | ### Networking 188 | 189 | - SignalR-Swift uses [Alamofire](https://github.com/Alamofire/Alamofire). The minimum supported version of AlamoFire is 4.2.x 190 | - SignalR-Swift uses [Starscream](https://github.com/daltoniam/Starscream). 191 | 192 | 193 | ## LICENSE 194 | 195 | SignalR-Swift is available under the MIT license. See the [LICENSE](https://github.com/AutosoftDMS/SignalR-Swift/blob/master/LICENSE.md) file for more info.
196 | SignalR-Swift uses 3rd-party code which each have specific licenses, see [ACKNOWLEDGEMENTS](https://github.com/AutosoftDMS/SignalR-Swift/blob/master/ACKNOWLEDGEMENTS.md) for contributions 197 | -------------------------------------------------------------------------------- /SignalR-Swift-Example/Podfile: -------------------------------------------------------------------------------- 1 | # Uncomment the next line to define a global platform for your project 2 | platform :ios, '10.3' 3 | 4 | target 'SignalR-Swift-Example' do 5 | # Comment the next line if you're not using Swift and don't want to use dynamic frameworks 6 | use_frameworks! 7 | 8 | # Pods for SignalR-Swift-Example 9 | pod 'SignalRSwift', :path => '../' 10 | 11 | target 'SignalR-Swift-ExampleTests' do 12 | inherit! :search_paths 13 | # Pods for testing 14 | end 15 | 16 | end 17 | -------------------------------------------------------------------------------- /SignalR-Swift-Example/Podfile.lock: -------------------------------------------------------------------------------- 1 | PODS: 2 | - Alamofire (4.6.0) 3 | - SignalRSwift (2.0.1): 4 | - Alamofire (~> 4.2) 5 | - Starscream (~> 3.0) 6 | - Starscream (3.0.3) 7 | 8 | DEPENDENCIES: 9 | - SignalRSwift (from `../`) 10 | 11 | EXTERNAL SOURCES: 12 | SignalRSwift: 13 | :path: ../ 14 | 15 | SPEC CHECKSUMS: 16 | Alamofire: f41a599bd63041760b26d393ec1069d9d7b917f4 17 | SignalRSwift: 92b0536d81f0dea4bb3f941adea7422dc26886e8 18 | Starscream: dfb1b3f39506717ee52b67fb48de0c8269504cbc 19 | 20 | PODFILE CHECKSUM: 0b670b9c4ac8d7df99f98ba1a30595cebc4466a9 21 | 22 | COCOAPODS: 1.3.1 23 | -------------------------------------------------------------------------------- /SignalR-Swift-Example/SignalR-Swift-Example.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 46; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 715553CCD279348CB60CA3CA /* Pods_SignalR_Swift_ExampleTests.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = BB5250B3C095B54B6A8372E7 /* Pods_SignalR_Swift_ExampleTests.framework */; }; 11 | C9E64D701E27DC3300DEDC20 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = C9E64D6F1E27DC3300DEDC20 /* AppDelegate.swift */; }; 12 | C9E64D721E27DC3300DEDC20 /* ViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = C9E64D711E27DC3300DEDC20 /* ViewController.swift */; }; 13 | C9E64D771E27DC3300DEDC20 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = C9E64D761E27DC3300DEDC20 /* Assets.xcassets */; }; 14 | C9E64D7A1E27DC3300DEDC20 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = C9E64D781E27DC3300DEDC20 /* LaunchScreen.storyboard */; }; 15 | C9E64D851E27DC3400DEDC20 /* SignalR_Swift_ExampleTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = C9E64D841E27DC3400DEDC20 /* SignalR_Swift_ExampleTests.swift */; }; 16 | C9E64D911E27E47800DEDC20 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = C9E64D8F1E27E47800DEDC20 /* Main.storyboard */; }; 17 | /* End PBXBuildFile section */ 18 | 19 | /* Begin PBXContainerItemProxy section */ 20 | C9E64D811E27DC3400DEDC20 /* PBXContainerItemProxy */ = { 21 | isa = PBXContainerItemProxy; 22 | containerPortal = C9E64D641E27DC3300DEDC20 /* Project object */; 23 | proxyType = 1; 24 | remoteGlobalIDString = C9E64D6B1E27DC3300DEDC20; 25 | remoteInfo = "SignalR-Swift-Example"; 26 | }; 27 | /* End PBXContainerItemProxy section */ 28 | 29 | /* Begin PBXFileReference section */ 30 | 26A458CE9EF8F3422E825CF9 /* Pods_SignalR_Swift_Example.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_SignalR_Swift_Example.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 31 | 790AC94075959051C56799D5 /* Pods-SignalR-Swift-ExampleTests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-SignalR-Swift-ExampleTests.release.xcconfig"; path = "Pods/Target Support Files/Pods-SignalR-Swift-ExampleTests/Pods-SignalR-Swift-ExampleTests.release.xcconfig"; sourceTree = ""; }; 32 | 793E4C03DAB6B7009192E2EF /* Pods-SignalR-Swift-Example.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-SignalR-Swift-Example.release.xcconfig"; path = "Pods/Target Support Files/Pods-SignalR-Swift-Example/Pods-SignalR-Swift-Example.release.xcconfig"; sourceTree = ""; }; 33 | A2D0F25BF5CDF45777FDC3D3 /* Pods-SignalR-Swift-ExampleTests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-SignalR-Swift-ExampleTests.debug.xcconfig"; path = "Pods/Target Support Files/Pods-SignalR-Swift-ExampleTests/Pods-SignalR-Swift-ExampleTests.debug.xcconfig"; sourceTree = ""; }; 34 | BB5250B3C095B54B6A8372E7 /* Pods_SignalR_Swift_ExampleTests.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_SignalR_Swift_ExampleTests.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 35 | C61CCEAF90DA9573CAF2F41D /* Pods-SignalR-Swift-Example.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-SignalR-Swift-Example.debug.xcconfig"; path = "Pods/Target Support Files/Pods-SignalR-Swift-Example/Pods-SignalR-Swift-Example.debug.xcconfig"; sourceTree = ""; }; 36 | C9E64D6C1E27DC3300DEDC20 /* SignalR-Swift-Example.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "SignalR-Swift-Example.app"; sourceTree = BUILT_PRODUCTS_DIR; }; 37 | C9E64D6F1E27DC3300DEDC20 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; 38 | C9E64D711E27DC3300DEDC20 /* ViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewController.swift; sourceTree = ""; }; 39 | C9E64D761E27DC3300DEDC20 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 40 | C9E64D791E27DC3300DEDC20 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; 41 | C9E64D7B1E27DC3300DEDC20 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 42 | C9E64D801E27DC3400DEDC20 /* SignalR-Swift-ExampleTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = "SignalR-Swift-ExampleTests.xctest"; sourceTree = BUILT_PRODUCTS_DIR; }; 43 | C9E64D841E27DC3400DEDC20 /* SignalR_Swift_ExampleTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SignalR_Swift_ExampleTests.swift; sourceTree = ""; }; 44 | C9E64D861E27DC3400DEDC20 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 45 | C9E64D901E27E47800DEDC20 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; 46 | /* End PBXFileReference section */ 47 | 48 | /* Begin PBXFrameworksBuildPhase section */ 49 | C9E64D691E27DC3300DEDC20 /* Frameworks */ = { 50 | isa = PBXFrameworksBuildPhase; 51 | buildActionMask = 2147483647; 52 | files = ( 53 | ); 54 | runOnlyForDeploymentPostprocessing = 0; 55 | }; 56 | C9E64D7D1E27DC3400DEDC20 /* Frameworks */ = { 57 | isa = PBXFrameworksBuildPhase; 58 | buildActionMask = 2147483647; 59 | files = ( 60 | 715553CCD279348CB60CA3CA /* Pods_SignalR_Swift_ExampleTests.framework in Frameworks */, 61 | ); 62 | runOnlyForDeploymentPostprocessing = 0; 63 | }; 64 | /* End PBXFrameworksBuildPhase section */ 65 | 66 | /* Begin PBXGroup section */ 67 | 3074C68C23B280A1578C9624 /* Frameworks */ = { 68 | isa = PBXGroup; 69 | children = ( 70 | 26A458CE9EF8F3422E825CF9 /* Pods_SignalR_Swift_Example.framework */, 71 | BB5250B3C095B54B6A8372E7 /* Pods_SignalR_Swift_ExampleTests.framework */, 72 | ); 73 | name = Frameworks; 74 | sourceTree = ""; 75 | }; 76 | 781EA09133FDDDA08190CC4A /* Pods */ = { 77 | isa = PBXGroup; 78 | children = ( 79 | C61CCEAF90DA9573CAF2F41D /* Pods-SignalR-Swift-Example.debug.xcconfig */, 80 | 793E4C03DAB6B7009192E2EF /* Pods-SignalR-Swift-Example.release.xcconfig */, 81 | A2D0F25BF5CDF45777FDC3D3 /* Pods-SignalR-Swift-ExampleTests.debug.xcconfig */, 82 | 790AC94075959051C56799D5 /* Pods-SignalR-Swift-ExampleTests.release.xcconfig */, 83 | ); 84 | name = Pods; 85 | sourceTree = ""; 86 | }; 87 | C9E64D631E27DC3300DEDC20 = { 88 | isa = PBXGroup; 89 | children = ( 90 | C9E64D6E1E27DC3300DEDC20 /* SignalR-Swift-Example */, 91 | C9E64D831E27DC3400DEDC20 /* SignalR-Swift-ExampleTests */, 92 | C9E64D6D1E27DC3300DEDC20 /* Products */, 93 | 781EA09133FDDDA08190CC4A /* Pods */, 94 | 3074C68C23B280A1578C9624 /* Frameworks */, 95 | ); 96 | sourceTree = ""; 97 | }; 98 | C9E64D6D1E27DC3300DEDC20 /* Products */ = { 99 | isa = PBXGroup; 100 | children = ( 101 | C9E64D6C1E27DC3300DEDC20 /* SignalR-Swift-Example.app */, 102 | C9E64D801E27DC3400DEDC20 /* SignalR-Swift-ExampleTests.xctest */, 103 | ); 104 | name = Products; 105 | sourceTree = ""; 106 | }; 107 | C9E64D6E1E27DC3300DEDC20 /* SignalR-Swift-Example */ = { 108 | isa = PBXGroup; 109 | children = ( 110 | C9E64D6F1E27DC3300DEDC20 /* AppDelegate.swift */, 111 | C9E64D711E27DC3300DEDC20 /* ViewController.swift */, 112 | C9E64D761E27DC3300DEDC20 /* Assets.xcassets */, 113 | C9E64D781E27DC3300DEDC20 /* LaunchScreen.storyboard */, 114 | C9E64D8F1E27E47800DEDC20 /* Main.storyboard */, 115 | C9E64D7B1E27DC3300DEDC20 /* Info.plist */, 116 | ); 117 | path = "SignalR-Swift-Example"; 118 | sourceTree = ""; 119 | }; 120 | C9E64D831E27DC3400DEDC20 /* SignalR-Swift-ExampleTests */ = { 121 | isa = PBXGroup; 122 | children = ( 123 | C9E64D841E27DC3400DEDC20 /* SignalR_Swift_ExampleTests.swift */, 124 | C9E64D861E27DC3400DEDC20 /* Info.plist */, 125 | ); 126 | path = "SignalR-Swift-ExampleTests"; 127 | sourceTree = ""; 128 | }; 129 | /* End PBXGroup section */ 130 | 131 | /* Begin PBXNativeTarget section */ 132 | C9E64D6B1E27DC3300DEDC20 /* SignalR-Swift-Example */ = { 133 | isa = PBXNativeTarget; 134 | buildConfigurationList = C9E64D891E27DC3400DEDC20 /* Build configuration list for PBXNativeTarget "SignalR-Swift-Example" */; 135 | buildPhases = ( 136 | 0C3202FEE06CE733AE264351 /* [CP] Check Pods Manifest.lock */, 137 | C9E64D681E27DC3300DEDC20 /* Sources */, 138 | C9E64D691E27DC3300DEDC20 /* Frameworks */, 139 | C9E64D6A1E27DC3300DEDC20 /* Resources */, 140 | 6F42A1175AEEF087513135D6 /* [CP] Embed Pods Frameworks */, 141 | 3B0F052C7D6CDBE56E5190EC /* [CP] Copy Pods Resources */, 142 | ); 143 | buildRules = ( 144 | ); 145 | dependencies = ( 146 | ); 147 | name = "SignalR-Swift-Example"; 148 | productName = "SignalR-Swift-Example"; 149 | productReference = C9E64D6C1E27DC3300DEDC20 /* SignalR-Swift-Example.app */; 150 | productType = "com.apple.product-type.application"; 151 | }; 152 | C9E64D7F1E27DC3400DEDC20 /* SignalR-Swift-ExampleTests */ = { 153 | isa = PBXNativeTarget; 154 | buildConfigurationList = C9E64D8C1E27DC3400DEDC20 /* Build configuration list for PBXNativeTarget "SignalR-Swift-ExampleTests" */; 155 | buildPhases = ( 156 | B721394EE4F0D4E64AB5B418 /* [CP] Check Pods Manifest.lock */, 157 | C9E64D7C1E27DC3400DEDC20 /* Sources */, 158 | C9E64D7D1E27DC3400DEDC20 /* Frameworks */, 159 | C9E64D7E1E27DC3400DEDC20 /* Resources */, 160 | 10D9A3292E3B053C4B1F1E8B /* [CP] Embed Pods Frameworks */, 161 | 77521BBF68CE60E8E2BA4051 /* [CP] Copy Pods Resources */, 162 | ); 163 | buildRules = ( 164 | ); 165 | dependencies = ( 166 | C9E64D821E27DC3400DEDC20 /* PBXTargetDependency */, 167 | ); 168 | name = "SignalR-Swift-ExampleTests"; 169 | productName = "SignalR-Swift-ExampleTests"; 170 | productReference = C9E64D801E27DC3400DEDC20 /* SignalR-Swift-ExampleTests.xctest */; 171 | productType = "com.apple.product-type.bundle.unit-test"; 172 | }; 173 | /* End PBXNativeTarget section */ 174 | 175 | /* Begin PBXProject section */ 176 | C9E64D641E27DC3300DEDC20 /* Project object */ = { 177 | isa = PBXProject; 178 | attributes = { 179 | LastSwiftUpdateCheck = 0820; 180 | LastUpgradeCheck = 0900; 181 | ORGANIZATIONNAME = "Jordan Camara"; 182 | TargetAttributes = { 183 | C9E64D6B1E27DC3300DEDC20 = { 184 | CreatedOnToolsVersion = 8.2.1; 185 | DevelopmentTeam = 8Z35TU97D6; 186 | ProvisioningStyle = Automatic; 187 | }; 188 | C9E64D7F1E27DC3400DEDC20 = { 189 | CreatedOnToolsVersion = 8.2.1; 190 | DevelopmentTeam = 8Z35TU97D6; 191 | ProvisioningStyle = Automatic; 192 | TestTargetID = C9E64D6B1E27DC3300DEDC20; 193 | }; 194 | }; 195 | }; 196 | buildConfigurationList = C9E64D671E27DC3300DEDC20 /* Build configuration list for PBXProject "SignalR-Swift-Example" */; 197 | compatibilityVersion = "Xcode 3.2"; 198 | developmentRegion = English; 199 | hasScannedForEncodings = 0; 200 | knownRegions = ( 201 | en, 202 | Base, 203 | ); 204 | mainGroup = C9E64D631E27DC3300DEDC20; 205 | productRefGroup = C9E64D6D1E27DC3300DEDC20 /* Products */; 206 | projectDirPath = ""; 207 | projectRoot = ""; 208 | targets = ( 209 | C9E64D6B1E27DC3300DEDC20 /* SignalR-Swift-Example */, 210 | C9E64D7F1E27DC3400DEDC20 /* SignalR-Swift-ExampleTests */, 211 | ); 212 | }; 213 | /* End PBXProject section */ 214 | 215 | /* Begin PBXResourcesBuildPhase section */ 216 | C9E64D6A1E27DC3300DEDC20 /* Resources */ = { 217 | isa = PBXResourcesBuildPhase; 218 | buildActionMask = 2147483647; 219 | files = ( 220 | C9E64D911E27E47800DEDC20 /* Main.storyboard in Resources */, 221 | C9E64D7A1E27DC3300DEDC20 /* LaunchScreen.storyboard in Resources */, 222 | C9E64D771E27DC3300DEDC20 /* Assets.xcassets in Resources */, 223 | ); 224 | runOnlyForDeploymentPostprocessing = 0; 225 | }; 226 | C9E64D7E1E27DC3400DEDC20 /* Resources */ = { 227 | isa = PBXResourcesBuildPhase; 228 | buildActionMask = 2147483647; 229 | files = ( 230 | ); 231 | runOnlyForDeploymentPostprocessing = 0; 232 | }; 233 | /* End PBXResourcesBuildPhase section */ 234 | 235 | /* Begin PBXShellScriptBuildPhase section */ 236 | 0C3202FEE06CE733AE264351 /* [CP] Check Pods Manifest.lock */ = { 237 | isa = PBXShellScriptBuildPhase; 238 | buildActionMask = 2147483647; 239 | files = ( 240 | ); 241 | inputPaths = ( 242 | "${PODS_PODFILE_DIR_PATH}/Podfile.lock", 243 | "${PODS_ROOT}/Manifest.lock", 244 | ); 245 | name = "[CP] Check Pods Manifest.lock"; 246 | outputPaths = ( 247 | "$(DERIVED_FILE_DIR)/Pods-SignalR-Swift-Example-checkManifestLockResult.txt", 248 | ); 249 | runOnlyForDeploymentPostprocessing = 0; 250 | shellPath = /bin/sh; 251 | shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; 252 | showEnvVarsInLog = 0; 253 | }; 254 | 10D9A3292E3B053C4B1F1E8B /* [CP] Embed Pods Frameworks */ = { 255 | isa = PBXShellScriptBuildPhase; 256 | buildActionMask = 2147483647; 257 | files = ( 258 | ); 259 | inputPaths = ( 260 | ); 261 | name = "[CP] Embed Pods Frameworks"; 262 | outputPaths = ( 263 | ); 264 | runOnlyForDeploymentPostprocessing = 0; 265 | shellPath = /bin/sh; 266 | shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods-SignalR-Swift-ExampleTests/Pods-SignalR-Swift-ExampleTests-frameworks.sh\"\n"; 267 | showEnvVarsInLog = 0; 268 | }; 269 | 3B0F052C7D6CDBE56E5190EC /* [CP] Copy Pods Resources */ = { 270 | isa = PBXShellScriptBuildPhase; 271 | buildActionMask = 2147483647; 272 | files = ( 273 | ); 274 | inputPaths = ( 275 | ); 276 | name = "[CP] Copy Pods Resources"; 277 | outputPaths = ( 278 | ); 279 | runOnlyForDeploymentPostprocessing = 0; 280 | shellPath = /bin/sh; 281 | shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods-SignalR-Swift-Example/Pods-SignalR-Swift-Example-resources.sh\"\n"; 282 | showEnvVarsInLog = 0; 283 | }; 284 | 6F42A1175AEEF087513135D6 /* [CP] Embed Pods Frameworks */ = { 285 | isa = PBXShellScriptBuildPhase; 286 | buildActionMask = 2147483647; 287 | files = ( 288 | ); 289 | inputPaths = ( 290 | "${SRCROOT}/Pods/Target Support Files/Pods-SignalR-Swift-Example/Pods-SignalR-Swift-Example-frameworks.sh", 291 | "${BUILT_PRODUCTS_DIR}/Alamofire/Alamofire.framework", 292 | "${BUILT_PRODUCTS_DIR}/SignalRSwift/SignalRSwift.framework", 293 | "${BUILT_PRODUCTS_DIR}/Starscream/Starscream.framework", 294 | ); 295 | name = "[CP] Embed Pods Frameworks"; 296 | outputPaths = ( 297 | "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/Alamofire.framework", 298 | "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/SignalRSwift.framework", 299 | "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/Starscream.framework", 300 | ); 301 | runOnlyForDeploymentPostprocessing = 0; 302 | shellPath = /bin/sh; 303 | shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods-SignalR-Swift-Example/Pods-SignalR-Swift-Example-frameworks.sh\"\n"; 304 | showEnvVarsInLog = 0; 305 | }; 306 | 77521BBF68CE60E8E2BA4051 /* [CP] Copy Pods Resources */ = { 307 | isa = PBXShellScriptBuildPhase; 308 | buildActionMask = 2147483647; 309 | files = ( 310 | ); 311 | inputPaths = ( 312 | ); 313 | name = "[CP] Copy Pods Resources"; 314 | outputPaths = ( 315 | ); 316 | runOnlyForDeploymentPostprocessing = 0; 317 | shellPath = /bin/sh; 318 | shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods-SignalR-Swift-ExampleTests/Pods-SignalR-Swift-ExampleTests-resources.sh\"\n"; 319 | showEnvVarsInLog = 0; 320 | }; 321 | B721394EE4F0D4E64AB5B418 /* [CP] Check Pods Manifest.lock */ = { 322 | isa = PBXShellScriptBuildPhase; 323 | buildActionMask = 2147483647; 324 | files = ( 325 | ); 326 | inputPaths = ( 327 | "${PODS_PODFILE_DIR_PATH}/Podfile.lock", 328 | "${PODS_ROOT}/Manifest.lock", 329 | ); 330 | name = "[CP] Check Pods Manifest.lock"; 331 | outputPaths = ( 332 | "$(DERIVED_FILE_DIR)/Pods-SignalR-Swift-ExampleTests-checkManifestLockResult.txt", 333 | ); 334 | runOnlyForDeploymentPostprocessing = 0; 335 | shellPath = /bin/sh; 336 | shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; 337 | showEnvVarsInLog = 0; 338 | }; 339 | /* End PBXShellScriptBuildPhase section */ 340 | 341 | /* Begin PBXSourcesBuildPhase section */ 342 | C9E64D681E27DC3300DEDC20 /* Sources */ = { 343 | isa = PBXSourcesBuildPhase; 344 | buildActionMask = 2147483647; 345 | files = ( 346 | C9E64D721E27DC3300DEDC20 /* ViewController.swift in Sources */, 347 | C9E64D701E27DC3300DEDC20 /* AppDelegate.swift in Sources */, 348 | ); 349 | runOnlyForDeploymentPostprocessing = 0; 350 | }; 351 | C9E64D7C1E27DC3400DEDC20 /* Sources */ = { 352 | isa = PBXSourcesBuildPhase; 353 | buildActionMask = 2147483647; 354 | files = ( 355 | C9E64D851E27DC3400DEDC20 /* SignalR_Swift_ExampleTests.swift in Sources */, 356 | ); 357 | runOnlyForDeploymentPostprocessing = 0; 358 | }; 359 | /* End PBXSourcesBuildPhase section */ 360 | 361 | /* Begin PBXTargetDependency section */ 362 | C9E64D821E27DC3400DEDC20 /* PBXTargetDependency */ = { 363 | isa = PBXTargetDependency; 364 | target = C9E64D6B1E27DC3300DEDC20 /* SignalR-Swift-Example */; 365 | targetProxy = C9E64D811E27DC3400DEDC20 /* PBXContainerItemProxy */; 366 | }; 367 | /* End PBXTargetDependency section */ 368 | 369 | /* Begin PBXVariantGroup section */ 370 | C9E64D781E27DC3300DEDC20 /* LaunchScreen.storyboard */ = { 371 | isa = PBXVariantGroup; 372 | children = ( 373 | C9E64D791E27DC3300DEDC20 /* Base */, 374 | ); 375 | name = LaunchScreen.storyboard; 376 | sourceTree = ""; 377 | }; 378 | C9E64D8F1E27E47800DEDC20 /* Main.storyboard */ = { 379 | isa = PBXVariantGroup; 380 | children = ( 381 | C9E64D901E27E47800DEDC20 /* Base */, 382 | ); 383 | name = Main.storyboard; 384 | sourceTree = ""; 385 | }; 386 | /* End PBXVariantGroup section */ 387 | 388 | /* Begin XCBuildConfiguration section */ 389 | C9E64D871E27DC3400DEDC20 /* Debug */ = { 390 | isa = XCBuildConfiguration; 391 | buildSettings = { 392 | ALWAYS_SEARCH_USER_PATHS = NO; 393 | CLANG_ANALYZER_NONNULL = YES; 394 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 395 | CLANG_CXX_LIBRARY = "libc++"; 396 | CLANG_ENABLE_MODULES = YES; 397 | CLANG_ENABLE_OBJC_ARC = YES; 398 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 399 | CLANG_WARN_BOOL_CONVERSION = YES; 400 | CLANG_WARN_COMMA = YES; 401 | CLANG_WARN_CONSTANT_CONVERSION = YES; 402 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 403 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 404 | CLANG_WARN_EMPTY_BODY = YES; 405 | CLANG_WARN_ENUM_CONVERSION = YES; 406 | CLANG_WARN_INFINITE_RECURSION = YES; 407 | CLANG_WARN_INT_CONVERSION = YES; 408 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 409 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 410 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 411 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 412 | CLANG_WARN_STRICT_PROTOTYPES = YES; 413 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 414 | CLANG_WARN_UNREACHABLE_CODE = YES; 415 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 416 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 417 | COPY_PHASE_STRIP = NO; 418 | DEBUG_INFORMATION_FORMAT = dwarf; 419 | ENABLE_STRICT_OBJC_MSGSEND = YES; 420 | ENABLE_TESTABILITY = YES; 421 | GCC_C_LANGUAGE_STANDARD = gnu99; 422 | GCC_DYNAMIC_NO_PIC = NO; 423 | GCC_NO_COMMON_BLOCKS = YES; 424 | GCC_OPTIMIZATION_LEVEL = 0; 425 | GCC_PREPROCESSOR_DEFINITIONS = ( 426 | "DEBUG=1", 427 | "$(inherited)", 428 | ); 429 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 430 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 431 | GCC_WARN_UNDECLARED_SELECTOR = YES; 432 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 433 | GCC_WARN_UNUSED_FUNCTION = YES; 434 | GCC_WARN_UNUSED_VARIABLE = YES; 435 | IPHONEOS_DEPLOYMENT_TARGET = 10.3; 436 | MTL_ENABLE_DEBUG_INFO = YES; 437 | ONLY_ACTIVE_ARCH = YES; 438 | SDKROOT = iphoneos; 439 | SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; 440 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 441 | TARGETED_DEVICE_FAMILY = "1,2"; 442 | }; 443 | name = Debug; 444 | }; 445 | C9E64D881E27DC3400DEDC20 /* Release */ = { 446 | isa = XCBuildConfiguration; 447 | buildSettings = { 448 | ALWAYS_SEARCH_USER_PATHS = NO; 449 | CLANG_ANALYZER_NONNULL = YES; 450 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 451 | CLANG_CXX_LIBRARY = "libc++"; 452 | CLANG_ENABLE_MODULES = YES; 453 | CLANG_ENABLE_OBJC_ARC = YES; 454 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 455 | CLANG_WARN_BOOL_CONVERSION = YES; 456 | CLANG_WARN_COMMA = YES; 457 | CLANG_WARN_CONSTANT_CONVERSION = YES; 458 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 459 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 460 | CLANG_WARN_EMPTY_BODY = YES; 461 | CLANG_WARN_ENUM_CONVERSION = YES; 462 | CLANG_WARN_INFINITE_RECURSION = YES; 463 | CLANG_WARN_INT_CONVERSION = YES; 464 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 465 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 466 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 467 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 468 | CLANG_WARN_STRICT_PROTOTYPES = YES; 469 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 470 | CLANG_WARN_UNREACHABLE_CODE = YES; 471 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 472 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 473 | COPY_PHASE_STRIP = NO; 474 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 475 | ENABLE_NS_ASSERTIONS = NO; 476 | ENABLE_STRICT_OBJC_MSGSEND = YES; 477 | GCC_C_LANGUAGE_STANDARD = gnu99; 478 | GCC_NO_COMMON_BLOCKS = YES; 479 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 480 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 481 | GCC_WARN_UNDECLARED_SELECTOR = YES; 482 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 483 | GCC_WARN_UNUSED_FUNCTION = YES; 484 | GCC_WARN_UNUSED_VARIABLE = YES; 485 | IPHONEOS_DEPLOYMENT_TARGET = 10.3; 486 | MTL_ENABLE_DEBUG_INFO = NO; 487 | SDKROOT = iphoneos; 488 | SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; 489 | TARGETED_DEVICE_FAMILY = "1,2"; 490 | VALIDATE_PRODUCT = YES; 491 | }; 492 | name = Release; 493 | }; 494 | C9E64D8A1E27DC3400DEDC20 /* Debug */ = { 495 | isa = XCBuildConfiguration; 496 | baseConfigurationReference = C61CCEAF90DA9573CAF2F41D /* Pods-SignalR-Swift-Example.debug.xcconfig */; 497 | buildSettings = { 498 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 499 | DEVELOPMENT_TEAM = 8Z35TU97D6; 500 | INFOPLIST_FILE = "SignalR-Swift-Example/Info.plist"; 501 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 502 | PRODUCT_BUNDLE_IDENTIFIER = "com.autosoftdms.SignalR-Swift-Example"; 503 | PRODUCT_NAME = "$(TARGET_NAME)"; 504 | SWIFT_VERSION = 4.0; 505 | }; 506 | name = Debug; 507 | }; 508 | C9E64D8B1E27DC3400DEDC20 /* Release */ = { 509 | isa = XCBuildConfiguration; 510 | baseConfigurationReference = 793E4C03DAB6B7009192E2EF /* Pods-SignalR-Swift-Example.release.xcconfig */; 511 | buildSettings = { 512 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 513 | DEVELOPMENT_TEAM = 8Z35TU97D6; 514 | INFOPLIST_FILE = "SignalR-Swift-Example/Info.plist"; 515 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 516 | PRODUCT_BUNDLE_IDENTIFIER = "com.autosoftdms.SignalR-Swift-Example"; 517 | PRODUCT_NAME = "$(TARGET_NAME)"; 518 | SWIFT_VERSION = 4.0; 519 | }; 520 | name = Release; 521 | }; 522 | C9E64D8D1E27DC3400DEDC20 /* Debug */ = { 523 | isa = XCBuildConfiguration; 524 | baseConfigurationReference = A2D0F25BF5CDF45777FDC3D3 /* Pods-SignalR-Swift-ExampleTests.debug.xcconfig */; 525 | buildSettings = { 526 | ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; 527 | BUNDLE_LOADER = "$(TEST_HOST)"; 528 | DEVELOPMENT_TEAM = 8Z35TU97D6; 529 | INFOPLIST_FILE = "SignalR-Swift-ExampleTests/Info.plist"; 530 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 531 | PRODUCT_BUNDLE_IDENTIFIER = "com.autosoftdms.SignalR-Swift-ExampleTests"; 532 | PRODUCT_NAME = "$(TARGET_NAME)"; 533 | SWIFT_VERSION = 4.0; 534 | TEST_HOST = "$(BUILT_PRODUCTS_DIR)/SignalR-Swift-Example.app/SignalR-Swift-Example"; 535 | }; 536 | name = Debug; 537 | }; 538 | C9E64D8E1E27DC3400DEDC20 /* Release */ = { 539 | isa = XCBuildConfiguration; 540 | baseConfigurationReference = 790AC94075959051C56799D5 /* Pods-SignalR-Swift-ExampleTests.release.xcconfig */; 541 | buildSettings = { 542 | ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; 543 | BUNDLE_LOADER = "$(TEST_HOST)"; 544 | DEVELOPMENT_TEAM = 8Z35TU97D6; 545 | INFOPLIST_FILE = "SignalR-Swift-ExampleTests/Info.plist"; 546 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 547 | PRODUCT_BUNDLE_IDENTIFIER = "com.autosoftdms.SignalR-Swift-ExampleTests"; 548 | PRODUCT_NAME = "$(TARGET_NAME)"; 549 | SWIFT_VERSION = 4.0; 550 | TEST_HOST = "$(BUILT_PRODUCTS_DIR)/SignalR-Swift-Example.app/SignalR-Swift-Example"; 551 | }; 552 | name = Release; 553 | }; 554 | /* End XCBuildConfiguration section */ 555 | 556 | /* Begin XCConfigurationList section */ 557 | C9E64D671E27DC3300DEDC20 /* Build configuration list for PBXProject "SignalR-Swift-Example" */ = { 558 | isa = XCConfigurationList; 559 | buildConfigurations = ( 560 | C9E64D871E27DC3400DEDC20 /* Debug */, 561 | C9E64D881E27DC3400DEDC20 /* Release */, 562 | ); 563 | defaultConfigurationIsVisible = 0; 564 | defaultConfigurationName = Release; 565 | }; 566 | C9E64D891E27DC3400DEDC20 /* Build configuration list for PBXNativeTarget "SignalR-Swift-Example" */ = { 567 | isa = XCConfigurationList; 568 | buildConfigurations = ( 569 | C9E64D8A1E27DC3400DEDC20 /* Debug */, 570 | C9E64D8B1E27DC3400DEDC20 /* Release */, 571 | ); 572 | defaultConfigurationIsVisible = 0; 573 | defaultConfigurationName = Release; 574 | }; 575 | C9E64D8C1E27DC3400DEDC20 /* Build configuration list for PBXNativeTarget "SignalR-Swift-ExampleTests" */ = { 576 | isa = XCConfigurationList; 577 | buildConfigurations = ( 578 | C9E64D8D1E27DC3400DEDC20 /* Debug */, 579 | C9E64D8E1E27DC3400DEDC20 /* Release */, 580 | ); 581 | defaultConfigurationIsVisible = 0; 582 | defaultConfigurationName = Release; 583 | }; 584 | /* End XCConfigurationList section */ 585 | }; 586 | rootObject = C9E64D641E27DC3300DEDC20 /* Project object */; 587 | } 588 | -------------------------------------------------------------------------------- /SignalR-Swift-Example/SignalR-Swift-Example.xcodeproj/xcuserdata/jordan_camara.xcuserdatad/xcschemes/SignalR-Swift-Example.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 33 | 39 | 40 | 41 | 42 | 43 | 49 | 50 | 51 | 52 | 53 | 54 | 64 | 66 | 72 | 73 | 74 | 75 | 76 | 77 | 83 | 85 | 91 | 92 | 93 | 94 | 96 | 97 | 100 | 101 | 102 | -------------------------------------------------------------------------------- /SignalR-Swift-Example/SignalR-Swift-Example.xcodeproj/xcuserdata/jordan_camara.xcuserdatad/xcschemes/xcschememanagement.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | SchemeUserState 6 | 7 | SignalR-Swift-Example.xcscheme 8 | 9 | orderHint 10 | 1 11 | 12 | 13 | SuppressBuildableAutocreation 14 | 15 | C9E64D6B1E27DC3300DEDC20 16 | 17 | primary 18 | 19 | 20 | C9E64D7F1E27DC3400DEDC20 21 | 22 | primary 23 | 24 | 25 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /SignalR-Swift-Example/SignalR-Swift-Example.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /SignalR-Swift-Example/SignalR-Swift-Example/AppDelegate.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.swift 3 | // SignalR-Swift-Example 4 | // 5 | // Created by Jordan Camara on 1/12/17. 6 | // Copyright © 2017 Jordan Camara. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | @UIApplicationMain 12 | class AppDelegate: UIResponder, UIApplicationDelegate { 13 | 14 | var window: UIWindow? 15 | 16 | 17 | func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool { 18 | // Override point for customization after application launch. 19 | return true 20 | } 21 | 22 | func applicationWillResignActive(_ application: UIApplication) { 23 | // Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state. 24 | // Use this method to pause ongoing tasks, disable timers, and invalidate graphics rendering callbacks. Games should use this method to pause the game. 25 | } 26 | 27 | func applicationDidEnterBackground(_ application: UIApplication) { 28 | // Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later. 29 | // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits. 30 | } 31 | 32 | func applicationWillEnterForeground(_ application: UIApplication) { 33 | // Called as part of the transition from the background to the active state; here you can undo many of the changes made on entering the background. 34 | } 35 | 36 | func applicationDidBecomeActive(_ application: UIApplication) { 37 | // Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface. 38 | } 39 | 40 | func applicationWillTerminate(_ application: UIApplication) { 41 | // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:. 42 | } 43 | 44 | 45 | } 46 | 47 | -------------------------------------------------------------------------------- /SignalR-Swift-Example/SignalR-Swift-Example/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "iphone", 5 | "size" : "20x20", 6 | "scale" : "2x" 7 | }, 8 | { 9 | "idiom" : "iphone", 10 | "size" : "20x20", 11 | "scale" : "3x" 12 | }, 13 | { 14 | "idiom" : "iphone", 15 | "size" : "29x29", 16 | "scale" : "2x" 17 | }, 18 | { 19 | "idiom" : "iphone", 20 | "size" : "29x29", 21 | "scale" : "3x" 22 | }, 23 | { 24 | "idiom" : "iphone", 25 | "size" : "40x40", 26 | "scale" : "2x" 27 | }, 28 | { 29 | "idiom" : "iphone", 30 | "size" : "40x40", 31 | "scale" : "3x" 32 | }, 33 | { 34 | "idiom" : "iphone", 35 | "size" : "60x60", 36 | "scale" : "2x" 37 | }, 38 | { 39 | "idiom" : "iphone", 40 | "size" : "60x60", 41 | "scale" : "3x" 42 | }, 43 | { 44 | "idiom" : "ipad", 45 | "size" : "20x20", 46 | "scale" : "1x" 47 | }, 48 | { 49 | "idiom" : "ipad", 50 | "size" : "20x20", 51 | "scale" : "2x" 52 | }, 53 | { 54 | "idiom" : "ipad", 55 | "size" : "29x29", 56 | "scale" : "1x" 57 | }, 58 | { 59 | "idiom" : "ipad", 60 | "size" : "29x29", 61 | "scale" : "2x" 62 | }, 63 | { 64 | "idiom" : "ipad", 65 | "size" : "40x40", 66 | "scale" : "1x" 67 | }, 68 | { 69 | "idiom" : "ipad", 70 | "size" : "40x40", 71 | "scale" : "2x" 72 | }, 73 | { 74 | "idiom" : "ipad", 75 | "size" : "76x76", 76 | "scale" : "1x" 77 | }, 78 | { 79 | "idiom" : "ipad", 80 | "size" : "76x76", 81 | "scale" : "2x" 82 | }, 83 | { 84 | "idiom" : "ipad", 85 | "size" : "83.5x83.5", 86 | "scale" : "2x" 87 | }, 88 | { 89 | "idiom" : "ios-marketing", 90 | "size" : "1024x1024", 91 | "scale" : "1x" 92 | } 93 | ], 94 | "info" : { 95 | "version" : 1, 96 | "author" : "xcode" 97 | } 98 | } -------------------------------------------------------------------------------- /SignalR-Swift-Example/SignalR-Swift-Example/Base.lproj/LaunchScreen.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /SignalR-Swift-Example/SignalR-Swift-Example/Base.lproj/Main.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 74 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | -------------------------------------------------------------------------------- /SignalR-Swift-Example/SignalR-Swift-Example/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | APPL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleVersion 20 | 1 21 | LSRequiresIPhoneOS 22 | 23 | UILaunchStoryboardName 24 | LaunchScreen 25 | UIMainStoryboardFile 26 | Main 27 | UIRequiredDeviceCapabilities 28 | 29 | armv7 30 | 31 | UISupportedInterfaceOrientations 32 | 33 | UIInterfaceOrientationPortrait 34 | UIInterfaceOrientationLandscapeLeft 35 | UIInterfaceOrientationLandscapeRight 36 | 37 | UISupportedInterfaceOrientations~ipad 38 | 39 | UIInterfaceOrientationPortrait 40 | UIInterfaceOrientationPortraitUpsideDown 41 | UIInterfaceOrientationLandscapeLeft 42 | UIInterfaceOrientationLandscapeRight 43 | 44 | NSAppTransportSecurity 45 | 46 | NSAllowsArbitraryLoads 47 | 48 | 49 | 50 | 51 | -------------------------------------------------------------------------------- /SignalR-Swift-Example/SignalR-Swift-Example/ViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ViewController.swift 3 | // SignalR-Swift-Example 4 | // 5 | // Created by Jordan Camara on 1/12/17. 6 | // Copyright © 2017 Jordan Camara. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | import SignalRSwift 11 | 12 | class ViewController: UIViewController { 13 | 14 | @IBOutlet weak var sendButton: UIButton! 15 | @IBOutlet weak var messageTextField: UITextField! 16 | @IBOutlet weak var chatTextView: UITextView! 17 | @IBOutlet weak var statusLabel: UILabel! 18 | @IBOutlet weak var startButton: UIBarButtonItem! 19 | 20 | var chatHub: HubProxy! 21 | var connection: HubConnection! 22 | var name: String! 23 | 24 | override func viewDidLoad() { 25 | super.viewDidLoad() 26 | 27 | connection = HubConnection(withUrl: "http://swiftr.azurewebsites.net") //SignalR("http://swiftr.azurewebsites.net") 28 | // connection.signalRVersion = .v2_2_0 29 | 30 | chatHub = self.connection.createHubProxy(hubName: "chatHub") 31 | _ = chatHub.on(eventName: "broadcastMessage") { (args) in 32 | if let name = args[0] as? String, let message = args[1] as? String, let text = self.chatTextView.text { 33 | self.chatTextView.text = "\(text)\n\n\(name): \(message)" 34 | } 35 | } 36 | 37 | // SignalR events 38 | 39 | connection.started = { [unowned self] in 40 | self.statusLabel.text = "Connected" 41 | self.startButton.isEnabled = true 42 | self.startButton.title = "Stop" 43 | self.sendButton.isEnabled = true 44 | } 45 | 46 | connection.reconnecting = { [unowned self] in 47 | self.statusLabel.text = "Reconnecting..." 48 | } 49 | 50 | connection.reconnected = { [unowned self] in 51 | self.statusLabel.text = "Reconnected. Connection ID: \(self.connection!.connectionId!)" 52 | self.startButton.isEnabled = true 53 | self.startButton.title = "Stop" 54 | self.sendButton.isEnabled = true 55 | } 56 | 57 | connection.closed = { [unowned self] in 58 | self.statusLabel.text = "Disconnected" 59 | self.startButton.isEnabled = true 60 | self.startButton.title = "Start" 61 | self.sendButton.isEnabled = false 62 | } 63 | 64 | connection.connectionSlow = { print("Connection slow...") } 65 | 66 | connection.error = { [unowned self] error in 67 | let anError = error as NSError 68 | if anError.code == NSURLErrorTimedOut { 69 | self.connection.start() 70 | } 71 | } 72 | 73 | connection.start() 74 | } 75 | 76 | override func viewDidAppear(_ animated: Bool) { 77 | let alertController = UIAlertController(title: "Name", message: "Please enter your name", preferredStyle: .alert) 78 | 79 | let okAction = UIAlertAction(title: "OK", style: .default) { [weak self] _ in 80 | self?.name = alertController.textFields?.first?.text 81 | 82 | if let name = self?.name , name.isEmpty { 83 | self?.name = "Anonymous" 84 | } 85 | 86 | alertController.textFields?.first?.resignFirstResponder() 87 | } 88 | 89 | alertController.addTextField { textField in 90 | textField.placeholder = "Your Name" 91 | } 92 | 93 | alertController.addAction(okAction) 94 | present(alertController, animated: true, completion: nil) 95 | } 96 | 97 | override func didReceiveMemoryWarning() { 98 | super.didReceiveMemoryWarning() 99 | // Dispose of any resources that can be recreated. 100 | } 101 | 102 | @IBAction func send(_ sender: AnyObject?) { 103 | if let hub = chatHub, let message = messageTextField.text { 104 | hub.invoke(method: "send", withArgs: [name, message]) 105 | } 106 | messageTextField.resignFirstResponder() 107 | } 108 | 109 | @IBAction func startStop(_ sender: AnyObject?) { 110 | if startButton.title == "Start" { 111 | connection.start() 112 | } else { 113 | connection.stop() 114 | } 115 | } 116 | 117 | /* 118 | // MARK: - Navigation 119 | 120 | // In a storyboard-based application, you will often want to do a little preparation before navigation 121 | override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) { 122 | // Get the new view controller using segue.destinationViewController. 123 | // Pass the selected object to the new view controller. 124 | } 125 | */ 126 | 127 | } 128 | -------------------------------------------------------------------------------- /SignalR-Swift-Example/SignalR-Swift-ExampleTests/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | BNDL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleVersion 20 | 1 21 | 22 | 23 | -------------------------------------------------------------------------------- /SignalR-Swift-Example/SignalR-Swift-ExampleTests/SignalR_Swift_ExampleTests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SignalR_Swift_ExampleTests.swift 3 | // SignalR-Swift-ExampleTests 4 | // 5 | // Created by Jordan Camara on 1/12/17. 6 | // Copyright © 2017 Jordan Camara. All rights reserved. 7 | // 8 | 9 | import XCTest 10 | @testable import SignalR_Swift_Example 11 | 12 | class SignalR_Swift_ExampleTests: XCTestCase { 13 | 14 | override func setUp() { 15 | super.setUp() 16 | // Put setup code here. This method is called before the invocation of each test method in the class. 17 | } 18 | 19 | override func tearDown() { 20 | // Put teardown code here. This method is called after the invocation of each test method in the class. 21 | super.tearDown() 22 | } 23 | 24 | func testExample() { 25 | // This is an example of a functional test case. 26 | // Use XCTAssert and related functions to verify your tests produce the correct results. 27 | } 28 | 29 | func testPerformanceExample() { 30 | // This is an example of a performance test case. 31 | self.measure { 32 | // Put the code you want to measure the time of here. 33 | } 34 | } 35 | 36 | } 37 | -------------------------------------------------------------------------------- /SignalR-Swift.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 46; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 03D89CA2D29AD16BE22FB404 /* Pods_SignalRSwift.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = E3DD50F71B5A709F5EC093E9 /* Pods_SignalRSwift.framework */; }; 11 | 563B87161F1FA11100D75CEF /* ServerSentEventsTransport.swift in Sources */ = {isa = PBXBuildFile; fileRef = 563B87151F1FA11100D75CEF /* ServerSentEventsTransport.swift */; }; 12 | 568A77191F223CB2004F5D1C /* ChunkBuffer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 568A77181F223CB2004F5D1C /* ChunkBuffer.swift */; }; 13 | 568A771B1F223CF0004F5D1C /* ServerSentEvent.swift in Sources */ = {isa = PBXBuildFile; fileRef = 568A771A1F223CF0004F5D1C /* ServerSentEvent.swift */; }; 14 | AA049197DB8438E88C7B3D5D /* Pods_SignalR_SwiftTests.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 291C54976E982A542BA4B4C3 /* Pods_SignalR_SwiftTests.framework */; }; 15 | C91154631E24113F007C0DD9 /* WebSocketTransport.swift in Sources */ = {isa = PBXBuildFile; fileRef = C91154621E24113F007C0DD9 /* WebSocketTransport.swift */; }; 16 | C91154661E241239007C0DD9 /* WebSocketConnectionInfo.swift in Sources */ = {isa = PBXBuildFile; fileRef = C91154651E241239007C0DD9 /* WebSocketConnectionInfo.swift */; }; 17 | C91154681E243082007C0DD9 /* ReceivedMessage.swift in Sources */ = {isa = PBXBuildFile; fileRef = C91154671E243082007C0DD9 /* ReceivedMessage.swift */; }; 18 | C911546A1E253B1B007C0DD9 /* LongPollingTransport.swift in Sources */ = {isa = PBXBuildFile; fileRef = C91154691E253B1B007C0DD9 /* LongPollingTransport.swift */; }; 19 | C911546C1E255CB2007C0DD9 /* ExceptionHelper.swift in Sources */ = {isa = PBXBuildFile; fileRef = C911546B1E255CB2007C0DD9 /* ExceptionHelper.swift */; }; 20 | C911546F1E256491007C0DD9 /* HubConnectionProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = C911546E1E256491007C0DD9 /* HubConnectionProtocol.swift */; }; 21 | C91154711E2564C1007C0DD9 /* HubResult.swift in Sources */ = {isa = PBXBuildFile; fileRef = C91154701E2564C1007C0DD9 /* HubResult.swift */; }; 22 | C91154731E2566DC007C0DD9 /* HubConnection.swift in Sources */ = {isa = PBXBuildFile; fileRef = C91154721E2566DC007C0DD9 /* HubConnection.swift */; }; 23 | C91154751E256721007C0DD9 /* HubProxy.swift in Sources */ = {isa = PBXBuildFile; fileRef = C91154741E256721007C0DD9 /* HubProxy.swift */; }; 24 | C91154771E256741007C0DD9 /* HubProxyProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = C91154761E256741007C0DD9 /* HubProxyProtocol.swift */; }; 25 | C911547B1E256F98007C0DD9 /* HubInvocation.swift in Sources */ = {isa = PBXBuildFile; fileRef = C911547A1E256F98007C0DD9 /* HubInvocation.swift */; }; 26 | C92AD2591E2EBCB9009686E2 /* Dictionary+Json.swift in Sources */ = {isa = PBXBuildFile; fileRef = C92AD2581E2EBCB9009686E2 /* Dictionary+Json.swift */; }; 27 | C9330B321E1FE59A00B1F2D5 /* SignalRSwift.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = C9330B281E1FE59A00B1F2D5 /* SignalRSwift.framework */; }; 28 | C9330B391E1FE59A00B1F2D5 /* SignalR-Swift.h in Headers */ = {isa = PBXBuildFile; fileRef = C9330B2B1E1FE59A00B1F2D5 /* SignalR-Swift.h */; settings = {ATTRIBUTES = (Public, ); }; }; 29 | C944563A1E76C02F000FF744 /* ConnectionTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = C94456391E76C02F000FF744 /* ConnectionTests.swift */; }; 30 | C944563C1E76C555000FF744 /* MockClientTransport.swift in Sources */ = {isa = PBXBuildFile; fileRef = C944563B1E76C555000FF744 /* MockClientTransport.swift */; }; 31 | C9B9229D1E295AD7007C2266 /* String+Dictionary.swift in Sources */ = {isa = PBXBuildFile; fileRef = C9B9229C1E295AD7007C2266 /* String+Dictionary.swift */; }; 32 | C9E1C9F81E1FEEF800335B1E /* Connection.swift in Sources */ = {isa = PBXBuildFile; fileRef = C9E1C9F71E1FEEF800335B1E /* Connection.swift */; }; 33 | C9E1C9FB1E1FF03400335B1E /* ConnectionState.swift in Sources */ = {isa = PBXBuildFile; fileRef = C9E1C9FA1E1FF03400335B1E /* ConnectionState.swift */; }; 34 | C9E1CA041E1FF3B700335B1E /* Version.swift in Sources */ = {isa = PBXBuildFile; fileRef = C9E1CA031E1FF3B700335B1E /* Version.swift */; }; 35 | C9E1CA061E1FFADA00335B1E /* HeartbeatMonitor.swift in Sources */ = {isa = PBXBuildFile; fileRef = C9E1CA051E1FFADA00335B1E /* HeartbeatMonitor.swift */; }; 36 | C9E1CA081E1FFB0C00335B1E /* ConnectionProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = C9E1CA071E1FFB0C00335B1E /* ConnectionProtocol.swift */; }; 37 | C9E1CA0A1E1FFB6600335B1E /* KeepAliveData.swift in Sources */ = {isa = PBXBuildFile; fileRef = C9E1CA091E1FFB6600335B1E /* KeepAliveData.swift */; }; 38 | C9E1CA0C1E1FFDA800335B1E /* ClientTransportProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = C9E1CA0B1E1FFDA800335B1E /* ClientTransportProtocol.swift */; }; 39 | C9E1CA0E1E1FFE0400335B1E /* NegotiationResponse.swift in Sources */ = {isa = PBXBuildFile; fileRef = C9E1CA0D1E1FFE0400335B1E /* NegotiationResponse.swift */; }; 40 | C9FC2D951E2025B80027FE59 /* AutoTransport.swift in Sources */ = {isa = PBXBuildFile; fileRef = C9FC2D941E2025B80027FE59 /* AutoTransport.swift */; }; 41 | C9FC2D971E2025DB0027FE59 /* HttpTransport.swift in Sources */ = {isa = PBXBuildFile; fileRef = C9FC2D961E2025DB0027FE59 /* HttpTransport.swift */; }; 42 | C9FC2D991E23BC600027FE59 /* ConnectionDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = C9FC2D981E23BC600027FE59 /* ConnectionDelegate.swift */; }; 43 | /* End PBXBuildFile section */ 44 | 45 | /* Begin PBXContainerItemProxy section */ 46 | C9330B331E1FE59A00B1F2D5 /* PBXContainerItemProxy */ = { 47 | isa = PBXContainerItemProxy; 48 | containerPortal = C9330B1F1E1FE59A00B1F2D5 /* Project object */; 49 | proxyType = 1; 50 | remoteGlobalIDString = C9330B271E1FE59A00B1F2D5; 51 | remoteInfo = "SignalR-Swift"; 52 | }; 53 | /* End PBXContainerItemProxy section */ 54 | 55 | /* Begin PBXFileReference section */ 56 | 291C54976E982A542BA4B4C3 /* Pods_SignalR_SwiftTests.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_SignalR_SwiftTests.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 57 | 42E40FC4FECA749AD861D67E /* Pods-SignalRSwift.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-SignalRSwift.debug.xcconfig"; path = "Pods/Target Support Files/Pods-SignalRSwift/Pods-SignalRSwift.debug.xcconfig"; sourceTree = ""; }; 58 | 563B87151F1FA11100D75CEF /* ServerSentEventsTransport.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ServerSentEventsTransport.swift; sourceTree = ""; }; 59 | 568A77181F223CB2004F5D1C /* ChunkBuffer.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = ChunkBuffer.swift; path = "SignalR-Swift/Transports/ServerSentEvents/ChunkBuffer.swift"; sourceTree = SOURCE_ROOT; }; 60 | 568A771A1F223CF0004F5D1C /* ServerSentEvent.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = ServerSentEvent.swift; path = "SignalR-Swift/Transports/ServerSentEvents/ServerSentEvent.swift"; sourceTree = SOURCE_ROOT; }; 61 | 65B59CFA9FAF15B46BDCDEC1 /* Pods-SignalR-SwiftTests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-SignalR-SwiftTests.debug.xcconfig"; path = "Pods/Target Support Files/Pods-SignalR-SwiftTests/Pods-SignalR-SwiftTests.debug.xcconfig"; sourceTree = ""; }; 62 | B5E080DF752A22D635CE38CF /* Pods-SignalR-SwiftTests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-SignalR-SwiftTests.release.xcconfig"; path = "Pods/Target Support Files/Pods-SignalR-SwiftTests/Pods-SignalR-SwiftTests.release.xcconfig"; sourceTree = ""; }; 63 | B9B048FC932F5933F9447DCE /* Pods-SignalRSwift.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-SignalRSwift.release.xcconfig"; path = "Pods/Target Support Files/Pods-SignalRSwift/Pods-SignalRSwift.release.xcconfig"; sourceTree = ""; }; 64 | C91154621E24113F007C0DD9 /* WebSocketTransport.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = WebSocketTransport.swift; sourceTree = ""; }; 65 | C91154651E241239007C0DD9 /* WebSocketConnectionInfo.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = WebSocketConnectionInfo.swift; sourceTree = ""; }; 66 | C91154671E243082007C0DD9 /* ReceivedMessage.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ReceivedMessage.swift; sourceTree = ""; }; 67 | C91154691E253B1B007C0DD9 /* LongPollingTransport.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LongPollingTransport.swift; sourceTree = ""; }; 68 | C911546B1E255CB2007C0DD9 /* ExceptionHelper.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ExceptionHelper.swift; sourceTree = ""; }; 69 | C911546E1E256491007C0DD9 /* HubConnectionProtocol.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = HubConnectionProtocol.swift; sourceTree = ""; }; 70 | C91154701E2564C1007C0DD9 /* HubResult.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = HubResult.swift; sourceTree = ""; }; 71 | C91154721E2566DC007C0DD9 /* HubConnection.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = HubConnection.swift; sourceTree = ""; }; 72 | C91154741E256721007C0DD9 /* HubProxy.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = HubProxy.swift; sourceTree = ""; }; 73 | C91154761E256741007C0DD9 /* HubProxyProtocol.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = HubProxyProtocol.swift; sourceTree = ""; }; 74 | C911547A1E256F98007C0DD9 /* HubInvocation.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = HubInvocation.swift; sourceTree = ""; }; 75 | C92AD2581E2EBCB9009686E2 /* Dictionary+Json.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Dictionary+Json.swift"; sourceTree = ""; }; 76 | C9330B281E1FE59A00B1F2D5 /* SignalRSwift.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = SignalRSwift.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 77 | C9330B2B1E1FE59A00B1F2D5 /* SignalR-Swift.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "SignalR-Swift.h"; sourceTree = ""; }; 78 | C9330B2C1E1FE59A00B1F2D5 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 79 | C9330B311E1FE59A00B1F2D5 /* SignalR-SwiftTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = "SignalR-SwiftTests.xctest"; sourceTree = BUILT_PRODUCTS_DIR; }; 80 | C9330B381E1FE59A00B1F2D5 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 81 | C94456391E76C02F000FF744 /* ConnectionTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = ConnectionTests.swift; path = "SignalR-SwiftTests/Connection/ConnectionTests.swift"; sourceTree = SOURCE_ROOT; }; 82 | C944563B1E76C555000FF744 /* MockClientTransport.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MockClientTransport.swift; sourceTree = ""; }; 83 | C9B9229C1E295AD7007C2266 /* String+Dictionary.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "String+Dictionary.swift"; sourceTree = ""; }; 84 | C9E1C9F71E1FEEF800335B1E /* Connection.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Connection.swift; sourceTree = ""; }; 85 | C9E1C9FA1E1FF03400335B1E /* ConnectionState.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ConnectionState.swift; sourceTree = ""; }; 86 | C9E1CA031E1FF3B700335B1E /* Version.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Version.swift; sourceTree = ""; }; 87 | C9E1CA051E1FFADA00335B1E /* HeartbeatMonitor.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = HeartbeatMonitor.swift; sourceTree = ""; }; 88 | C9E1CA071E1FFB0C00335B1E /* ConnectionProtocol.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ConnectionProtocol.swift; sourceTree = ""; }; 89 | C9E1CA091E1FFB6600335B1E /* KeepAliveData.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = KeepAliveData.swift; sourceTree = ""; }; 90 | C9E1CA0B1E1FFDA800335B1E /* ClientTransportProtocol.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ClientTransportProtocol.swift; sourceTree = ""; }; 91 | C9E1CA0D1E1FFE0400335B1E /* NegotiationResponse.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NegotiationResponse.swift; sourceTree = ""; }; 92 | C9FC2D941E2025B80027FE59 /* AutoTransport.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AutoTransport.swift; sourceTree = ""; }; 93 | C9FC2D961E2025DB0027FE59 /* HttpTransport.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = HttpTransport.swift; sourceTree = ""; }; 94 | C9FC2D981E23BC600027FE59 /* ConnectionDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ConnectionDelegate.swift; sourceTree = ""; }; 95 | E3DD50F71B5A709F5EC093E9 /* Pods_SignalRSwift.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_SignalRSwift.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 96 | /* End PBXFileReference section */ 97 | 98 | /* Begin PBXFrameworksBuildPhase section */ 99 | C9330B241E1FE59A00B1F2D5 /* Frameworks */ = { 100 | isa = PBXFrameworksBuildPhase; 101 | buildActionMask = 2147483647; 102 | files = ( 103 | 03D89CA2D29AD16BE22FB404 /* Pods_SignalRSwift.framework in Frameworks */, 104 | ); 105 | runOnlyForDeploymentPostprocessing = 0; 106 | }; 107 | C9330B2E1E1FE59A00B1F2D5 /* Frameworks */ = { 108 | isa = PBXFrameworksBuildPhase; 109 | buildActionMask = 2147483647; 110 | files = ( 111 | C9330B321E1FE59A00B1F2D5 /* SignalRSwift.framework in Frameworks */, 112 | AA049197DB8438E88C7B3D5D /* Pods_SignalR_SwiftTests.framework in Frameworks */, 113 | ); 114 | runOnlyForDeploymentPostprocessing = 0; 115 | }; 116 | /* End PBXFrameworksBuildPhase section */ 117 | 118 | /* Begin PBXGroup section */ 119 | 316A9BF3DEFAB629DD2C78FD /* Frameworks */ = { 120 | isa = PBXGroup; 121 | children = ( 122 | E3DD50F71B5A709F5EC093E9 /* Pods_SignalRSwift.framework */, 123 | 291C54976E982A542BA4B4C3 /* Pods_SignalR_SwiftTests.framework */, 124 | ); 125 | name = Frameworks; 126 | sourceTree = ""; 127 | }; 128 | 5686CBB81F7C03B3008CA187 /* ServerSentEvents */ = { 129 | isa = PBXGroup; 130 | children = ( 131 | 568A77181F223CB2004F5D1C /* ChunkBuffer.swift */, 132 | 568A771A1F223CF0004F5D1C /* ServerSentEvent.swift */, 133 | ); 134 | path = ServerSentEvents; 135 | sourceTree = ""; 136 | }; 137 | C4B90292188059B3970F0902 /* Pods */ = { 138 | isa = PBXGroup; 139 | children = ( 140 | 65B59CFA9FAF15B46BDCDEC1 /* Pods-SignalR-SwiftTests.debug.xcconfig */, 141 | B5E080DF752A22D635CE38CF /* Pods-SignalR-SwiftTests.release.xcconfig */, 142 | 42E40FC4FECA749AD861D67E /* Pods-SignalRSwift.debug.xcconfig */, 143 | B9B048FC932F5933F9447DCE /* Pods-SignalRSwift.release.xcconfig */, 144 | ); 145 | name = Pods; 146 | sourceTree = ""; 147 | }; 148 | C911545F1E23D205007C0DD9 /* Models */ = { 149 | isa = PBXGroup; 150 | children = ( 151 | C91154671E243082007C0DD9 /* ReceivedMessage.swift */, 152 | ); 153 | path = Models; 154 | sourceTree = ""; 155 | }; 156 | C91154641E24122C007C0DD9 /* WebSockets */ = { 157 | isa = PBXGroup; 158 | children = ( 159 | C91154651E241239007C0DD9 /* WebSocketConnectionInfo.swift */, 160 | ); 161 | path = WebSockets; 162 | sourceTree = ""; 163 | }; 164 | C911546D1E25643D007C0DD9 /* Hubs */ = { 165 | isa = PBXGroup; 166 | children = ( 167 | C91154701E2564C1007C0DD9 /* HubResult.swift */, 168 | C91154721E2566DC007C0DD9 /* HubConnection.swift */, 169 | C91154741E256721007C0DD9 /* HubProxy.swift */, 170 | C911547A1E256F98007C0DD9 /* HubInvocation.swift */, 171 | ); 172 | path = Hubs; 173 | sourceTree = ""; 174 | }; 175 | C9330B1E1E1FE59A00B1F2D5 = { 176 | isa = PBXGroup; 177 | children = ( 178 | C9330B2A1E1FE59A00B1F2D5 /* SignalR-Swift */, 179 | C9330B351E1FE59A00B1F2D5 /* SignalR-SwiftTests */, 180 | C9330B291E1FE59A00B1F2D5 /* Products */, 181 | C4B90292188059B3970F0902 /* Pods */, 182 | 316A9BF3DEFAB629DD2C78FD /* Frameworks */, 183 | ); 184 | sourceTree = ""; 185 | }; 186 | C9330B291E1FE59A00B1F2D5 /* Products */ = { 187 | isa = PBXGroup; 188 | children = ( 189 | C9330B281E1FE59A00B1F2D5 /* SignalRSwift.framework */, 190 | C9330B311E1FE59A00B1F2D5 /* SignalR-SwiftTests.xctest */, 191 | ); 192 | name = Products; 193 | sourceTree = ""; 194 | }; 195 | C9330B2A1E1FE59A00B1F2D5 /* SignalR-Swift */ = { 196 | isa = PBXGroup; 197 | children = ( 198 | C9B9229B1E295A78007C2266 /* Extensions */, 199 | C911545F1E23D205007C0DD9 /* Models */, 200 | C9E1C9F61E1FEEBE00335B1E /* Client */, 201 | C9FC2D931E2025990027FE59 /* Transports */, 202 | C9330B2B1E1FE59A00B1F2D5 /* SignalR-Swift.h */, 203 | C9330B2C1E1FE59A00B1F2D5 /* Info.plist */, 204 | ); 205 | path = "SignalR-Swift"; 206 | sourceTree = ""; 207 | }; 208 | C9330B351E1FE59A00B1F2D5 /* SignalR-SwiftTests */ = { 209 | isa = PBXGroup; 210 | children = ( 211 | C94456361E76BFCB000FF744 /* Mocks */, 212 | C94456371E76BFCB000FF744 /* Transport */, 213 | C94456381E76BFCB000FF744 /* Connection */, 214 | C9330B381E1FE59A00B1F2D5 /* Info.plist */, 215 | ); 216 | path = "SignalR-SwiftTests"; 217 | sourceTree = ""; 218 | }; 219 | C94456361E76BFCB000FF744 /* Mocks */ = { 220 | isa = PBXGroup; 221 | children = ( 222 | C944563B1E76C555000FF744 /* MockClientTransport.swift */, 223 | ); 224 | path = Mocks; 225 | sourceTree = ""; 226 | }; 227 | C94456371E76BFCB000FF744 /* Transport */ = { 228 | isa = PBXGroup; 229 | children = ( 230 | ); 231 | path = Transport; 232 | sourceTree = ""; 233 | }; 234 | C94456381E76BFCB000FF744 /* Connection */ = { 235 | isa = PBXGroup; 236 | children = ( 237 | C94456391E76C02F000FF744 /* ConnectionTests.swift */, 238 | ); 239 | path = Connection; 240 | sourceTree = ""; 241 | }; 242 | C9B9229B1E295A78007C2266 /* Extensions */ = { 243 | isa = PBXGroup; 244 | children = ( 245 | C9B9229C1E295AD7007C2266 /* String+Dictionary.swift */, 246 | C92AD2581E2EBCB9009686E2 /* Dictionary+Json.swift */, 247 | ); 248 | path = Extensions; 249 | sourceTree = ""; 250 | }; 251 | C9E1C9F61E1FEEBE00335B1E /* Client */ = { 252 | isa = PBXGroup; 253 | children = ( 254 | C911546D1E25643D007C0DD9 /* Hubs */, 255 | C9E1CA021E1FF3B700335B1E /* Infrastructure */, 256 | C9E1C9F91E1FF02200335B1E /* Enums */, 257 | C9E1C9FC1E1FF1B100335B1E /* Protocols */, 258 | C9E1C9F71E1FEEF800335B1E /* Connection.swift */, 259 | C9E1CA051E1FFADA00335B1E /* HeartbeatMonitor.swift */, 260 | C9E1CA091E1FFB6600335B1E /* KeepAliveData.swift */, 261 | C9E1CA0D1E1FFE0400335B1E /* NegotiationResponse.swift */, 262 | ); 263 | path = Client; 264 | sourceTree = ""; 265 | }; 266 | C9E1C9F91E1FF02200335B1E /* Enums */ = { 267 | isa = PBXGroup; 268 | children = ( 269 | C9E1C9FA1E1FF03400335B1E /* ConnectionState.swift */, 270 | ); 271 | path = Enums; 272 | sourceTree = ""; 273 | }; 274 | C9E1C9FC1E1FF1B100335B1E /* Protocols */ = { 275 | isa = PBXGroup; 276 | children = ( 277 | C9E1CA071E1FFB0C00335B1E /* ConnectionProtocol.swift */, 278 | C9E1CA0B1E1FFDA800335B1E /* ClientTransportProtocol.swift */, 279 | C9FC2D981E23BC600027FE59 /* ConnectionDelegate.swift */, 280 | C911546E1E256491007C0DD9 /* HubConnectionProtocol.swift */, 281 | C91154761E256741007C0DD9 /* HubProxyProtocol.swift */, 282 | ); 283 | path = Protocols; 284 | sourceTree = ""; 285 | }; 286 | C9E1CA021E1FF3B700335B1E /* Infrastructure */ = { 287 | isa = PBXGroup; 288 | children = ( 289 | C9E1CA031E1FF3B700335B1E /* Version.swift */, 290 | C911546B1E255CB2007C0DD9 /* ExceptionHelper.swift */, 291 | ); 292 | path = Infrastructure; 293 | sourceTree = ""; 294 | }; 295 | C9FC2D931E2025990027FE59 /* Transports */ = { 296 | isa = PBXGroup; 297 | children = ( 298 | 5686CBB81F7C03B3008CA187 /* ServerSentEvents */, 299 | C91154641E24122C007C0DD9 /* WebSockets */, 300 | C9FC2D941E2025B80027FE59 /* AutoTransport.swift */, 301 | C9FC2D961E2025DB0027FE59 /* HttpTransport.swift */, 302 | C91154621E24113F007C0DD9 /* WebSocketTransport.swift */, 303 | C91154691E253B1B007C0DD9 /* LongPollingTransport.swift */, 304 | 563B87151F1FA11100D75CEF /* ServerSentEventsTransport.swift */, 305 | ); 306 | path = Transports; 307 | sourceTree = ""; 308 | }; 309 | /* End PBXGroup section */ 310 | 311 | /* Begin PBXHeadersBuildPhase section */ 312 | C9330B251E1FE59A00B1F2D5 /* Headers */ = { 313 | isa = PBXHeadersBuildPhase; 314 | buildActionMask = 2147483647; 315 | files = ( 316 | C9330B391E1FE59A00B1F2D5 /* SignalR-Swift.h in Headers */, 317 | ); 318 | runOnlyForDeploymentPostprocessing = 0; 319 | }; 320 | /* End PBXHeadersBuildPhase section */ 321 | 322 | /* Begin PBXNativeTarget section */ 323 | C9330B271E1FE59A00B1F2D5 /* SignalRSwift */ = { 324 | isa = PBXNativeTarget; 325 | buildConfigurationList = C9330B3C1E1FE59A00B1F2D5 /* Build configuration list for PBXNativeTarget "SignalRSwift" */; 326 | buildPhases = ( 327 | 8893B2F1770B24873610B1CA /* [CP] Check Pods Manifest.lock */, 328 | C9330B231E1FE59A00B1F2D5 /* Sources */, 329 | C9330B241E1FE59A00B1F2D5 /* Frameworks */, 330 | C9330B251E1FE59A00B1F2D5 /* Headers */, 331 | C9330B261E1FE59A00B1F2D5 /* Resources */, 332 | BE9A67E61EA448B742E04260 /* [CP] Copy Pods Resources */, 333 | ); 334 | buildRules = ( 335 | ); 336 | dependencies = ( 337 | ); 338 | name = SignalRSwift; 339 | productName = "SignalR-Swift"; 340 | productReference = C9330B281E1FE59A00B1F2D5 /* SignalRSwift.framework */; 341 | productType = "com.apple.product-type.framework"; 342 | }; 343 | C9330B301E1FE59A00B1F2D5 /* SignalR-SwiftTests */ = { 344 | isa = PBXNativeTarget; 345 | buildConfigurationList = C9330B3F1E1FE59A00B1F2D5 /* Build configuration list for PBXNativeTarget "SignalR-SwiftTests" */; 346 | buildPhases = ( 347 | F71AD12E7750A42FE3671D48 /* [CP] Check Pods Manifest.lock */, 348 | C9330B2D1E1FE59A00B1F2D5 /* Sources */, 349 | C9330B2E1E1FE59A00B1F2D5 /* Frameworks */, 350 | C9330B2F1E1FE59A00B1F2D5 /* Resources */, 351 | 1408BA98AFE158F7832B6F37 /* [CP] Embed Pods Frameworks */, 352 | E29934DC4C7132EA079822B1 /* [CP] Copy Pods Resources */, 353 | ); 354 | buildRules = ( 355 | ); 356 | dependencies = ( 357 | C9330B341E1FE59A00B1F2D5 /* PBXTargetDependency */, 358 | ); 359 | name = "SignalR-SwiftTests"; 360 | productName = "SignalR-SwiftTests"; 361 | productReference = C9330B311E1FE59A00B1F2D5 /* SignalR-SwiftTests.xctest */; 362 | productType = "com.apple.product-type.bundle.unit-test"; 363 | }; 364 | /* End PBXNativeTarget section */ 365 | 366 | /* Begin PBXProject section */ 367 | C9330B1F1E1FE59A00B1F2D5 /* Project object */ = { 368 | isa = PBXProject; 369 | attributes = { 370 | LastSwiftUpdateCheck = 0820; 371 | LastUpgradeCheck = 0820; 372 | ORGANIZATIONNAME = "Jordan Camara"; 373 | TargetAttributes = { 374 | C9330B271E1FE59A00B1F2D5 = { 375 | CreatedOnToolsVersion = 8.2.1; 376 | DevelopmentTeam = 8Z35TU97D6; 377 | LastSwiftMigration = 0820; 378 | ProvisioningStyle = Automatic; 379 | }; 380 | C9330B301E1FE59A00B1F2D5 = { 381 | CreatedOnToolsVersion = 8.2.1; 382 | DevelopmentTeam = 8Z35TU97D6; 383 | LastSwiftMigration = 0820; 384 | ProvisioningStyle = Automatic; 385 | }; 386 | }; 387 | }; 388 | buildConfigurationList = C9330B221E1FE59A00B1F2D5 /* Build configuration list for PBXProject "SignalR-Swift" */; 389 | compatibilityVersion = "Xcode 3.2"; 390 | developmentRegion = English; 391 | hasScannedForEncodings = 0; 392 | knownRegions = ( 393 | en, 394 | ); 395 | mainGroup = C9330B1E1E1FE59A00B1F2D5; 396 | productRefGroup = C9330B291E1FE59A00B1F2D5 /* Products */; 397 | projectDirPath = ""; 398 | projectRoot = ""; 399 | targets = ( 400 | C9330B271E1FE59A00B1F2D5 /* SignalRSwift */, 401 | C9330B301E1FE59A00B1F2D5 /* SignalR-SwiftTests */, 402 | ); 403 | }; 404 | /* End PBXProject section */ 405 | 406 | /* Begin PBXResourcesBuildPhase section */ 407 | C9330B261E1FE59A00B1F2D5 /* Resources */ = { 408 | isa = PBXResourcesBuildPhase; 409 | buildActionMask = 2147483647; 410 | files = ( 411 | ); 412 | runOnlyForDeploymentPostprocessing = 0; 413 | }; 414 | C9330B2F1E1FE59A00B1F2D5 /* Resources */ = { 415 | isa = PBXResourcesBuildPhase; 416 | buildActionMask = 2147483647; 417 | files = ( 418 | ); 419 | runOnlyForDeploymentPostprocessing = 0; 420 | }; 421 | /* End PBXResourcesBuildPhase section */ 422 | 423 | /* Begin PBXShellScriptBuildPhase section */ 424 | 1408BA98AFE158F7832B6F37 /* [CP] Embed Pods Frameworks */ = { 425 | isa = PBXShellScriptBuildPhase; 426 | buildActionMask = 2147483647; 427 | files = ( 428 | ); 429 | inputPaths = ( 430 | "${SRCROOT}/Pods/Target Support Files/Pods-SignalR-SwiftTests/Pods-SignalR-SwiftTests-frameworks.sh", 431 | "${BUILT_PRODUCTS_DIR}/Mockit/Mockit.framework", 432 | "${BUILT_PRODUCTS_DIR}/Nimble/Nimble.framework", 433 | "${BUILT_PRODUCTS_DIR}/Quick/Quick.framework", 434 | "${BUILT_PRODUCTS_DIR}/Alamofire/Alamofire.framework", 435 | "${BUILT_PRODUCTS_DIR}/Starscream/Starscream.framework", 436 | ); 437 | name = "[CP] Embed Pods Frameworks"; 438 | outputPaths = ( 439 | "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/Mockit.framework", 440 | "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/Nimble.framework", 441 | "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/Quick.framework", 442 | "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/Alamofire.framework", 443 | "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/Starscream.framework", 444 | ); 445 | runOnlyForDeploymentPostprocessing = 0; 446 | shellPath = /bin/sh; 447 | shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods-SignalR-SwiftTests/Pods-SignalR-SwiftTests-frameworks.sh\"\n"; 448 | showEnvVarsInLog = 0; 449 | }; 450 | 8893B2F1770B24873610B1CA /* [CP] Check Pods Manifest.lock */ = { 451 | isa = PBXShellScriptBuildPhase; 452 | buildActionMask = 2147483647; 453 | files = ( 454 | ); 455 | inputPaths = ( 456 | "${PODS_PODFILE_DIR_PATH}/Podfile.lock", 457 | "${PODS_ROOT}/Manifest.lock", 458 | ); 459 | name = "[CP] Check Pods Manifest.lock"; 460 | outputPaths = ( 461 | "$(DERIVED_FILE_DIR)/Pods-SignalRSwift-checkManifestLockResult.txt", 462 | ); 463 | runOnlyForDeploymentPostprocessing = 0; 464 | shellPath = /bin/sh; 465 | shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; 466 | showEnvVarsInLog = 0; 467 | }; 468 | BE9A67E61EA448B742E04260 /* [CP] Copy Pods Resources */ = { 469 | isa = PBXShellScriptBuildPhase; 470 | buildActionMask = 2147483647; 471 | files = ( 472 | ); 473 | inputPaths = ( 474 | ); 475 | name = "[CP] Copy Pods Resources"; 476 | outputPaths = ( 477 | ); 478 | runOnlyForDeploymentPostprocessing = 0; 479 | shellPath = /bin/sh; 480 | shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods-SignalRSwift/Pods-SignalRSwift-resources.sh\"\n"; 481 | showEnvVarsInLog = 0; 482 | }; 483 | E29934DC4C7132EA079822B1 /* [CP] Copy Pods Resources */ = { 484 | isa = PBXShellScriptBuildPhase; 485 | buildActionMask = 2147483647; 486 | files = ( 487 | ); 488 | inputPaths = ( 489 | ); 490 | name = "[CP] Copy Pods Resources"; 491 | outputPaths = ( 492 | ); 493 | runOnlyForDeploymentPostprocessing = 0; 494 | shellPath = /bin/sh; 495 | shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods-SignalR-SwiftTests/Pods-SignalR-SwiftTests-resources.sh\"\n"; 496 | showEnvVarsInLog = 0; 497 | }; 498 | F71AD12E7750A42FE3671D48 /* [CP] Check Pods Manifest.lock */ = { 499 | isa = PBXShellScriptBuildPhase; 500 | buildActionMask = 2147483647; 501 | files = ( 502 | ); 503 | inputPaths = ( 504 | "${PODS_PODFILE_DIR_PATH}/Podfile.lock", 505 | "${PODS_ROOT}/Manifest.lock", 506 | ); 507 | name = "[CP] Check Pods Manifest.lock"; 508 | outputPaths = ( 509 | "$(DERIVED_FILE_DIR)/Pods-SignalR-SwiftTests-checkManifestLockResult.txt", 510 | ); 511 | runOnlyForDeploymentPostprocessing = 0; 512 | shellPath = /bin/sh; 513 | shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; 514 | showEnvVarsInLog = 0; 515 | }; 516 | /* End PBXShellScriptBuildPhase section */ 517 | 518 | /* Begin PBXSourcesBuildPhase section */ 519 | C9330B231E1FE59A00B1F2D5 /* Sources */ = { 520 | isa = PBXSourcesBuildPhase; 521 | buildActionMask = 2147483647; 522 | files = ( 523 | C9E1C9F81E1FEEF800335B1E /* Connection.swift in Sources */, 524 | C91154731E2566DC007C0DD9 /* HubConnection.swift in Sources */, 525 | C9B9229D1E295AD7007C2266 /* String+Dictionary.swift in Sources */, 526 | C9E1CA061E1FFADA00335B1E /* HeartbeatMonitor.swift in Sources */, 527 | 568A771B1F223CF0004F5D1C /* ServerSentEvent.swift in Sources */, 528 | C911546C1E255CB2007C0DD9 /* ExceptionHelper.swift in Sources */, 529 | 568A77191F223CB2004F5D1C /* ChunkBuffer.swift in Sources */, 530 | C9E1CA0A1E1FFB6600335B1E /* KeepAliveData.swift in Sources */, 531 | C91154711E2564C1007C0DD9 /* HubResult.swift in Sources */, 532 | C9E1CA0C1E1FFDA800335B1E /* ClientTransportProtocol.swift in Sources */, 533 | C9E1CA0E1E1FFE0400335B1E /* NegotiationResponse.swift in Sources */, 534 | C9E1CA041E1FF3B700335B1E /* Version.swift in Sources */, 535 | C911546A1E253B1B007C0DD9 /* LongPollingTransport.swift in Sources */, 536 | C92AD2591E2EBCB9009686E2 /* Dictionary+Json.swift in Sources */, 537 | C9E1C9FB1E1FF03400335B1E /* ConnectionState.swift in Sources */, 538 | C9FC2D971E2025DB0027FE59 /* HttpTransport.swift in Sources */, 539 | C91154661E241239007C0DD9 /* WebSocketConnectionInfo.swift in Sources */, 540 | C91154771E256741007C0DD9 /* HubProxyProtocol.swift in Sources */, 541 | C9FC2D951E2025B80027FE59 /* AutoTransport.swift in Sources */, 542 | C911546F1E256491007C0DD9 /* HubConnectionProtocol.swift in Sources */, 543 | 563B87161F1FA11100D75CEF /* ServerSentEventsTransport.swift in Sources */, 544 | C9E1CA081E1FFB0C00335B1E /* ConnectionProtocol.swift in Sources */, 545 | C91154681E243082007C0DD9 /* ReceivedMessage.swift in Sources */, 546 | C9FC2D991E23BC600027FE59 /* ConnectionDelegate.swift in Sources */, 547 | C91154751E256721007C0DD9 /* HubProxy.swift in Sources */, 548 | C91154631E24113F007C0DD9 /* WebSocketTransport.swift in Sources */, 549 | C911547B1E256F98007C0DD9 /* HubInvocation.swift in Sources */, 550 | ); 551 | runOnlyForDeploymentPostprocessing = 0; 552 | }; 553 | C9330B2D1E1FE59A00B1F2D5 /* Sources */ = { 554 | isa = PBXSourcesBuildPhase; 555 | buildActionMask = 2147483647; 556 | files = ( 557 | C944563C1E76C555000FF744 /* MockClientTransport.swift in Sources */, 558 | C944563A1E76C02F000FF744 /* ConnectionTests.swift in Sources */, 559 | ); 560 | runOnlyForDeploymentPostprocessing = 0; 561 | }; 562 | /* End PBXSourcesBuildPhase section */ 563 | 564 | /* Begin PBXTargetDependency section */ 565 | C9330B341E1FE59A00B1F2D5 /* PBXTargetDependency */ = { 566 | isa = PBXTargetDependency; 567 | target = C9330B271E1FE59A00B1F2D5 /* SignalRSwift */; 568 | targetProxy = C9330B331E1FE59A00B1F2D5 /* PBXContainerItemProxy */; 569 | }; 570 | /* End PBXTargetDependency section */ 571 | 572 | /* Begin XCBuildConfiguration section */ 573 | C9330B3A1E1FE59A00B1F2D5 /* Debug */ = { 574 | isa = XCBuildConfiguration; 575 | buildSettings = { 576 | ALWAYS_SEARCH_USER_PATHS = NO; 577 | CLANG_ANALYZER_NONNULL = YES; 578 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 579 | CLANG_CXX_LIBRARY = "libc++"; 580 | CLANG_ENABLE_MODULES = YES; 581 | CLANG_ENABLE_OBJC_ARC = YES; 582 | CLANG_WARN_BOOL_CONVERSION = YES; 583 | CLANG_WARN_CONSTANT_CONVERSION = YES; 584 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 585 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 586 | CLANG_WARN_EMPTY_BODY = YES; 587 | CLANG_WARN_ENUM_CONVERSION = YES; 588 | CLANG_WARN_INFINITE_RECURSION = YES; 589 | CLANG_WARN_INT_CONVERSION = YES; 590 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 591 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 592 | CLANG_WARN_UNREACHABLE_CODE = YES; 593 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 594 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 595 | COPY_PHASE_STRIP = NO; 596 | CURRENT_PROJECT_VERSION = 1; 597 | DEBUG_INFORMATION_FORMAT = dwarf; 598 | ENABLE_STRICT_OBJC_MSGSEND = YES; 599 | ENABLE_TESTABILITY = YES; 600 | GCC_C_LANGUAGE_STANDARD = gnu99; 601 | GCC_DYNAMIC_NO_PIC = NO; 602 | GCC_NO_COMMON_BLOCKS = YES; 603 | GCC_OPTIMIZATION_LEVEL = 0; 604 | GCC_PREPROCESSOR_DEFINITIONS = ( 605 | "DEBUG=1", 606 | "$(inherited)", 607 | ); 608 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 609 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 610 | GCC_WARN_UNDECLARED_SELECTOR = YES; 611 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 612 | GCC_WARN_UNUSED_FUNCTION = YES; 613 | GCC_WARN_UNUSED_VARIABLE = YES; 614 | IPHONEOS_DEPLOYMENT_TARGET = 10.3; 615 | MTL_ENABLE_DEBUG_INFO = YES; 616 | ONLY_ACTIVE_ARCH = YES; 617 | SDKROOT = iphoneos; 618 | SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; 619 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 620 | TARGETED_DEVICE_FAMILY = "1,2"; 621 | VERSIONING_SYSTEM = "apple-generic"; 622 | VERSION_INFO_PREFIX = ""; 623 | }; 624 | name = Debug; 625 | }; 626 | C9330B3B1E1FE59A00B1F2D5 /* Release */ = { 627 | isa = XCBuildConfiguration; 628 | buildSettings = { 629 | ALWAYS_SEARCH_USER_PATHS = NO; 630 | CLANG_ANALYZER_NONNULL = YES; 631 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 632 | CLANG_CXX_LIBRARY = "libc++"; 633 | CLANG_ENABLE_MODULES = YES; 634 | CLANG_ENABLE_OBJC_ARC = YES; 635 | CLANG_WARN_BOOL_CONVERSION = YES; 636 | CLANG_WARN_CONSTANT_CONVERSION = YES; 637 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 638 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 639 | CLANG_WARN_EMPTY_BODY = YES; 640 | CLANG_WARN_ENUM_CONVERSION = YES; 641 | CLANG_WARN_INFINITE_RECURSION = YES; 642 | CLANG_WARN_INT_CONVERSION = YES; 643 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 644 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 645 | CLANG_WARN_UNREACHABLE_CODE = YES; 646 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 647 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 648 | COPY_PHASE_STRIP = NO; 649 | CURRENT_PROJECT_VERSION = 1; 650 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 651 | ENABLE_NS_ASSERTIONS = NO; 652 | ENABLE_STRICT_OBJC_MSGSEND = YES; 653 | GCC_C_LANGUAGE_STANDARD = gnu99; 654 | GCC_NO_COMMON_BLOCKS = YES; 655 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 656 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 657 | GCC_WARN_UNDECLARED_SELECTOR = YES; 658 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 659 | GCC_WARN_UNUSED_FUNCTION = YES; 660 | GCC_WARN_UNUSED_VARIABLE = YES; 661 | IPHONEOS_DEPLOYMENT_TARGET = 10.3; 662 | MTL_ENABLE_DEBUG_INFO = NO; 663 | SDKROOT = iphoneos; 664 | SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; 665 | TARGETED_DEVICE_FAMILY = "1,2"; 666 | VALIDATE_PRODUCT = YES; 667 | VERSIONING_SYSTEM = "apple-generic"; 668 | VERSION_INFO_PREFIX = ""; 669 | }; 670 | name = Release; 671 | }; 672 | C9330B3D1E1FE59A00B1F2D5 /* Debug */ = { 673 | isa = XCBuildConfiguration; 674 | baseConfigurationReference = 42E40FC4FECA749AD861D67E /* Pods-SignalRSwift.debug.xcconfig */; 675 | buildSettings = { 676 | CLANG_ENABLE_MODULES = YES; 677 | CODE_SIGN_IDENTITY = ""; 678 | DEFINES_MODULE = YES; 679 | DEVELOPMENT_TEAM = 8Z35TU97D6; 680 | DYLIB_COMPATIBILITY_VERSION = 1; 681 | DYLIB_CURRENT_VERSION = 1; 682 | DYLIB_INSTALL_NAME_BASE = "@rpath"; 683 | INFOPLIST_FILE = "SignalR-Swift/Info.plist"; 684 | INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; 685 | IPHONEOS_DEPLOYMENT_TARGET = 10.3; 686 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 687 | PRODUCT_BUNDLE_IDENTIFIER = "com.autosoftdms.SignalR-Swift"; 688 | PRODUCT_NAME = "$(TARGET_NAME)"; 689 | SKIP_INSTALL = YES; 690 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 691 | SWIFT_VERSION = 4.0; 692 | }; 693 | name = Debug; 694 | }; 695 | C9330B3E1E1FE59A00B1F2D5 /* Release */ = { 696 | isa = XCBuildConfiguration; 697 | baseConfigurationReference = B9B048FC932F5933F9447DCE /* Pods-SignalRSwift.release.xcconfig */; 698 | buildSettings = { 699 | CLANG_ENABLE_MODULES = YES; 700 | CODE_SIGN_IDENTITY = ""; 701 | DEFINES_MODULE = YES; 702 | DEVELOPMENT_TEAM = 8Z35TU97D6; 703 | DYLIB_COMPATIBILITY_VERSION = 1; 704 | DYLIB_CURRENT_VERSION = 1; 705 | DYLIB_INSTALL_NAME_BASE = "@rpath"; 706 | INFOPLIST_FILE = "SignalR-Swift/Info.plist"; 707 | INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; 708 | IPHONEOS_DEPLOYMENT_TARGET = 10.3; 709 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 710 | PRODUCT_BUNDLE_IDENTIFIER = "com.autosoftdms.SignalR-Swift"; 711 | PRODUCT_NAME = "$(TARGET_NAME)"; 712 | SKIP_INSTALL = YES; 713 | SWIFT_VERSION = 4.0; 714 | }; 715 | name = Release; 716 | }; 717 | C9330B401E1FE59A00B1F2D5 /* Debug */ = { 718 | isa = XCBuildConfiguration; 719 | baseConfigurationReference = 65B59CFA9FAF15B46BDCDEC1 /* Pods-SignalR-SwiftTests.debug.xcconfig */; 720 | buildSettings = { 721 | ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; 722 | CLANG_ENABLE_MODULES = YES; 723 | DEVELOPMENT_TEAM = 8Z35TU97D6; 724 | INFOPLIST_FILE = "SignalR-SwiftTests/Info.plist"; 725 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 726 | PRODUCT_BUNDLE_IDENTIFIER = "com.autosoftdms.SignalR-SwiftTests"; 727 | PRODUCT_NAME = "$(TARGET_NAME)"; 728 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 729 | SWIFT_VERSION = 4.0; 730 | }; 731 | name = Debug; 732 | }; 733 | C9330B411E1FE59A00B1F2D5 /* Release */ = { 734 | isa = XCBuildConfiguration; 735 | baseConfigurationReference = B5E080DF752A22D635CE38CF /* Pods-SignalR-SwiftTests.release.xcconfig */; 736 | buildSettings = { 737 | ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; 738 | CLANG_ENABLE_MODULES = YES; 739 | DEVELOPMENT_TEAM = 8Z35TU97D6; 740 | INFOPLIST_FILE = "SignalR-SwiftTests/Info.plist"; 741 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 742 | PRODUCT_BUNDLE_IDENTIFIER = "com.autosoftdms.SignalR-SwiftTests"; 743 | PRODUCT_NAME = "$(TARGET_NAME)"; 744 | SWIFT_VERSION = 4.0; 745 | }; 746 | name = Release; 747 | }; 748 | /* End XCBuildConfiguration section */ 749 | 750 | /* Begin XCConfigurationList section */ 751 | C9330B221E1FE59A00B1F2D5 /* Build configuration list for PBXProject "SignalR-Swift" */ = { 752 | isa = XCConfigurationList; 753 | buildConfigurations = ( 754 | C9330B3A1E1FE59A00B1F2D5 /* Debug */, 755 | C9330B3B1E1FE59A00B1F2D5 /* Release */, 756 | ); 757 | defaultConfigurationIsVisible = 0; 758 | defaultConfigurationName = Release; 759 | }; 760 | C9330B3C1E1FE59A00B1F2D5 /* Build configuration list for PBXNativeTarget "SignalRSwift" */ = { 761 | isa = XCConfigurationList; 762 | buildConfigurations = ( 763 | C9330B3D1E1FE59A00B1F2D5 /* Debug */, 764 | C9330B3E1E1FE59A00B1F2D5 /* Release */, 765 | ); 766 | defaultConfigurationIsVisible = 0; 767 | defaultConfigurationName = Release; 768 | }; 769 | C9330B3F1E1FE59A00B1F2D5 /* Build configuration list for PBXNativeTarget "SignalR-SwiftTests" */ = { 770 | isa = XCConfigurationList; 771 | buildConfigurations = ( 772 | C9330B401E1FE59A00B1F2D5 /* Debug */, 773 | C9330B411E1FE59A00B1F2D5 /* Release */, 774 | ); 775 | defaultConfigurationIsVisible = 0; 776 | defaultConfigurationName = Release; 777 | }; 778 | /* End XCConfigurationList section */ 779 | }; 780 | rootObject = C9330B1F1E1FE59A00B1F2D5 /* Project object */; 781 | } 782 | -------------------------------------------------------------------------------- /SignalR-Swift.xcodeproj/xcuserdata/jordan_camara.xcuserdatad/xcschemes/SignalR-Swift.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 33 | 39 | 40 | 41 | 42 | 43 | 49 | 50 | 51 | 52 | 53 | 54 | 64 | 65 | 71 | 72 | 73 | 74 | 75 | 76 | 82 | 83 | 89 | 90 | 91 | 92 | 94 | 95 | 98 | 99 | 100 | -------------------------------------------------------------------------------- /SignalR-Swift.xcodeproj/xcuserdata/jordan_camara.xcuserdatad/xcschemes/xcschememanagement.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | SchemeUserState 6 | 7 | SignalR-Swift.xcscheme 8 | 9 | orderHint 10 | 0 11 | 12 | 13 | SuppressBuildableAutocreation 14 | 15 | C9330B271E1FE59A00B1F2D5 16 | 17 | primary 18 | 19 | 20 | C9330B301E1FE59A00B1F2D5 21 | 22 | primary 23 | 24 | 25 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /SignalR-Swift.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 9 | 10 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /SignalR-Swift/Client/Connection.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Connection.swift 3 | // SignalR-Swift 4 | // 5 | // 6 | // Copyright © 2017 Jordan Camara. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import UIKit 11 | import Alamofire 12 | 13 | public typealias ConnectionStartedClosure = (() -> ()) 14 | public typealias ConnectionReceivedClosure = ((Any) -> ()) 15 | public typealias ConnectionErrorClosure = ((Error) -> ()) 16 | public typealias ConnectionClosedClosure = (() -> ()) 17 | public typealias ConnectionReconnectingClosure = (() -> ()) 18 | public typealias ConnectionReconnectedClosure = (() -> ()) 19 | public typealias ConnectionStateChangedClosure = ((ConnectionState) -> ()) 20 | public typealias ConnectionConnectionSlowClosure = (() -> ()) 21 | 22 | public class Connection: ConnectionProtocol { 23 | var defaultAbortTimeout = 30.0 24 | var assemblyVersion: Version? 25 | var disconnectTimeout: Double? 26 | var disconnectTimeoutOperation: BlockOperation! 27 | 28 | public var state = ConnectionState.disconnected 29 | public var url: String 30 | 31 | public var items = [String : Any]() 32 | public let queryString: [String: String]? 33 | 34 | var connectionData: String? 35 | var monitor: HeartbeatMonitor? 36 | 37 | public var version = Version(major: 1, minor: 3) 38 | 39 | public var connectionId: String? 40 | public var connectionToken: String? 41 | public var groupsToken: String? 42 | public var messageId: String? 43 | 44 | public var headers = HTTPHeaders() 45 | public var keepAliveData: KeepAliveData? 46 | public var webSocketAllowsSelfSignedSSL = false 47 | public internal(set) var sessionManager: SessionManager 48 | 49 | public var transport: ClientTransportProtocol? 50 | public var transportConnectTimeout = 0.0 51 | 52 | public var started: ConnectionStartedClosure? 53 | public var received: ConnectionReceivedClosure? 54 | public var error: ConnectionErrorClosure? 55 | public var closed: ConnectionClosedClosure? 56 | public var reconnecting: ConnectionReconnectingClosure? 57 | public var reconnected: ConnectionReconnectedClosure? 58 | public var stateChanged: ConnectionStateChangedClosure? 59 | public var connectionSlow: ConnectionConnectionSlowClosure? 60 | 61 | weak var delegate: ConnectionDelegate? 62 | 63 | static func ensureReconnecting(connection: ConnectionProtocol) -> Bool { 64 | if connection.changeState(oldState: .connected, toState: .reconnecting) { 65 | connection.willReconnect() 66 | } 67 | 68 | return connection.state == .reconnecting 69 | } 70 | 71 | public init(withUrl url: String, queryString: [String: String]? = nil, sessionManager: SessionManager = .default) { 72 | self.url = url.hasSuffix("/") ? url : url.appending("/") 73 | self.queryString = queryString 74 | self.sessionManager = sessionManager 75 | } 76 | 77 | // MARK: - Connection management 78 | 79 | public func start() { 80 | self.start(transport: AutoTransport()) 81 | } 82 | 83 | public func start(transport: ClientTransportProtocol) { 84 | if !self.changeState(oldState: .disconnected, toState: .connecting) { 85 | return 86 | } 87 | 88 | self.monitor = HeartbeatMonitor(withConnection: self) 89 | self.transport = transport 90 | 91 | self.negotiate(transport: transport) 92 | } 93 | 94 | func negotiate(transport: ClientTransportProtocol) { 95 | self.connectionData = self.onSending() 96 | 97 | transport.negotiate(connection: self, connectionData: self.connectionData, completionHandler: { [unowned self] (response, error) in 98 | if let error = error { 99 | self.didReceiveError(error: error) 100 | self.stopButDoNotCallServer() 101 | return 102 | } 103 | 104 | defer { self.startTransport() } 105 | 106 | guard let response = response else { return } 107 | 108 | self.verifyProtocolVersion(versionString: response.protocolVersion) 109 | 110 | self.connectionId = response.connectionId 111 | self.connectionToken = response.connectionToken 112 | self.disconnectTimeout = response.disconnectTimeout 113 | 114 | if let transportTimeout = response.transportConnectTimeout { 115 | self.transportConnectTimeout += transportTimeout 116 | } 117 | 118 | if let keepAlive = response.keepAliveTimeout { 119 | self.keepAliveData = KeepAliveData(timeout: keepAlive) 120 | } 121 | }) 122 | } 123 | 124 | func startTransport() { 125 | self.transport?.start(connection: self, connectionData: self.connectionData, completionHandler: { [unowned self] (response, error) in 126 | if let error = error { 127 | self.didReceiveError(error: error) 128 | self.stopButDoNotCallServer() 129 | return 130 | } 131 | 132 | _ = self.changeState(oldState: .connecting, toState: .connected) 133 | 134 | if self.keepAliveData != nil, let transport = self.transport, transport.supportsKeepAlive { 135 | self.monitor?.start() 136 | } 137 | 138 | self.started?() 139 | self.delegate?.connectionDidOpen(connection: self) 140 | }) 141 | } 142 | 143 | public func changeState(oldState: ConnectionState, toState newState: ConnectionState) -> Bool { 144 | guard self.state == oldState else { /* invalid transition */ return false } 145 | 146 | self.state = newState 147 | self.stateChanged?(self.state) 148 | self.delegate?.connection(connection: self, didChangeState: oldState, newState: newState) 149 | 150 | return true 151 | } 152 | 153 | func verifyProtocolVersion(versionString: String) { 154 | if Version(string: versionString) != self.version { 155 | NSException.raise(.internalInconsistencyException, format: NSLocalizedString("Incompatible Protocol Version", comment: "internal inconsistency exception"), arguments: getVaList(["nil"])) 156 | } 157 | } 158 | 159 | func stopAndCallServer() { 160 | self.stop(withTimeout: self.defaultAbortTimeout) 161 | } 162 | 163 | func stopButDoNotCallServer() { 164 | self.stop(withTimeout: -1.0) 165 | } 166 | 167 | public func stop() { 168 | self.stopAndCallServer() 169 | } 170 | 171 | func stop(withTimeout timeout: Double) { 172 | guard self.state != .disconnected else { return } 173 | 174 | self.monitor?.stop() 175 | self.monitor = nil 176 | 177 | self.transport?.abort(connection: self, timeout: timeout, connectionData: self.connectionData) 178 | self.disconnect() 179 | 180 | self.transport = nil 181 | } 182 | 183 | public func disconnect() { 184 | guard self.state != .disconnected else { return } 185 | 186 | self.state = .disconnected 187 | 188 | self.monitor?.stop() 189 | self.monitor = nil 190 | 191 | // clear the state for this connection 192 | self.connectionId = nil 193 | self.connectionToken = nil 194 | self.groupsToken = nil 195 | self.messageId = nil 196 | 197 | self.didClose() 198 | } 199 | 200 | // MARK: - Sending Data 201 | 202 | public func onSending() -> String? { 203 | return nil 204 | } 205 | 206 | public func send(object: Any, completionHandler: ((Any?, Error?) -> ())?) { 207 | if self.state == .disconnected { 208 | let userInfo = [ 209 | NSLocalizedFailureReasonErrorKey: NSExceptionName.internalInconsistencyException.rawValue, 210 | NSLocalizedDescriptionKey: NSLocalizedString("Start must be called before data can be sent.", comment: "start order exception") 211 | ] 212 | 213 | let error = NSError(domain: "com.autosoftdms.SignalR-Swift.\(type(of: self))", code: 0, userInfo: userInfo) 214 | self.didReceiveError(error: error) 215 | completionHandler?(nil, error) 216 | 217 | return 218 | } 219 | 220 | if self.state == .connecting { 221 | let userInfo = [ 222 | NSLocalizedFailureReasonErrorKey: NSExceptionName.internalInconsistencyException.rawValue, 223 | NSLocalizedDescriptionKey: NSLocalizedString("The connection has not been established.", comment: "connection not established exception") 224 | ] 225 | 226 | let error = NSError(domain: "com.autosoftdms.SignalR-Swift.\(type(of: self))", code: 0, userInfo: userInfo) 227 | self.didReceiveError(error: error) 228 | completionHandler?(nil, error) 229 | return 230 | } 231 | 232 | self.transport?.send(connection: self, data: object, connectionData: self.connectionData, completionHandler: completionHandler) 233 | } 234 | 235 | // MARK: - Received Data 236 | 237 | public func didReceiveData(data: Any) { 238 | self.received?(data) 239 | self.delegate?.connection(connection: self, didReceiveData: data) 240 | } 241 | 242 | public func didReceiveError(error: Error) { 243 | self.error?(error) 244 | self.delegate?.connection(connection: self, didReceiveError: error) 245 | } 246 | 247 | public func willReconnect() { 248 | if let disconnectTimeout = self.disconnectTimeout { 249 | self.disconnectTimeoutOperation = BlockOperation(block: { [weak self] in self?.stopButDoNotCallServer() }) 250 | self.disconnectTimeoutOperation.perform(#selector(BlockOperation.start), with: nil, afterDelay: disconnectTimeout) 251 | } 252 | 253 | if let reconnecting = self.reconnecting { 254 | reconnecting() 255 | } 256 | 257 | self.delegate?.connectionWillReconnect(connection: self) 258 | } 259 | 260 | public func didReconnect() { 261 | NSObject.cancelPreviousPerformRequests(withTarget: self.disconnectTimeoutOperation, selector: #selector(BlockOperation.start), object: nil) 262 | self.disconnectTimeoutOperation = nil 263 | 264 | self.reconnected?() 265 | 266 | self.delegate?.connectionDidReconnect(connection: self) 267 | 268 | self.updateLastKeepAlive() 269 | } 270 | 271 | public func connectionDidSlow() { 272 | self.connectionSlow?() 273 | self.delegate?.connectionDidSlow(connection: self) 274 | } 275 | 276 | func didClose() { 277 | self.closed?() 278 | self.delegate?.connectionDidClose(connection: self) 279 | } 280 | 281 | // MARK: - Prepare Request 282 | 283 | public func addValue(value: String, forHttpHeaderField field: String) { 284 | self.headers[field] = value 285 | } 286 | 287 | public func updateLastKeepAlive() { 288 | self.keepAliveData?.lastKeepAlive = Date() 289 | } 290 | 291 | public func getRequest(url: URLConvertible, httpMethod: HTTPMethod, encoding: ParameterEncoding, parameters: Parameters?) -> DataRequest { 292 | return self.getRequest(url: url, httpMethod: httpMethod, encoding: encoding, parameters: parameters, timeout: 30.0, headers: [:]) 293 | } 294 | 295 | public func getRequest(url: URLConvertible, httpMethod: HTTPMethod, encoding: ParameterEncoding, parameters: Parameters?, timeout: Double) -> DataRequest { 296 | return self.getRequest(url: url, httpMethod: httpMethod, encoding: encoding, parameters: parameters, timeout: timeout, headers: [:]) 297 | } 298 | 299 | public func getRequest(url: URLConvertible, httpMethod: HTTPMethod, encoding: ParameterEncoding, parameters: Parameters?, timeout: Double, headers: HTTPHeaders) -> DataRequest { 300 | var globalHeaders = self.headers 301 | globalHeaders["User-Agent"] = self.createUserAgentString(client: "SignalR.Client.iOS") 302 | 303 | for (httpHeader, value) in headers { 304 | globalHeaders[httpHeader] = value 305 | } 306 | 307 | var urlRequest = try? URLRequest(url: url.asURL(), method: httpMethod, headers: globalHeaders) 308 | urlRequest?.timeoutInterval = timeout 309 | 310 | let encodedURLRequest = try? encoding.encode(urlRequest!, with: parameters) 311 | return sessionManager.request(encodedURLRequest!) 312 | } 313 | 314 | func createUserAgentString(client: String) -> String { 315 | if self.assemblyVersion == nil { 316 | self.assemblyVersion = Version(major: 2, minor: 0) 317 | } 318 | 319 | return "\(client)/\(self.assemblyVersion!) (\(UIDevice.current.localizedModel) \(UIDevice.current.systemVersion))" 320 | } 321 | 322 | public func processResponse(response: Data, shouldReconnect: inout Bool, disconnected: inout Bool) { 323 | self.updateLastKeepAlive() 324 | 325 | shouldReconnect = false 326 | disconnected = false 327 | 328 | guard let json = try? JSONSerialization.jsonObject(with: response), 329 | let message = ReceivedMessage(jsonObject: json) else { return } 330 | 331 | if message.result != nil { 332 | self.didReceiveData(data: json) 333 | } 334 | 335 | if let reconnect = message.shouldReconnect { 336 | shouldReconnect = reconnect 337 | } 338 | 339 | if disconnected, let disconnect = message.disconnected { 340 | disconnected = disconnect 341 | return 342 | } 343 | 344 | if let groupsToken = message.groupsToken { 345 | self.groupsToken = groupsToken 346 | } 347 | 348 | if let messages = message.messages { 349 | if let messageId = message.messageId { 350 | self.messageId = messageId 351 | } 352 | 353 | messages.forEach(self.didReceiveData) 354 | } 355 | } 356 | 357 | deinit { 358 | if self.state != .disconnected { 359 | self.stop() 360 | } 361 | } 362 | } 363 | -------------------------------------------------------------------------------- /SignalR-Swift/Client/Enums/ConnectionState.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ConnectionState.swift 3 | // SignalR-Swift 4 | // 5 | // 6 | // Copyright © 2017 Jordan Camara. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | public enum ConnectionState : Int { 12 | case connecting 13 | case connected 14 | case reconnecting 15 | case disconnected 16 | } 17 | -------------------------------------------------------------------------------- /SignalR-Swift/Client/HeartbeatMonitor.swift: -------------------------------------------------------------------------------- 1 | // 2 | // HeartbeatMonitor.swift 3 | // SignalR-Swift 4 | // 5 | // 6 | // Copyright © 2017 Jordan Camara. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | public final class HeartbeatMonitor { 12 | 13 | private(set) var hasBeenWarned = false 14 | private(set) var didTimeOut = false 15 | private var timer: Timer? 16 | private unowned let connection: ConnectionProtocol 17 | 18 | init(withConnection connection: ConnectionProtocol) { 19 | self.connection = connection 20 | } 21 | 22 | func start() { 23 | self.connection.updateLastKeepAlive() 24 | self.hasBeenWarned = false 25 | self.didTimeOut = false 26 | if let interval = self.connection.keepAliveData?.checkInterval { 27 | self.timer = Timer.scheduledTimer(timeInterval: interval, target: self, selector: #selector(heartBeat(_:)), userInfo: nil, repeats: true) 28 | } 29 | } 30 | 31 | @objc func heartBeat(_ timer: Timer) { 32 | guard let lastKeepAlive = connection.keepAliveData?.lastKeepAlive else { return } 33 | 34 | let timeElapsed = Date().timeIntervalSince(lastKeepAlive) 35 | self.beat(timeElapsed: timeElapsed) 36 | } 37 | 38 | func beat(timeElapsed: Double) { 39 | guard self.connection.state == .connected, let keepAlive = self.connection.keepAliveData else { return } 40 | 41 | if timeElapsed >= keepAlive.timeout { 42 | if !self.didTimeOut { 43 | self.didTimeOut = true 44 | self.connection.transport?.lostConnection(connection: self.connection) 45 | } 46 | } else if timeElapsed >= keepAlive.timeoutWarning { 47 | if !self.hasBeenWarned { 48 | self.hasBeenWarned = true 49 | self.connection.connectionDidSlow() 50 | } 51 | } else { 52 | self.hasBeenWarned = false 53 | self.didTimeOut = false 54 | } 55 | } 56 | 57 | func stop() { 58 | self.timer?.invalidate() 59 | self.timer = nil 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /SignalR-Swift/Client/Hubs/HubConnection.swift: -------------------------------------------------------------------------------- 1 | // 2 | // HubConnection.swift 3 | // SignalR-Swift 4 | // 5 | // 6 | // Copyright © 2017 Jordan Camara. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import Alamofire 11 | 12 | public class HubConnection: Connection, HubConnectionProtocol { 13 | 14 | private var hubs = [String: HubProxy]() 15 | private var callbacks = [String: HubConnectionHubResultClosure]() 16 | private var callbackId = UInt.min 17 | 18 | public init(withUrl url: String, 19 | queryString: [String: String]? = nil, 20 | sessionManager: SessionManager = .default, 21 | useDefault: Bool = true) { 22 | super.init(withUrl: HubConnection.getUrl(url: url, useDefault: useDefault), 23 | queryString: queryString, 24 | sessionManager: sessionManager) 25 | } 26 | 27 | public func createHubProxy(hubName: String) -> HubProxy? { 28 | if self.state != .disconnected { 29 | NSException.raise(.internalInconsistencyException, format: NSLocalizedString("Proxies cannot be added after the connection has been started.", comment: "proxy added after connection starts exception"), arguments: getVaList(["nil"])) 30 | } 31 | 32 | let hubName = hubName.lowercased() 33 | 34 | guard self.hubs[hubName] == nil else { return nil } 35 | 36 | let proxy = HubProxy(connection: self, hubName: hubName) 37 | self.hubs[hubName] = proxy 38 | return proxy 39 | } 40 | 41 | public func registerCallback(callback: @escaping HubConnectionHubResultClosure) -> String { 42 | let newId = String(self.callbackId) 43 | self.callbacks[newId] = callback 44 | self.callbackId += 1 45 | 46 | return newId 47 | } 48 | 49 | public func removeCallback(callbackId: String) { 50 | self.callbacks.removeValue(forKey: callbackId) 51 | } 52 | 53 | func clearInvocationCallbacks(error: String?) { 54 | let result = HubResult(error: error) 55 | 56 | for (_, callback) in self.callbacks { 57 | callback(result) 58 | } 59 | 60 | self.callbacks.removeAll() 61 | } 62 | 63 | // MARK: - Private 64 | 65 | static func getUrl(url: String, useDefault: Bool) -> String { 66 | let urlResult = url.hasSuffix("/") ? url : url.appending("/") 67 | return useDefault ? urlResult.appending("signalr") : urlResult 68 | } 69 | 70 | // MARK - Sending Data 71 | 72 | override public func onSending() -> String { 73 | let hubNames = self.hubs.map { (key, _) in ["Name": key] } 74 | let data = try! JSONSerialization.data(withJSONObject: hubNames) 75 | return String(data: data, encoding: .utf8)! 76 | } 77 | 78 | // MARK: - Received Data 79 | 80 | override public func didReceiveData(data: Any) { 81 | guard let dict = data as? [String: Any] else { return } 82 | 83 | if dict["I"] != nil { 84 | let result = HubResult(jsonObject: dict) 85 | if let callback = self.callbacks[result.id!] { 86 | callback(result) 87 | return 88 | } 89 | } 90 | 91 | let invocation = HubInvocation(jsonObject: dict) 92 | 93 | if let hubProxy = self.hubs[invocation.hub.lowercased()] { 94 | invocation.state.forEach { (key, value) in hubProxy.state[key] = value } 95 | 96 | hubProxy.invokeEvent(eventName: invocation.method, withArgs: invocation.args) 97 | } 98 | 99 | super.didReceiveData(data: data) 100 | } 101 | 102 | override public func willReconnect() { 103 | self.clearInvocationCallbacks(error: "Connection started reconnecting before invocation result was received.") 104 | super.willReconnect() 105 | } 106 | 107 | override func didClose() { 108 | self.clearInvocationCallbacks(error: "Connection was disconnected before invocation result was received.") 109 | super.didClose() 110 | } 111 | } 112 | -------------------------------------------------------------------------------- /SignalR-Swift/Client/Hubs/HubInvocation.swift: -------------------------------------------------------------------------------- 1 | // 2 | // HubInvocation.swift 3 | // SignalR-Swift 4 | // 5 | // 6 | // Copyright © 2017 Jordan Camara. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | private let kCallbackId = "I" 12 | private let kHub = "H" 13 | private let kMethod = "M" 14 | private let kArgs = "A" 15 | private let kState = "S" 16 | 17 | struct HubInvocation { 18 | 19 | let callbackId: String 20 | let hub: String 21 | let method: String 22 | let args: [Any] 23 | let state: [String: Any] 24 | 25 | init(callbackId: String, hub: String, method: String, args: [Any], state: [String: Any] = [:]) { 26 | self.callbackId = callbackId 27 | self.hub = hub 28 | self.method = method 29 | self.args = args 30 | self.state = state 31 | } 32 | 33 | init(jsonObject dict: [String: Any]) { 34 | callbackId = dict[kCallbackId] as? String ?? "" 35 | hub = dict[kHub] as? String ?? "" 36 | method = dict[kMethod] as? String ?? "" 37 | args = dict[kArgs] as? [Any] ?? [] 38 | state = dict[kState] as? [String: Any] ?? [:] 39 | } 40 | 41 | func toJSONString() -> String? { 42 | let json: [String: Any] = [ 43 | kCallbackId: callbackId, 44 | kHub: hub, 45 | kMethod: method, 46 | kArgs: args, 47 | kState: state 48 | ] 49 | return json.toJSONString() 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /SignalR-Swift/Client/Hubs/HubProxy.swift: -------------------------------------------------------------------------------- 1 | // 2 | // HubProxy.swift 3 | // SignalR-Swift 4 | // 5 | // 6 | // Copyright © 2017 Jordan Camara. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | public class HubProxy: HubProxyProtocol { 12 | 13 | public var state = [String: Any]() 14 | 15 | private weak var connection: HubConnectionProtocol? 16 | private let hubName: String 17 | private var subscriptions = [String: Subscription]() 18 | 19 | // MARK: - Init 20 | 21 | public init(connection: HubConnectionProtocol, hubName: String) { 22 | self.connection = connection 23 | self.hubName = hubName 24 | } 25 | 26 | // MARK: - Subscribe 27 | 28 | public func on(eventName: String, handler: @escaping Subscription) -> Subscription? { 29 | guard !eventName.isEmpty else { 30 | NSException.raise(.invalidArgumentException, format: NSLocalizedString("Argument eventName is null", comment: "null event name exception"), arguments: getVaList(["nil"])) 31 | return nil 32 | } 33 | 34 | return self.subscriptions[eventName] ?? self.subscriptions.updateValue(handler, forKey: eventName) ?? handler 35 | } 36 | 37 | public func invokeEvent(eventName: String, withArgs args: [Any]) { 38 | if let subscription = self.subscriptions[eventName] { 39 | subscription(args) 40 | } 41 | } 42 | 43 | // MARK: - Publish 44 | 45 | public func invoke(method: String, withArgs args: [Any]) { 46 | self.invoke(method: method, withArgs: args, completionHandler: nil) 47 | } 48 | 49 | public func invoke(method: String, withArgs args: [Any], completionHandler: ((Any?, Error?) -> ())?) { 50 | guard !method.isEmpty else { 51 | NSException.raise(.invalidArgumentException, format: NSLocalizedString("Argument method is null", comment: "null event name exception"), arguments: getVaList(["nil"])) 52 | return 53 | } 54 | 55 | guard let connection = self.connection else { return } 56 | 57 | let callbackId = connection.registerCallback { result in 58 | guard let hubResult = result else { return } 59 | hubResult.state?.forEach { (key, value) in self.state[key] = value } 60 | completionHandler?(hubResult.result, nil) 61 | } 62 | 63 | let hubData = HubInvocation(callbackId: callbackId, 64 | hub: self.hubName, 65 | method: method, 66 | args: args, 67 | state: self.state) 68 | 69 | connection.send(object: hubData.toJSONString()!, completionHandler: completionHandler) 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /SignalR-Swift/Client/Hubs/HubResult.swift: -------------------------------------------------------------------------------- 1 | // 2 | // HubResult.swift 3 | // SignalR-Swift 4 | // 5 | // 6 | // Copyright © 2017 Jordan Camara. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | private let kId = "I" 12 | private let kResult = "R" 13 | private let kHubException = "H" 14 | private let kError = "E" 15 | private let kErrorData = "D" 16 | private let kState = "S" 17 | 18 | public struct HubResult { 19 | let id: String? 20 | let result: Any? 21 | let hubException: Bool 22 | let error: String? 23 | let errorData: Any? 24 | let state: [String: Any]? 25 | 26 | init(id: String? = nil, result: Any? = nil, hubException: Bool = false, error: String? = nil, errorData: Any? = nil, state: [String: Any]? = nil) { 27 | self.id = id 28 | self.result = result 29 | self.hubException = hubException 30 | self.error = error 31 | self.errorData = errorData 32 | self.state = state 33 | } 34 | 35 | init(jsonObject dict: [String: Any]) { 36 | id = dict[kId] as? String 37 | result = dict[kResult] 38 | hubException = dict[kHubException] as? Bool ?? false 39 | error = dict[kError] as? String 40 | errorData = dict[kErrorData] 41 | state = dict[kState] as? [String: Any] 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /SignalR-Swift/Client/Infrastructure/ExceptionHelper.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ExceptionHelper.swift 3 | // SignalR-Swift 4 | // 5 | // 6 | // Copyright © 2017 Jordan Camara. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | class ExceptionHelper { 12 | 13 | static func isRequestAborted(error: NSError?) -> Bool { 14 | return error != nil && error!.code == NSURLErrorCancelled 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /SignalR-Swift/Client/Infrastructure/Version.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Version.swift 3 | // SignalR-Swift 4 | // 5 | // 6 | // Copyright © 2017 Jordan Camara. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import UIKit 11 | 12 | public struct Version : Equatable, CustomStringConvertible { 13 | let major: UInt 14 | let minor: UInt 15 | 16 | public init(major: UInt = 0, minor: UInt = 0) { 17 | self.major = major 18 | self.minor = minor 19 | } 20 | 21 | public init?(string input: String) { 22 | let components = input.components(separatedBy: ".") 23 | guard (2...4) ~= components.count, let major = UInt(components[0]), let minor = UInt(components[1]) else { 24 | return nil } 25 | 26 | self.major = major 27 | self.minor = minor 28 | } 29 | 30 | public static func == (lhs: Version, rhs: Version) -> Bool { 31 | return lhs.major == rhs.major && lhs.minor == rhs.minor 32 | } 33 | 34 | public var description: String { 35 | return "\(self.major).\(self.minor)" 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /SignalR-Swift/Client/KeepAliveData.swift: -------------------------------------------------------------------------------- 1 | // 2 | // KeepAliveData.swift 3 | // SignalR-Swift 4 | // 5 | // 6 | // Copyright © 2017 Jordan Camara. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | public class KeepAliveData { 12 | var lastKeepAlive: Date? 13 | var timeout: Double! 14 | var timeoutWarning: Double! 15 | var checkInterval: Double! 16 | 17 | init(timeout: Double) { 18 | self.timeout = timeout 19 | self.timeoutWarning = timeout * (2 / 3) 20 | self.checkInterval = timeout - self.timeoutWarning / 3.0 21 | } 22 | 23 | init(withLastKeepAlive lastKeepAlive: Date, timeout: Double, timeoutWarning: Double, checkInterval: Double) { 24 | self.lastKeepAlive = lastKeepAlive 25 | self.timeout = timeout 26 | self.timeoutWarning = timeoutWarning 27 | self.checkInterval = checkInterval 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /SignalR-Swift/Client/NegotiationResponse.swift: -------------------------------------------------------------------------------- 1 | // 2 | // NegotiationResponse.swift 3 | // SignalR-Swift 4 | // 5 | // 6 | // Copyright © 2017 Jordan Camara. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | private let kConnectionId = "ConnectionId" 12 | private let kConnectionToken = "ConnectionToken" 13 | private let kUrl = "Url" 14 | private let kProtocolVersion = "ProtocolVersion" 15 | private let kDisconnectTimeout = "DisconnectTimeout" 16 | private let kTryWebSockets = "TryWebSockets" 17 | private let kKeepAliveTimeout = "KeepAliveTimeout" 18 | private let kTransportConnectTimeout = "TransportConnectTimeout" 19 | 20 | public struct NegotiationResponse { 21 | let connectionId: String 22 | let connectionToken: String 23 | let url: String 24 | let protocolVersion: String 25 | let disconnectTimeout: Double 26 | let tryWebSockets: Bool 27 | let keepAliveTimeout: Double? 28 | let transportConnectTimeout: Double? 29 | 30 | init(jsonObject dict: [String: Any]) { 31 | connectionId = dict[kConnectionId] as? String ?? "" 32 | connectionToken = dict[kConnectionToken] as? String ?? "" 33 | url = dict[kUrl] as? String ?? "" 34 | protocolVersion = dict[kProtocolVersion] as? String ?? "" 35 | disconnectTimeout = dict[kDisconnectTimeout] as? Double ?? 0.0 36 | tryWebSockets = dict[kTryWebSockets] as? Bool ?? false 37 | keepAliveTimeout = dict[kKeepAliveTimeout] as? Double 38 | transportConnectTimeout = dict[kTransportConnectTimeout] as? Double 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /SignalR-Swift/Client/Protocols/ClientTransportProtocol.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ClientTransportProtocol.swift 3 | // SignalR-Swift 4 | // 5 | // 6 | // Copyright © 2017 Jordan Camara. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | public protocol ClientTransportProtocol { 12 | var name: String? { get } 13 | var supportsKeepAlive: Bool { get } 14 | func negotiate(connection: ConnectionProtocol, connectionData: String?, completionHandler: ((_ response: NegotiationResponse?, _ error: Error?) -> ())?) 15 | func start(connection: ConnectionProtocol, connectionData: String?, completionHandler: ((_ response: Any?, _ error: Error?) -> ())?) 16 | func send(connection: ConnectionProtocol, data: Any, connectionData: String?, completionHandler: ((_ response: Any?, _ error: Error?) -> ())?) 17 | 18 | func abort(connection: ConnectionProtocol, timeout: Double, connectionData: String?) 19 | func lostConnection(connection: ConnectionProtocol) 20 | } 21 | -------------------------------------------------------------------------------- /SignalR-Swift/Client/Protocols/ConnectionDelegate.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ConnectionDelegate.swift 3 | // SignalR-Swift 4 | // 5 | // 6 | // Copyright © 2017 Jordan Camara. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | protocol ConnectionDelegate: class { 12 | func connectionDidOpen(connection: ConnectionProtocol) 13 | func connectionDidClose(connection: ConnectionProtocol) 14 | func connectionWillReconnect(connection: ConnectionProtocol) 15 | func connectionDidReconnect(connection: ConnectionProtocol) 16 | func connection(connection: ConnectionProtocol, didReceiveData data: Any) 17 | func connection(connection: ConnectionProtocol, didReceiveError error: Error) 18 | func connection(connection: ConnectionProtocol, didChangeState oldState: ConnectionState, newState: ConnectionState) 19 | func connectionDidSlow(connection: ConnectionProtocol) 20 | } 21 | -------------------------------------------------------------------------------- /SignalR-Swift/Client/Protocols/ConnectionProtocol.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ConnectionProtocol.swift 3 | // SignalR-Swift 4 | // 5 | // 6 | // Copyright © 2017 Jordan Camara. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import UIKit 11 | import Alamofire 12 | 13 | public protocol ConnectionProtocol: class { 14 | var version: Version { get set } 15 | var transportConnectTimeout: Double { get set } 16 | var keepAliveData: KeepAliveData? { get set } 17 | var messageId: String? { get set } 18 | var groupsToken: String? { get set } 19 | var items: [String: Any] { get set } 20 | var connectionId: String? { get set } 21 | var connectionToken: String? { get set } 22 | var url: String { get } 23 | var queryString: [String: String]? { get } 24 | var state: ConnectionState { get } 25 | var transport: ClientTransportProtocol? { get } 26 | var headers: HTTPHeaders { get set } 27 | var sessionManager: SessionManager { get } 28 | var webSocketAllowsSelfSignedSSL: Bool { get set } 29 | 30 | func onSending() -> String? 31 | 32 | func changeState(oldState: ConnectionState, toState newState: ConnectionState) -> Bool 33 | func stop() 34 | func disconnect() 35 | 36 | func send(object: Any, completionHandler: ((Any?, Error?) -> ())?) 37 | 38 | func didReceiveData(data: Any) 39 | func didReceiveError(error: Error) 40 | func willReconnect() 41 | func didReconnect() 42 | func connectionDidSlow() 43 | 44 | func updateLastKeepAlive() 45 | 46 | func getRequest(url: URLConvertible, httpMethod: HTTPMethod, encoding: ParameterEncoding, parameters: Parameters?) -> DataRequest 47 | func getRequest(url: URLConvertible, httpMethod: HTTPMethod, encoding: ParameterEncoding, parameters: Parameters?, timeout: Double) -> DataRequest 48 | func getRequest(url: URLConvertible, httpMethod: HTTPMethod, encoding: ParameterEncoding, parameters: Parameters?, timeout: Double, headers: HTTPHeaders) -> DataRequest 49 | func processResponse(response: Data, shouldReconnect: inout Bool, disconnected: inout Bool) 50 | } 51 | -------------------------------------------------------------------------------- /SignalR-Swift/Client/Protocols/HubConnectionProtocol.swift: -------------------------------------------------------------------------------- 1 | // 2 | // HubConnectionProtocol.swift 3 | // SignalR-Swift 4 | // 5 | // 6 | // Copyright © 2017 Jordan Camara. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | public typealias HubConnectionHubResultClosure = (HubResult?) -> () 12 | 13 | public protocol HubConnectionProtocol: ConnectionProtocol { 14 | func registerCallback(callback: @escaping HubConnectionHubResultClosure) -> String 15 | func removeCallback(callbackId: String) 16 | } 17 | -------------------------------------------------------------------------------- /SignalR-Swift/Client/Protocols/HubProxyProtocol.swift: -------------------------------------------------------------------------------- 1 | // 2 | // HubProxyProtocol.swift 3 | // SignalR-Swift 4 | // 5 | // 6 | // Copyright © 2017 Jordan Camara. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | public typealias Subscription = ([Any]) -> () 12 | 13 | protocol HubProxyProtocol { 14 | func on(eventName: String, handler: @escaping Subscription) -> Subscription? 15 | 16 | func invoke(method: String, withArgs args: [Any]) 17 | 18 | func invoke(method: String, withArgs args: [Any], completionHandler: ((_ response: Any?, _ error: Error?) -> ())?) 19 | } 20 | -------------------------------------------------------------------------------- /SignalR-Swift/Extensions/Dictionary+Json.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Dictionary+Json.swift 3 | // SignalR-Swift 4 | // 5 | // 6 | // Copyright © 2017 Jordan Camara. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | extension Dictionary where Key: ExpressibleByStringLiteral { 12 | func toJSONString() -> String? { 13 | do { 14 | return String(data: try JSONSerialization.data(withJSONObject: self, options: JSONSerialization.WritingOptions.prettyPrinted), encoding: .utf8) 15 | } catch { 16 | print(error.localizedDescription) 17 | } 18 | 19 | return nil 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /SignalR-Swift/Extensions/String+Dictionary.swift: -------------------------------------------------------------------------------- 1 | // 2 | // String+Dictionary.swift 3 | // SignalR-Swift 4 | // 5 | // 6 | // Copyright © 2017 Jordan Camara. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | extension String { 12 | func toDictionary() -> [String: Any]? { 13 | if let data = self.data(using: .utf8) { 14 | do { 15 | return try JSONSerialization.jsonObject(with: data, options: []) as? [String: Any] 16 | } catch { 17 | print(error.localizedDescription) 18 | } 19 | } 20 | 21 | return nil 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /SignalR-Swift/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | FMWK 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleVersion 20 | $(CURRENT_PROJECT_VERSION) 21 | NSPrincipalClass 22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /SignalR-Swift/Models/ReceivedMessage.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Message.swift 3 | // SignalR-Swift 4 | // 5 | // 6 | // Copyright © 2017 Jordan Camara. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | struct ReceivedMessage { 12 | 13 | let result: String? 14 | let shouldReconnect: Bool? 15 | let disconnected: Bool? 16 | let groupsToken: String? 17 | let messageId: String? 18 | let messages: [Any]? 19 | 20 | init?(jsonObject: Any) { 21 | 22 | guard let dict = jsonObject as? [String: Any] else { return nil } 23 | 24 | result = dict["I"] as? String 25 | shouldReconnect = dict["T"] as? Bool 26 | disconnected = dict["D"] as? Bool 27 | groupsToken = dict["G"] as? String 28 | messageId = dict["C"] as? String 29 | messages = dict["M"] as? [Any] 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /SignalR-Swift/SignalR-Swift.h: -------------------------------------------------------------------------------- 1 | // 2 | // SignalR-Swift.h 3 | // SignalR-Swift 4 | // 5 | // Created by Jordan Camara on 1/6/17. 6 | // Copyright © 2017 Jordan Camara. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | //! Project version number for SignalR-Swift. 12 | FOUNDATION_EXPORT double SignalR_SwiftVersionNumber; 13 | 14 | //! Project version string for SignalR-Swift. 15 | FOUNDATION_EXPORT const unsigned char SignalR_SwiftVersionString[]; 16 | 17 | // In this header, you should import all the public headers of your framework using statements like #import 18 | 19 | 20 | -------------------------------------------------------------------------------- /SignalR-Swift/Transports/AutoTransport.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AutoTransport.swift 3 | // SignalR-Swift 4 | // 5 | // 6 | // Copyright © 2017 Jordan Camara. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | public class AutoTransport: HttpTransport { 12 | var transports = [ClientTransportProtocol]() 13 | var transport: ClientTransportProtocol? 14 | 15 | override convenience init() { 16 | let transports = [ 17 | WebSocketTransport(), 18 | ServerSentEventsTransport(), 19 | LongPollingTransport() 20 | ] 21 | 22 | self.init(withTransports: transports) 23 | } 24 | 25 | init(withTransports transports: [ClientTransportProtocol]) { 26 | self.transports = transports 27 | } 28 | 29 | // MARK: - Client Transport Protocol 30 | 31 | override public var name: String? { 32 | return self.transport?.name 33 | } 34 | 35 | override public var supportsKeepAlive: Bool { 36 | return self.transport?.supportsKeepAlive ?? false 37 | } 38 | 39 | override public func negotiate(connection: ConnectionProtocol, connectionData: String?, completionHandler: ((NegotiationResponse?, Error?) -> ())?) { 40 | super.negotiate(connection: connection, connectionData: connectionData) { [weak self] (response, error) in 41 | guard let strongRef = self else { return } 42 | 43 | if error == nil, 44 | let tryWebSockets = response?.tryWebSockets, !tryWebSockets, 45 | let invalidIndex = strongRef.transports.index(where: { $0.name == "webSockets" }) { 46 | strongRef.transports.remove(at: invalidIndex) 47 | } 48 | 49 | completionHandler?(response, error) 50 | } 51 | } 52 | 53 | override public func start(connection: ConnectionProtocol, connectionData: String?, completionHandler: ((Any?, Error?) -> ())?) { 54 | self.start(connection: connection, connectionData: connectionData, transportIndex: 0, completionHandler: completionHandler) 55 | } 56 | 57 | func start(connection: ConnectionProtocol, connectionData: String?, transportIndex index: Int, completionHandler: ((String?, Error?) -> ())?) { 58 | let transport = self.transports[index] 59 | transport.start(connection: connection, connectionData: connectionData) { [weak self] (response, error) in 60 | guard let strongRef = self else { return } 61 | 62 | if error != nil { 63 | let nextIndex = index + 1 64 | 65 | if nextIndex < strongRef.transports.count { 66 | strongRef.start(connection: connection, connectionData: connectionData, transportIndex: nextIndex, completionHandler: completionHandler) 67 | } else { 68 | let userInfo = [ 69 | NSLocalizedFailureReasonErrorKey: NSExceptionName.internalInconsistencyException.rawValue, 70 | NSLocalizedDescriptionKey: NSLocalizedString("No transport could be initialized successfully. Try specifying a different transport or none at all for auto initialization.", comment: "no transport initialized") 71 | ] 72 | 73 | let error = NSError(domain: "com.autosoftdms.SignalR-Swift.\(type(of: self))", code: 0, userInfo: userInfo) 74 | 75 | completionHandler?(nil, error) 76 | } 77 | } else { 78 | strongRef.transport = transport 79 | 80 | completionHandler?(nil, nil) 81 | } 82 | } 83 | } 84 | 85 | override public func send(connection: ConnectionProtocol, data: Any, connectionData: String?, completionHandler: ((Any?, Error?) -> ())?) { 86 | self.transport?.send(connection: connection, data: data, connectionData: connectionData, completionHandler: completionHandler) 87 | } 88 | 89 | override public func lostConnection(connection: ConnectionProtocol) { 90 | self.transport?.lostConnection(connection: connection) 91 | } 92 | 93 | public override func abort(connection: ConnectionProtocol, timeout: Double, connectionData: String?) { 94 | self.transport?.abort(connection: connection, timeout: timeout, connectionData: connectionData) 95 | } 96 | } 97 | -------------------------------------------------------------------------------- /SignalR-Swift/Transports/HttpTransport.swift: -------------------------------------------------------------------------------- 1 | // 2 | // HttpTransport.swift 3 | // SignalR-Swift 4 | // 5 | // 6 | // Copyright © 2017 Jordan Camara. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import Alamofire 11 | 12 | public class HttpTransport: ClientTransportProtocol { 13 | 14 | public var name: String? { 15 | return "" 16 | } 17 | 18 | public var supportsKeepAlive: Bool { 19 | return false 20 | } 21 | 22 | var startedAbort: Bool = false 23 | 24 | public func negotiate(connection: ConnectionProtocol, connectionData: String?, completionHandler: ((NegotiationResponse?, Error?) -> ())?) { 25 | let url = connection.url.appending("negotiate") 26 | 27 | let parameters = self.getConnectionParameters(connection: connection, connectionData: connectionData) 28 | 29 | let encodedRequest = connection.getRequest(url: url, httpMethod: .get, encoding: URLEncoding.default, parameters: parameters, timeout: 30.0) 30 | 31 | encodedRequest.validate().responseJSON { (response: DataResponse) in 32 | switch response.result { 33 | case .success(let result): 34 | if let json = result as? [String: Any] { 35 | completionHandler?(NegotiationResponse(jsonObject: json), nil) 36 | } 37 | else { 38 | completionHandler?(nil, AFError.responseSerializationFailed(reason: .inputDataNil)) 39 | } 40 | case .failure(let error): 41 | completionHandler?(nil, error) 42 | } 43 | } 44 | } 45 | 46 | public func start(connection: ConnectionProtocol, connectionData: String?, completionHandler: ((Any?, Error?) -> ())?) { 47 | 48 | } 49 | 50 | public func send(connection: ConnectionProtocol, data: Any, connectionData: String?, completionHandler: ((Any?, Error?) -> ())?) { 51 | let url = connection.url.appending("send") 52 | 53 | let parameters = self.getConnectionParameters(connection: connection, connectionData: connectionData) 54 | 55 | var encodedRequestURL = url 56 | do { 57 | let originalRequest = try URLRequest(url: url, method: .get, headers: nil) 58 | encodedRequestURL = try URLEncoding.queryString.encode(originalRequest, with: parameters).url!.absoluteString 59 | } catch { 60 | } 61 | 62 | var requestParams = [String: Any]() 63 | 64 | if let dataString = data as? String { 65 | requestParams["data"] = dataString 66 | } else if let dataDict = data as? [String: Any] { 67 | requestParams = dataDict 68 | } 69 | 70 | let request = connection.getRequest(url: encodedRequestURL, httpMethod: .post, encoding: URLEncoding.httpBody, parameters: requestParams) 71 | request.validate().responseJSON { (response: DataResponse) in 72 | switch response.result { 73 | case .success(let result): 74 | connection.didReceiveData(data: result) 75 | 76 | if let handler = completionHandler { 77 | handler(result, nil) 78 | } 79 | case .failure(let error): 80 | connection.didReceiveError(error: error) 81 | 82 | if let handler = completionHandler { 83 | handler(nil, error) 84 | } 85 | } 86 | } 87 | } 88 | 89 | func completeAbort() { 90 | self.startedAbort = true 91 | } 92 | 93 | func tryCompleteAbort() -> Bool { 94 | return startedAbort 95 | } 96 | 97 | public func lostConnection(connection: ConnectionProtocol) { 98 | 99 | } 100 | 101 | public func abort(connection: ConnectionProtocol, timeout: Double, connectionData: String?) { 102 | guard timeout > 0, !self.startedAbort else { return } 103 | 104 | self.startedAbort = true 105 | 106 | let url = connection.url.appending("abort") 107 | 108 | let parameters = self.getConnectionParameters(connection: connection, connectionData: connectionData) 109 | 110 | let encodedRequest = connection.getRequest(url: url, httpMethod: .get, encoding: URLEncoding.default, parameters: parameters, timeout: 2.0) 111 | 112 | let request = connection.getRequest(url: encodedRequest.request!.url!.absoluteString, httpMethod: .post, encoding: URLEncoding.httpBody, parameters: nil) 113 | request.validate().response { response in 114 | if response.error != nil { 115 | self.completeAbort() 116 | } 117 | } 118 | } 119 | 120 | func getConnectionParameters(connection: ConnectionProtocol, connectionData: String?) -> [String: Any] { 121 | var parameters: [String: Any] = [ 122 | "clientProtocol": connection.version.description, 123 | "transport": self.name ?? "", 124 | "connectionData": connectionData ?? "", 125 | "connectionToken": connection.connectionToken ?? "", 126 | ] 127 | 128 | if let queryString = connection.queryString { 129 | for (key, value) in queryString { 130 | parameters[key] = value 131 | } 132 | } 133 | 134 | return parameters 135 | } 136 | } 137 | -------------------------------------------------------------------------------- /SignalR-Swift/Transports/LongPollingTransport.swift: -------------------------------------------------------------------------------- 1 | // 2 | // LongPollingTransport.swift 3 | // SignalR-Swift 4 | // 5 | // 6 | // Copyright © 2017 Jordan Camara. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import Alamofire 11 | 12 | public class LongPollingTransport: HttpTransport { 13 | var reconnectDelay = 5.0 14 | var errorDelay = 2.0 15 | var pollingQueue = DispatchQueue(label: "com.autosoftdms.SignalR-Swift.serial") 16 | 17 | // MARK: - Client Transport Protocol 18 | 19 | override public var name: String? { 20 | return "longPolling" 21 | } 22 | 23 | override public var supportsKeepAlive: Bool { 24 | return false 25 | } 26 | 27 | override public func negotiate(connection: ConnectionProtocol, connectionData: String?, completionHandler: ((NegotiationResponse?, Error?) -> ())?) { 28 | super.negotiate(connection: connection, connectionData: connectionData, completionHandler: completionHandler) 29 | } 30 | 31 | override public func start(connection: ConnectionProtocol, connectionData: String?, completionHandler: ((Any?, Error?) -> ())?) { 32 | self.poll(connection: connection, connectionData: connectionData, completionHandler: completionHandler) 33 | } 34 | 35 | override public func send(connection: ConnectionProtocol, data: Any, connectionData: String?, completionHandler: ((Any?, Error?) -> ())?) { 36 | super.send(connection: connection, data: data, connectionData: connectionData, completionHandler: completionHandler) 37 | } 38 | 39 | override public func abort(connection: ConnectionProtocol, timeout: Double, connectionData: String?) { 40 | super.abort(connection: connection, timeout: timeout, connectionData: connectionData) 41 | } 42 | 43 | override public func lostConnection(connection: ConnectionProtocol) { 44 | 45 | } 46 | 47 | // MARK: - Long Polling 48 | 49 | func poll(connection: ConnectionProtocol, connectionData: String?, completionHandler: ((_ response: String?, _ error: Error?) -> ())?) { 50 | var canReconnect = true 51 | 52 | var url = connection.url 53 | if connection.messageId == nil { 54 | url = url.appending("connect") 55 | } else if self.isConnectionReconnecting(connection: connection) { 56 | url = url.appending("reconnect") 57 | } else { 58 | url = url.appending("poll") 59 | } 60 | 61 | self.delayConnectionReconnect(connection: connection, canReconnect: &canReconnect) 62 | 63 | var parameters: [String: Any] = [ 64 | "transport": self.name!, 65 | "connectionToken": connection.connectionToken ?? "", 66 | "connectionData": connectionData ?? "", 67 | "groupsToken": connection.groupsToken ?? "", 68 | "messageId": connection.messageId ?? "" 69 | ] 70 | 71 | if let queryString = connection.queryString { 72 | for (key, value) in queryString { 73 | parameters[key] = value 74 | } 75 | } 76 | 77 | let encodedRequest = connection.getRequest(url: url, httpMethod: .get, encoding: URLEncoding.default, parameters: parameters, timeout: 240) 78 | 79 | _ = self.pollingQueue.sync { 80 | encodedRequest.validate().responseData(completionHandler: { [weak self, weak connection] response in 81 | guard let strongSelf = self, let strongConnection = connection else { return } 82 | 83 | switch response.result { 84 | case .success(let result): 85 | 86 | var shouldReconnect = false 87 | var disconnectedReceived = false 88 | 89 | strongConnection.processResponse(response: result, shouldReconnect: &shouldReconnect, disconnected: &disconnectedReceived) 90 | 91 | if let handler = completionHandler { 92 | handler(nil, nil) 93 | } 94 | 95 | if strongSelf.isConnectionReconnecting(connection: strongConnection) { 96 | strongSelf.connectionReconnect(connection: strongConnection, canReconnect: canReconnect) 97 | } 98 | 99 | if shouldReconnect { 100 | _ = Connection.ensureReconnecting(connection: strongConnection) 101 | } 102 | 103 | if disconnectedReceived { 104 | strongConnection.disconnect() 105 | } 106 | 107 | if !strongSelf.tryCompleteAbort() { 108 | canReconnect = true 109 | strongSelf.poll(connection: strongConnection, connectionData: connectionData, completionHandler: nil) 110 | } 111 | case .failure(let error): 112 | canReconnect = false 113 | 114 | _ = Connection.ensureReconnecting(connection: strongConnection) 115 | 116 | if !strongSelf.tryCompleteAbort() && !ExceptionHelper.isRequestAborted(error: (error as NSError)) { 117 | strongConnection.didReceiveError(error: error) 118 | 119 | canReconnect = true 120 | 121 | _ = BlockOperation(block: { 122 | strongSelf.poll(connection: strongConnection, connectionData: connectionData, completionHandler: nil) 123 | }).perform(#selector(BlockOperation.start), with: nil, afterDelay: strongSelf.errorDelay) 124 | } else { 125 | strongSelf.completeAbort() 126 | if let handler = completionHandler { 127 | handler(nil, error) 128 | } 129 | } 130 | } 131 | }) 132 | } 133 | } 134 | 135 | func delayConnectionReconnect(connection: ConnectionProtocol, canReconnect: inout Bool) { 136 | if self.isConnectionReconnecting(connection: connection) { 137 | let canReconnectCopy = canReconnect 138 | if canReconnect { 139 | canReconnect = false 140 | 141 | _ = BlockOperation(block: { [weak self, weak connection] in 142 | guard let strongSelf = self, let strongConnection = connection else { return } 143 | strongSelf.connectionReconnect(connection: strongConnection, canReconnect: canReconnectCopy) 144 | }).perform(#selector(BlockOperation.start), with: nil, afterDelay: self.reconnectDelay) 145 | } 146 | } 147 | } 148 | 149 | func connectionReconnect(connection: ConnectionProtocol, canReconnect: Bool) { 150 | if canReconnect { 151 | if connection.changeState(oldState: .reconnecting, toState: .connected) { 152 | connection.didReconnect() 153 | } 154 | } 155 | } 156 | 157 | func isConnectionReconnecting(connection: ConnectionProtocol) -> Bool { 158 | return connection.state == .reconnecting 159 | } 160 | } 161 | -------------------------------------------------------------------------------- /SignalR-Swift/Transports/ServerSentEvents/ChunkBuffer.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ChunkBuffer.swift 3 | // SignalR-Swift 4 | // 5 | // Created by Vladimir Kushelkov on 21/07/2017. 6 | // Copyright © 2017 Jordan Camara. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | final class ChunkBuffer { 12 | private var buffer = String() 13 | 14 | var hasChunks: Bool { 15 | return !buffer.isEmpty 16 | } 17 | 18 | func append(data: Data) { 19 | guard let newChunk = String(data: data, encoding: .utf8), !newChunk.isEmpty else { return } 20 | buffer.append(newChunk) 21 | } 22 | 23 | func readLine() -> String? { 24 | var line: String? 25 | 26 | while let endIndex = buffer.index(of: "\n") { 27 | let substring = buffer[.. ServerSentEvent { 25 | return ServerSentEvent(event: Event(rawValue: fields["event"]!)!, 26 | identifier: fields["id"], 27 | data: fields["data"], 28 | retry: fields["retry"], 29 | userInfo: nil) 30 | } 31 | 32 | static func tryParse(line: String) -> ServerSentEvent? { 33 | if line.hasPrefix("data:") { 34 | let data = line["data:".endIndex.. () 13 | 14 | public class ServerSentEventsTransport: HttpTransport { 15 | private var stop = false 16 | private var connectTimeoutOperation: BlockOperation? 17 | private var completionHandler: CompletionHandler? 18 | private let sseQueue = DispatchQueue(label: "com.autosoftdms.SignalR-Swift.serverSentEvents", qos: .userInitiated) 19 | 20 | var reconnectDelay: TimeInterval = 2.0 21 | 22 | override public var name: String? { 23 | return "serverSentEvents" 24 | } 25 | 26 | override public var supportsKeepAlive: Bool { 27 | return true 28 | } 29 | 30 | override public func negotiate(connection: ConnectionProtocol, connectionData: String?, completionHandler: ((NegotiationResponse?, Error?) -> ())?) { 31 | super.negotiate(connection: connection, connectionData: connectionData, completionHandler: nil) 32 | } 33 | 34 | override public func start(connection: ConnectionProtocol, connectionData: String?, completionHandler: ((Any?, Error?) -> ())?) { 35 | self.completionHandler = completionHandler 36 | 37 | self.connectTimeoutOperation = BlockOperation { [weak self] in 38 | guard let strongSelf = self, let completionHandler = strongSelf.completionHandler else { return } 39 | 40 | let userInfo = [ 41 | NSLocalizedDescriptionKey: NSLocalizedString("Connection timed out.", comment: "timeout error description"), 42 | NSLocalizedFailureReasonErrorKey: NSLocalizedString("Connection did not receive initialized message before the timeout.", comment: "timeout error reason"), 43 | NSLocalizedRecoverySuggestionErrorKey: NSLocalizedString("Retry or switch transports.", comment: "timeout error retry suggestion") 44 | ] 45 | let error = NSError(domain: "com.autosoftdms.SignalR-Swift.\(type(of: strongSelf))", code: NSURLErrorTimedOut, userInfo: userInfo) 46 | completionHandler(nil, error) 47 | strongSelf.completionHandler = nil 48 | } 49 | 50 | self.connectTimeoutOperation!.perform(#selector(BlockOperation.start), with: nil, afterDelay: connection.transportConnectTimeout) 51 | 52 | self.open(connection: connection, connectionData: connectionData, isReconnecting: false) 53 | } 54 | 55 | override public func send(connection: ConnectionProtocol, data: Any, connectionData: String?, completionHandler: ((Any?, Error?) -> ())?) { 56 | super.send(connection: connection, data: data, connectionData: connectionData, completionHandler: completionHandler) 57 | } 58 | 59 | override public func abort(connection: ConnectionProtocol, timeout: Double, connectionData: String?) { 60 | self.stop = true 61 | super.abort(connection: connection, timeout: timeout, connectionData: connectionData) 62 | } 63 | 64 | override public func lostConnection(connection: ConnectionProtocol) { 65 | 66 | } 67 | 68 | // MARK: - SSE Transport 69 | 70 | private let buffer = ChunkBuffer() 71 | 72 | private func open(connection: ConnectionProtocol, connectionData: String?, isReconnecting: Bool) { 73 | var parameters = connection.queryString ?? [:] 74 | parameters["transport"] = self.name! 75 | parameters["connectionToken"] = connection.connectionToken ?? "" 76 | parameters["messageId"] = connection.messageId ?? "" 77 | parameters["groupsToken"] = connection.groupsToken ?? "" 78 | parameters["connectionData"] = connectionData ?? "" 79 | 80 | let url = isReconnecting ? connection.url.appending("reconnect") : connection.url.appending("connect") 81 | 82 | connection.getRequest(url: url, 83 | httpMethod: .get, 84 | encoding: URLEncoding.default, 85 | parameters: parameters, 86 | timeout: 240, 87 | headers: ["Connection": "Keep-Alive"]) 88 | .stream { [weak self] data in 89 | self?.sseQueue.async { [weak connection] in 90 | guard let strongSelf = self, let strongConnection = connection else { return } 91 | 92 | strongSelf.buffer.append(data: data) 93 | 94 | while let line = strongSelf.buffer.readLine() { 95 | guard let message = ServerSentEvent.tryParse(line: line) else { continue } 96 | DispatchQueue.main.async { strongSelf.process(message: message, connection: strongConnection) } 97 | } 98 | } 99 | }.validate().response() { [weak self, weak connection] dataResponse in 100 | guard let strongSelf = self, let strongConnection = connection else { return } 101 | 102 | strongSelf.cancelTimeoutOperation() 103 | 104 | if let error = dataResponse.error as NSError?, error.code != NSURLErrorCancelled { 105 | strongConnection.didReceiveError(error: error) 106 | } 107 | 108 | if strongSelf.stop { 109 | strongSelf.completeAbort() 110 | } else if !strongSelf.tryCompleteAbort() && !isReconnecting { 111 | strongSelf.reconnect(connection: strongConnection, data: connectionData) 112 | } 113 | } 114 | } 115 | 116 | private func process(message: ServerSentEvent, connection: ConnectionProtocol) { 117 | guard message.event == .data else { return } 118 | 119 | if message.data == "initialized" { 120 | if connection.changeState(oldState: .reconnecting, toState: .connected) { 121 | connection.didReconnect() 122 | } 123 | 124 | return 125 | } 126 | 127 | guard let data = message.data?.data(using: .utf8) else { return } 128 | 129 | var shouldReconnect = false 130 | var disconnected = false 131 | connection.processResponse(response: data, shouldReconnect: &shouldReconnect, disconnected: &disconnected) 132 | 133 | cancelTimeoutOperation() 134 | 135 | if disconnected { 136 | stop = true 137 | connection.disconnect() 138 | } 139 | } 140 | 141 | private func cancelTimeoutOperation() { 142 | guard let completionHandler = self.completionHandler, 143 | let timeoutOperation = connectTimeoutOperation else { return } 144 | 145 | NSObject.cancelPreviousPerformRequests(withTarget: timeoutOperation, selector: #selector(BlockOperation.start), object: nil) 146 | connectTimeoutOperation = nil 147 | completionHandler(nil, nil) 148 | self.completionHandler = nil 149 | } 150 | 151 | private func reconnect(connection: ConnectionProtocol, data: String?) { 152 | _ = BlockOperation { [weak self, weak connection] in 153 | if let strongSelf = self, let strongConnection = connection, 154 | strongConnection.state != .disconnected, Connection.ensureReconnecting(connection: strongConnection) { 155 | strongSelf.open(connection: strongConnection, connectionData: data, isReconnecting: true) 156 | } 157 | }.perform(#selector(BlockOperation.start), with: nil, afterDelay: self.reconnectDelay) 158 | } 159 | } 160 | -------------------------------------------------------------------------------- /SignalR-Swift/Transports/WebSocketTransport.swift: -------------------------------------------------------------------------------- 1 | // 2 | // WebSocketTransport.swift 3 | // SignalR-Swift 4 | // 5 | // 6 | // Copyright © 2017 Jordan Camara. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import Starscream 11 | import Alamofire 12 | 13 | private typealias WebSocketStartClosure = (String?, Error?) -> () 14 | 15 | public class WebSocketTransport: HttpTransport, WebSocketDelegate { 16 | var reconnectDelay = 2.0 17 | private var connectionInfo: WebSocketConnectionInfo? 18 | private var webSocket: WebSocket? 19 | private var startClosure: WebSocketStartClosure? 20 | private var connectTimeoutOperation: BlockOperation? 21 | 22 | override public var name: String? { 23 | return "webSockets" 24 | } 25 | 26 | override public var supportsKeepAlive: Bool { 27 | return true 28 | } 29 | 30 | override public func negotiate(connection: ConnectionProtocol, connectionData: String?, completionHandler: ((NegotiationResponse?, Error?) -> ())?) { 31 | super.negotiate(connection: connection, connectionData: connectionData, completionHandler: completionHandler) 32 | } 33 | 34 | override public func start(connection: ConnectionProtocol, connectionData: String?, completionHandler: ((Any?, Error?) -> ())?) { 35 | self.connectionInfo = WebSocketConnectionInfo(connection: connection, data: connectionData) 36 | 37 | // perform connection 38 | self.performConnect(completionHandler: completionHandler) 39 | } 40 | 41 | override public func send(connection: ConnectionProtocol, data: Any, connectionData: String?, completionHandler: ((Any?, Error?) -> ())?) { 42 | if let dataString = data as? String { 43 | self.webSocket?.write(string: dataString) 44 | } else if let dataDict = data as? [String: Any] { 45 | self.webSocket?.write(string: dataDict.toJSONString()!) 46 | } 47 | 48 | completionHandler?(nil, nil) 49 | } 50 | 51 | override public func abort(connection: ConnectionProtocol, timeout: Double, connectionData: String?) { 52 | self.stopWebSocket() 53 | super.abort(connection: connection, timeout: timeout, connectionData: connectionData) 54 | } 55 | 56 | override public func lostConnection(connection: ConnectionProtocol) { 57 | self.stopWebSocket() 58 | 59 | if self.tryCompleteAbort() { 60 | return 61 | } 62 | 63 | self.reconnect(connection: self.connectionInfo?.connection) 64 | } 65 | 66 | private func stopWebSocket() { 67 | self.webSocket?.delegate = nil 68 | self.webSocket?.disconnect() 69 | self.webSocket = nil 70 | } 71 | 72 | // MARK: - WebSockets transport 73 | 74 | func performConnect(completionHandler: ((_ response: String?, _ error: Error?) -> ())?) { 75 | self.performConnect(reconnecting: false, completionHandler: completionHandler) 76 | } 77 | 78 | func performConnect(reconnecting: Bool, completionHandler: ((_ response: String?, _ error: Error?) -> ())?) { 79 | let connection = self.connectionInfo?.connection 80 | var parameters: [String: Any] = [ 81 | "transport": self.name!, 82 | "connectionToken": connection?.connectionToken ?? "", 83 | "messageId": connection?.messageId ?? "", 84 | "groupsToken": connection?.groupsToken ?? "", 85 | "connectionData": self.connectionInfo?.data ?? "" 86 | ] 87 | 88 | if let queryString = self.connectionInfo?.connection?.queryString { 89 | for (key, value) in queryString { 90 | parameters[key] = value 91 | } 92 | } 93 | 94 | var urlComponents = URLComponents(string: connection!.url) 95 | if let urlScheme = urlComponents?.scheme { 96 | if urlScheme.hasPrefix("https") { 97 | urlComponents?.scheme = "wss" 98 | } else if urlScheme.hasPrefix("http") { 99 | urlComponents?.scheme = "ws" 100 | } 101 | } 102 | 103 | do { 104 | let baseUrl = try urlComponents?.asURL() 105 | 106 | let url = reconnecting ? baseUrl!.absoluteString.appending("reconnect") : baseUrl!.absoluteString.appending("connect") 107 | 108 | let request = connection?.getRequest(url: url, httpMethod: .get, encoding: URLEncoding.default, parameters: parameters, timeout: 30) 109 | 110 | self.startClosure = completionHandler 111 | if let startClosure = self.startClosure { 112 | self.connectTimeoutOperation = BlockOperation(block: { [weak self] in 113 | guard let strongSelf = self else { return } 114 | 115 | let userInfo = [ 116 | NSLocalizedDescriptionKey: NSLocalizedString("Connection timed out.", comment: "timeout error description"), 117 | NSLocalizedFailureReasonErrorKey: NSLocalizedString("Connection did not receive initialized message before the timeout.", comment: "timeout error reason"), 118 | NSLocalizedRecoverySuggestionErrorKey: NSLocalizedString("Retry or switch transports.", comment: "timeout error retry suggestion") 119 | ] 120 | let error = NSError(domain: "com.autosoftdms.SignalR-Swift.\(type(of: strongSelf))", code: NSURLErrorTimedOut, userInfo: userInfo) 121 | strongSelf.stopWebSocket() 122 | 123 | strongSelf.startClosure = nil 124 | startClosure(nil, error) 125 | }) 126 | 127 | self.connectTimeoutOperation?.perform(#selector(BlockOperation.start), with: nil, afterDelay: connection!.transportConnectTimeout) 128 | } 129 | 130 | if let encodedRequest = request?.request { 131 | self.webSocket = WebSocket(request: encodedRequest) 132 | self.webSocket!.disableSSLCertValidation = connection?.webSocketAllowsSelfSignedSSL ?? false 133 | self.webSocket!.delegate = self 134 | self.webSocket!.connect() 135 | } 136 | } catch { 137 | 138 | } 139 | } 140 | 141 | func reconnect(connection: ConnectionProtocol?) { 142 | _ = BlockOperation { [weak self] in 143 | if let strongSelf = self, let connection = connection, Connection.ensureReconnecting(connection: connection) { 144 | strongSelf.performConnect(reconnecting: true, completionHandler: nil) 145 | } 146 | }.perform(#selector(BlockOperation.start), with: nil, afterDelay: self.reconnectDelay) 147 | } 148 | 149 | // MARK: - WebSocketDelegate 150 | 151 | public func websocketDidConnect(socket: WebSocketClient){ 152 | if let connection = self.connectionInfo?.connection, connection.changeState(oldState: .reconnecting, toState: .connected) { 153 | connection.didReconnect() 154 | } 155 | } 156 | 157 | public func websocketDidDisconnect(socket: WebSocketClient, error: Error?) { 158 | if let error = error { 159 | webSocketError(error) 160 | } 161 | else if !self.tryCompleteAbort() { 162 | self.reconnect(connection: self.connectionInfo?.connection) 163 | } 164 | } 165 | 166 | private func webSocketError(_ error: Error) { 167 | if let startClosure = self.startClosure, let connectTimeoutOperation = self.connectTimeoutOperation { 168 | NSObject.cancelPreviousPerformRequests(withTarget: connectTimeoutOperation, selector: #selector(BlockOperation.start), object: nil) 169 | 170 | self.connectTimeoutOperation = nil 171 | self.stopWebSocket() 172 | 173 | self.startClosure = nil 174 | startClosure(nil, error) 175 | } else if !self.startedAbort { 176 | self.reconnect(connection: self.connectionInfo?.connection) 177 | } 178 | } 179 | 180 | public func websocketDidReceiveMessage(socket: WebSocketClient, text: String) { 181 | var timedOut = false 182 | var disconnected = false 183 | 184 | if let connection = self.connectionInfo?.connection, let data = text.data(using: .utf8) { 185 | connection.processResponse(response: data, shouldReconnect: &timedOut, disconnected: &disconnected) 186 | } 187 | 188 | if let startClosure = self.startClosure, let connectTimeoutOperation = self.connectTimeoutOperation { 189 | NSObject.cancelPreviousPerformRequests(withTarget: connectTimeoutOperation, selector: #selector(BlockOperation.start), object: nil) 190 | self.connectTimeoutOperation = nil 191 | 192 | self.startClosure = nil 193 | startClosure(nil, nil) 194 | } 195 | 196 | if disconnected { 197 | self.connectionInfo?.connection?.disconnect() 198 | self.stopWebSocket() 199 | } 200 | } 201 | 202 | public func websocketDidReceiveData(socket: WebSocketClient, data: Data) { 203 | } 204 | } 205 | -------------------------------------------------------------------------------- /SignalR-Swift/Transports/WebSockets/WebSocketConnectionInfo.swift: -------------------------------------------------------------------------------- 1 | // 2 | // WebSocketConnectionInfo.swift 3 | // SignalR-Swift 4 | // 5 | // 6 | // Copyright © 2017 Jordan Camara. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | class WebSocketConnectionInfo { 12 | weak var connection: ConnectionProtocol? 13 | var data: String? 14 | 15 | init(connection: ConnectionProtocol, data: String?) { 16 | self.connection = connection 17 | self.data = data 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /SignalR-SwiftTests/Connection/ConnectionTests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ConnectionTests.swift 3 | // SignalR-Swift 4 | // 5 | // 6 | // Copyright © 2017 Jordan Camara. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import Quick 11 | import Nimble 12 | import Mockit 13 | import XCTest 14 | @testable import SignalRSwift 15 | 16 | class ConnectionTests: QuickSpec { 17 | var connection: Connection! 18 | var mockClientTransport: MockClientTransport! 19 | 20 | override func spec() { 21 | beforeEach { 22 | self.mockClientTransport = MockClientTransport(testCase: self) 23 | self.connection = Connection(withUrl: "http://localhost:0000") 24 | } 25 | 26 | it("should cause a closed event when transport error occurs") { 27 | self.connection.start(transport: self.mockClientTransport) 28 | self.expectation(description: "gets closed when transport errors out") 29 | 30 | self.waitForExpectations(timeout: 5.0, handler: { error in 31 | if let theError = error { 32 | print("Sub-Timeout Error: \(theError)") 33 | } 34 | }) 35 | } 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /SignalR-SwiftTests/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | BNDL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleVersion 20 | 1 21 | 22 | 23 | -------------------------------------------------------------------------------- /SignalR-SwiftTests/Mocks/MockClientTransport.swift: -------------------------------------------------------------------------------- 1 | // 2 | // MockClientTransport.swift 3 | // SignalR-Swift 4 | // 5 | // 6 | // Copyright © 2017 Jordan Camara. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import Mockit 11 | import XCTest 12 | @testable import SignalRSwift 13 | 14 | class MockClientTransport: ClientTransportProtocol, Mock { 15 | public var callHandler: CallHandler 16 | 17 | init(testCase: XCTestCase) { 18 | self.callHandler = CallHandlerImpl(withTestCase: testCase) 19 | } 20 | 21 | func instanceType() -> MockClientTransport { 22 | return self 23 | } 24 | 25 | var name: String? { 26 | return self.callHandler.accept("", ofFunction: #function, atFile: #file, inLine: #line, withArgs: self.name) as! String? 27 | } 28 | 29 | var supportsKeepAlive: Bool { 30 | return self.callHandler.accept(true, ofFunction: #function, atFile: #file, inLine: #line, withArgs: self.supportsKeepAlive) as! Bool 31 | } 32 | 33 | func negotiate(connection: ConnectionProtocol, connectionData: String?, completionHandler: ((_ response: NegotiationResponse?, _ error: Error?) -> ())?) { 34 | self.callHandler.accept(nil, ofFunction: #function, atFile: #file, inLine: #line, withArgs: connection, connectionData, completionHandler) 35 | } 36 | 37 | func start(connection: ConnectionProtocol, connectionData: String?, completionHandler: ((_ response: Any?, _ error: Error?) -> ())?) { 38 | self.callHandler.accept(nil, ofFunction: #function, atFile: #file, inLine: #line, withArgs: connection, connectionData, completionHandler) 39 | } 40 | 41 | func send(connection: ConnectionProtocol, data: Any, connectionData: String?, completionHandler: ((_ response: Any?, _ error: Error?) -> ())?) { 42 | self.callHandler.accept(nil, ofFunction: #function, atFile: #file, inLine: #line, withArgs: connection, connectionData, completionHandler) 43 | } 44 | 45 | func abort(connection: ConnectionProtocol, timeout: Double, connectionData: String?) { 46 | self.callHandler.accept(nil, ofFunction: #function, atFile: #file, inLine: #line, withArgs: connection, timeout, connectionData) 47 | } 48 | 49 | func lostConnection(connection: ConnectionProtocol) { 50 | self.callHandler.accept(nil, ofFunction: #function, atFile: #file, inLine: #line, withArgs: connection) 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /SignalRSwift.podspec: -------------------------------------------------------------------------------- 1 | # 2 | # Be sure to run `pod spec lint SignalR-Swift.podspec' to ensure this is a 3 | # valid spec and to remove all comments including this before submitting the spec. 4 | # 5 | # To learn more about Podspec attributes see http://docs.cocoapods.org/specification.html 6 | # To see working Podspecs in the CocoaPods repo see https://github.com/CocoaPods/Specs/ 7 | # 8 | 9 | Pod::Spec.new do |s| 10 | 11 | # ――― Spec Metadata ―――――――――――――――――――――――――――――――――――――――――――――――――――――――――― # 12 | # 13 | # These will help people to find your library, and whilst it 14 | # can feel like a chore to fill in it's definitely to your advantage. The 15 | # summary should be tweet-length, and the description more in depth. 16 | # 17 | 18 | s.name = "SignalRSwift" 19 | s.version = "2.0.3" 20 | s.summary = "A SignalR client library for iOS written in Swift" 21 | 22 | # This description is used to generate tags and improve search results. 23 | # * Think: What does it do? Why did you write it? What is the focus? 24 | # * Try to keep it short, snappy and to the point. 25 | # * Write the description between the DESC delimiters below. 26 | # * Finally, don't worry about the indent, CocoaPods strips it! 27 | s.description = "This is a signalR client written purely in swift using Alamofire, SwiftWebSockets and ObjectMapper" 28 | 29 | s.homepage = "https://github.com/AutosoftDMS/SignalR-Swift" 30 | 31 | # ――― Spec License ――――――――――――――――――――――――――――――――――――――――――――――――――――――――――― # 32 | # 33 | # Licensing your code is important. See http://choosealicense.com for more info. 34 | # CocoaPods will detect a license file if there is a named LICENSE* 35 | # Popular ones are 'MIT', 'BSD' and 'Apache License, Version 2.0'. 36 | # 37 | 38 | s.license = "MIT" 39 | # s.license = { :type => "MIT", :file => "FILE_LICENSE" } 40 | 41 | 42 | # ――― Author Metadata ――――――――――――――――――――――――――――――――――――――――――――――――――――――――― # 43 | # 44 | # Specify the authors of the library, with email addresses. Email addresses 45 | # of the authors are extracted from the SCM log. E.g. $ git log. CocoaPods also 46 | # accepts just a name if you'd rather not provide an email address. 47 | # 48 | # Specify a social_media_url where others can refer to, for example a twitter 49 | # profile URL. 50 | # 51 | 52 | s.author = { "Jordan Camara" => "jcamara@autosoftdms.com" } 53 | 54 | # ――― Platform Specifics ――――――――――――――――――――――――――――――――――――――――――――――――――――――― # 55 | # 56 | # If this Pod runs only on iOS or OS X, then specify the platform and 57 | # the deployment target. You can optionally include the target after the platform. 58 | # 59 | 60 | s.platform = :ios, "8.0" 61 | 62 | s.source = { :git => "https://github.com/AutosoftDMS/SignalR-Swift.git", :tag => "#{s.version}" } 63 | 64 | s.source_files = "SignalR-Swift/**/*.swift" 65 | s.exclude_files = "Classes/Exclude" 66 | 67 | s.dependency "Alamofire", "~> 4.2" 68 | s.dependency "Starscream", "~> 3.0" 69 | 70 | end 71 | --------------------------------------------------------------------------------