├── srv-SocketChat.zip
├── Starter_Project.zip
├── SocketChat.xcodeproj
├── project.xcworkspace
│ ├── contents.xcworkspacedata
│ └── xcuserdata
│ │ ├── simon.xcuserdatad
│ │ └── UserInterfaceState.xcuserstate
│ │ └── gabriel.xcuserdatad
│ │ └── UserInterfaceState.xcuserstate
├── xcuserdata
│ ├── simon.xcuserdatad
│ │ └── xcschemes
│ │ │ ├── xcschememanagement.plist
│ │ │ └── SocketChat.xcscheme
│ └── gabriel.xcuserdatad
│ │ └── xcschemes
│ │ ├── xcschememanagement.plist
│ │ └── SocketChat.xcscheme
└── project.pbxproj
├── README.md
├── SocketChat
├── UserCell.swift
├── ChatCell.swift
├── BaseCell.swift
├── Assets.xcassets
│ └── AppIcon.appiconset
│ │ └── Contents.json
├── Base.lproj
│ ├── LaunchScreen.storyboard
│ └── Main.storyboard
├── Info.plist
├── AppDelegate.swift
├── SocketIOManager.swift
├── UserCell.xib
├── UsersViewController.swift
├── ChatCell.xib
└── ChatViewController.swift
└── Source
├── SocketEnginePacketType.swift
├── SocketTypes.swift
├── SocketEngineClient.swift
├── SocketEventHandler.swift
├── SocketAnyEvent.swift
├── SocketIOClientStatus.swift
├── SocketFixUTF8.swift
├── SocketAckEmitter.swift
├── SocketClientSpec.swift
├── SocketLogger.swift
├── SocketAckManager.swift
├── SocketStringReader.swift
├── SocketEngineWebsocket.swift
├── SocketEngineSpec.swift
├── SwiftRegex.swift
├── SocketParsable.swift
├── SocketIOClientOption.swift
├── SocketEnginePollable.swift
├── SocketPacket.swift
├── SocketIOClient.swift
└── SocketEngine.swift
/srv-SocketChat.zip:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/appcoda/SocketIOChat/HEAD/srv-SocketChat.zip
--------------------------------------------------------------------------------
/Starter_Project.zip:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/appcoda/SocketIOChat/HEAD/Starter_Project.zip
--------------------------------------------------------------------------------
/SocketChat.xcodeproj/project.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/SocketChat.xcodeproj/project.xcworkspace/xcuserdata/simon.xcuserdatad/UserInterfaceState.xcuserstate:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/appcoda/SocketIOChat/HEAD/SocketChat.xcodeproj/project.xcworkspace/xcuserdata/simon.xcuserdatad/UserInterfaceState.xcuserstate
--------------------------------------------------------------------------------
/SocketChat.xcodeproj/project.xcworkspace/xcuserdata/gabriel.xcuserdatad/UserInterfaceState.xcuserstate:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/appcoda/SocketIOChat/HEAD/SocketChat.xcodeproj/project.xcworkspace/xcuserdata/gabriel.xcuserdatad/UserInterfaceState.xcuserstate
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Building an iOS Chat App Using Socket IO
2 |
3 | In order to show to you how to make use of the Socket.IO library (client) for iOS and how to achieve real-time communication with a server, I decided that our best hit would be to create a chat application as the demo app of this tutorial.
4 |
5 | For the full tutorial, please check out http://www.appcoda.com/socket-io-chat-app/
6 |
--------------------------------------------------------------------------------
/SocketChat/UserCell.swift:
--------------------------------------------------------------------------------
1 |
2 | //
3 | // UserCell.swift
4 | // SocketChat
5 | //
6 | // Created by Gabriel Theodoropoulos on 1/31/16.
7 | // Copyright © 2016 AppCoda. All rights reserved.
8 | //
9 |
10 | import UIKit
11 |
12 | class UserCell: BaseCell {
13 |
14 | override func awakeFromNib() {
15 | super.awakeFromNib()
16 | // Initialization code
17 | }
18 |
19 | override func setSelected(selected: Bool, animated: Bool) {
20 | super.setSelected(selected, animated: animated)
21 |
22 | // Configure the view for the selected state
23 | }
24 |
25 | }
26 |
--------------------------------------------------------------------------------
/SocketChat.xcodeproj/xcuserdata/simon.xcuserdatad/xcschemes/xcschememanagement.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | SchemeUserState
6 |
7 | SocketChat.xcscheme
8 |
9 | orderHint
10 | 0
11 |
12 |
13 | SuppressBuildableAutocreation
14 |
15 | C075D7181C5E0E81009F9044
16 |
17 | primary
18 |
19 |
20 |
21 |
22 |
23 |
--------------------------------------------------------------------------------
/SocketChat.xcodeproj/xcuserdata/gabriel.xcuserdatad/xcschemes/xcschememanagement.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | SchemeUserState
6 |
7 | SocketChat.xcscheme
8 |
9 | orderHint
10 | 0
11 |
12 |
13 | SuppressBuildableAutocreation
14 |
15 | C075D7181C5E0E81009F9044
16 |
17 | primary
18 |
19 |
20 |
21 |
22 |
23 |
--------------------------------------------------------------------------------
/SocketChat/ChatCell.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ChatCell.swift
3 | // SocketChat
4 | //
5 | // Created by Gabriel Theodoropoulos on 1/31/16.
6 | // Copyright © 2016 AppCoda. All rights reserved.
7 | //
8 |
9 | import UIKit
10 |
11 | class ChatCell: BaseCell {
12 |
13 | @IBOutlet weak var lblChatMessage: UILabel!
14 |
15 | @IBOutlet weak var lblMessageDetails: UILabel!
16 |
17 |
18 | override func awakeFromNib() {
19 | super.awakeFromNib()
20 | // Initialization code
21 |
22 | }
23 |
24 | override func setSelected(selected: Bool, animated: Bool) {
25 | super.setSelected(selected, animated: animated)
26 |
27 | // Configure the view for the selected state
28 | }
29 |
30 | }
31 |
--------------------------------------------------------------------------------
/SocketChat/BaseCell.swift:
--------------------------------------------------------------------------------
1 | //
2 | // BaseCell.swift
3 | // SocketChat
4 | //
5 | // Created by Gabriel Theodoropoulos on 1/31/16.
6 | // Copyright © 2016 AppCoda. All rights reserved.
7 | //
8 |
9 | import UIKit
10 |
11 | class BaseCell: UITableViewCell {
12 |
13 | override func awakeFromNib() {
14 | super.awakeFromNib()
15 | // Initialization code
16 |
17 | separatorInset = UIEdgeInsetsZero
18 | preservesSuperviewLayoutMargins = false
19 | layoutMargins = UIEdgeInsetsZero
20 | layoutIfNeeded()
21 |
22 | // Set the selection style to None.
23 | selectionStyle = UITableViewCellSelectionStyle.None
24 | }
25 |
26 | override func setSelected(selected: Bool, animated: Bool) {
27 | super.setSelected(selected, animated: animated)
28 |
29 | // Configure the view for the selected state
30 | }
31 |
32 | }
33 |
--------------------------------------------------------------------------------
/SocketChat/Assets.xcassets/AppIcon.appiconset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "iphone",
5 | "size" : "29x29",
6 | "scale" : "2x"
7 | },
8 | {
9 | "idiom" : "iphone",
10 | "size" : "29x29",
11 | "scale" : "3x"
12 | },
13 | {
14 | "idiom" : "iphone",
15 | "size" : "40x40",
16 | "scale" : "2x"
17 | },
18 | {
19 | "idiom" : "iphone",
20 | "size" : "40x40",
21 | "scale" : "3x"
22 | },
23 | {
24 | "idiom" : "iphone",
25 | "size" : "60x60",
26 | "scale" : "2x"
27 | },
28 | {
29 | "idiom" : "iphone",
30 | "size" : "60x60",
31 | "scale" : "3x"
32 | },
33 | {
34 | "idiom" : "ipad",
35 | "size" : "29x29",
36 | "scale" : "1x"
37 | },
38 | {
39 | "idiom" : "ipad",
40 | "size" : "29x29",
41 | "scale" : "2x"
42 | },
43 | {
44 | "idiom" : "ipad",
45 | "size" : "40x40",
46 | "scale" : "1x"
47 | },
48 | {
49 | "idiom" : "ipad",
50 | "size" : "40x40",
51 | "scale" : "2x"
52 | },
53 | {
54 | "idiom" : "ipad",
55 | "size" : "76x76",
56 | "scale" : "1x"
57 | },
58 | {
59 | "idiom" : "ipad",
60 | "size" : "76x76",
61 | "scale" : "2x"
62 | }
63 | ],
64 | "info" : {
65 | "version" : 1,
66 | "author" : "xcode"
67 | }
68 | }
--------------------------------------------------------------------------------
/Source/SocketEnginePacketType.swift:
--------------------------------------------------------------------------------
1 | //
2 | // SocketEnginePacketType.swift
3 | // Socket.IO-Client-Swift
4 | //
5 | // Created by Erik Little on 10/7/15.
6 | //
7 | // Permission is hereby granted, free of charge, to any person obtaining a copy
8 | // of this software and associated documentation files (the "Software"), to deal
9 | // in the Software without restriction, including without limitation the rights
10 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11 | // copies of the Software, and to permit persons to whom the Software is
12 | // furnished to do so, subject to the following conditions:
13 | //
14 | // The above copyright notice and this permission notice shall be included in
15 | // all copies or substantial portions of the Software.
16 | //
17 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
23 | // THE SOFTWARE.
24 | //
25 |
26 | import Foundation
27 |
28 | @objc public enum SocketEnginePacketType: Int {
29 | case Open, Close, Ping, Pong, Message, Upgrade, Noop
30 | }
--------------------------------------------------------------------------------
/Source/SocketTypes.swift:
--------------------------------------------------------------------------------
1 | //
2 | // SocketTypes.swift
3 | // Socket.IO-Client-Swift
4 | //
5 | // Created by Erik Little on 4/8/15.
6 | //
7 | // Permission is hereby granted, free of charge, to any person obtaining a copy
8 | // of this software and associated documentation files (the "Software"), to deal
9 | // in the Software without restriction, including without limitation the rights
10 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11 | // copies of the Software, and to permit persons to whom the Software is
12 | // furnished to do so, subject to the following conditions:
13 | //
14 | // The above copyright notice and this permission notice shall be included in
15 | // all copies or substantial portions of the Software.
16 | //
17 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
23 | // THE SOFTWARE.
24 |
25 | import Foundation
26 |
27 | public typealias AckCallback = ([AnyObject]) -> Void
28 | public typealias NormalCallback = ([AnyObject], SocketAckEmitter) -> Void
29 | public typealias OnAckCallback = (timeoutAfter: UInt64, callback: AckCallback) -> Void
30 |
31 | enum Either {
32 | case Left(E)
33 | case Right(V)
34 | }
35 |
--------------------------------------------------------------------------------
/Source/SocketEngineClient.swift:
--------------------------------------------------------------------------------
1 | //
2 | // SocketEngineClient.swift
3 | // Socket.IO-Client-Swift
4 | //
5 | // Created by Erik Little on 3/19/15.
6 | //
7 | // Permission is hereby granted, free of charge, to any person obtaining a copy
8 | // of this software and associated documentation files (the "Software"), to deal
9 | // in the Software without restriction, including without limitation the rights
10 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11 | // copies of the Software, and to permit persons to whom the Software is
12 | // furnished to do so, subject to the following conditions:
13 | //
14 | // The above copyright notice and this permission notice shall be included in
15 | // all copies or substantial portions of the Software.
16 | //
17 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
23 | // THE SOFTWARE.
24 | //
25 |
26 | import Foundation
27 |
28 | @objc public protocol SocketEngineClient {
29 | func engineDidError(reason: String)
30 | func engineDidClose(reason: String)
31 | optional func engineDidOpen(reason: String)
32 | func parseEngineMessage(msg: String)
33 | func parseEngineBinaryData(data: NSData)
34 | }
35 |
--------------------------------------------------------------------------------
/Source/SocketEventHandler.swift:
--------------------------------------------------------------------------------
1 | //
2 | // EventHandler.swift
3 | // Socket.IO-Client-Swift
4 | //
5 | // Created by Erik Little on 1/18/15.
6 | //
7 | // Permission is hereby granted, free of charge, to any person obtaining a copy
8 | // of this software and associated documentation files (the "Software"), to deal
9 | // in the Software without restriction, including without limitation the rights
10 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11 | // copies of the Software, and to permit persons to whom the Software is
12 | // furnished to do so, subject to the following conditions:
13 | //
14 | // The above copyright notice and this permission notice shall be included in
15 | // all copies or substantial portions of the Software.
16 | //
17 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
23 | // THE SOFTWARE.
24 |
25 | import Foundation
26 |
27 | struct SocketEventHandler {
28 | let event: String
29 | let id: NSUUID
30 | let callback: NormalCallback
31 |
32 | func executeCallback(items: [AnyObject], withAck ack: Int, withSocket socket: SocketIOClient) {
33 | callback(items, SocketAckEmitter(socket: socket, ackNum: ack))
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/Source/SocketAnyEvent.swift:
--------------------------------------------------------------------------------
1 | //
2 | // SocketAnyEvent.swift
3 | // Socket.IO-Client-Swift
4 | //
5 | // Created by Erik Little on 3/28/15.
6 | //
7 | // Permission is hereby granted, free of charge, to any person obtaining a copy
8 | // of this software and associated documentation files (the "Software"), to deal
9 | // in the Software without restriction, including without limitation the rights
10 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11 | // copies of the Software, and to permit persons to whom the Software is
12 | // furnished to do so, subject to the following conditions:
13 | //
14 | // The above copyright notice and this permission notice shall be included in
15 | // all copies or substantial portions of the Software.
16 | //
17 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
23 | // THE SOFTWARE.
24 |
25 | import Foundation
26 |
27 | @objc public final class SocketAnyEvent: NSObject {
28 | public let event: String!
29 | public let items: NSArray?
30 | override public var description: String {
31 | return "SocketAnyEvent: Event: \(event) items: \(items ?? nil)"
32 | }
33 |
34 | init(event: String, items: NSArray?) {
35 | self.event = event
36 | self.items = items
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/SocketChat/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 |
--------------------------------------------------------------------------------
/SocketChat/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 | CFBundleSignature
20 | ????
21 | CFBundleVersion
22 | 1
23 | LSRequiresIPhoneOS
24 |
25 | UILaunchStoryboardName
26 | LaunchScreen
27 | UIMainStoryboardFile
28 | Main
29 | UIRequiredDeviceCapabilities
30 |
31 | armv7
32 |
33 | UISupportedInterfaceOrientations
34 |
35 | UIInterfaceOrientationPortrait
36 | UIInterfaceOrientationLandscapeLeft
37 | UIInterfaceOrientationLandscapeRight
38 |
39 | UISupportedInterfaceOrientations~ipad
40 |
41 | UIInterfaceOrientationPortrait
42 | UIInterfaceOrientationPortraitUpsideDown
43 | UIInterfaceOrientationLandscapeLeft
44 | UIInterfaceOrientationLandscapeRight
45 |
46 | NSAppTransportSecurity
47 |
48 | NSAllowsArbitraryLoads
49 |
50 |
51 |
52 |
53 |
--------------------------------------------------------------------------------
/Source/SocketIOClientStatus.swift:
--------------------------------------------------------------------------------
1 | //
2 | // SocketIOClientStatus.swift
3 | // Socket.IO-Client-Swift
4 | //
5 | // Created by Erik Little on 8/14/15.
6 | //
7 | // Permission is hereby granted, free of charge, to any person obtaining a copy
8 | // of this software and associated documentation files (the "Software"), to deal
9 | // in the Software without restriction, including without limitation the rights
10 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11 | // copies of the Software, and to permit persons to whom the Software is
12 | // furnished to do so, subject to the following conditions:
13 | //
14 | // The above copyright notice and this permission notice shall be included in
15 | // all copies or substantial portions of the Software.
16 | //
17 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
23 | // THE SOFTWARE.
24 |
25 | import Foundation
26 |
27 | @objc public enum SocketIOClientStatus: Int, CustomStringConvertible {
28 | case NotConnected, Closed, Connecting, Connected, Reconnecting
29 |
30 | public var description: String {
31 | switch self {
32 | case NotConnected:
33 | return "Not Connected"
34 | case Closed:
35 | return "Closed"
36 | case Connecting:
37 | return "Connecting"
38 | case Connected:
39 | return "Connected"
40 | case Reconnecting:
41 | return "Reconnecting"
42 | }
43 | }
44 | }
--------------------------------------------------------------------------------
/Source/SocketFixUTF8.swift:
--------------------------------------------------------------------------------
1 | //
2 | // SocketFixUTF8.swift
3 | // Socket.IO-Client-Swift
4 | //
5 | // Created by Erik Little on 3/16/15.
6 | //
7 | // Permission is hereby granted, free of charge, to any person obtaining a copy
8 | // of this software and associated documentation files (the "Software"), to deal
9 | // in the Software without restriction, including without limitation the rights
10 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11 | // copies of the Software, and to permit persons to whom the Software is
12 | // furnished to do so, subject to the following conditions:
13 | //
14 | // The above copyright notice and this permission notice shall be included in
15 | // all copies or substantial portions of the Software.
16 | //
17 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
23 | // THE SOFTWARE.
24 | //
25 |
26 | import Foundation
27 |
28 | func fixDoubleUTF8(string: String) -> String {
29 | if let utf8 = string.dataUsingEncoding(NSISOLatin1StringEncoding),
30 | latin1 = NSString(data: utf8, encoding: NSUTF8StringEncoding) {
31 | return latin1 as String
32 | } else {
33 | return string
34 | }
35 | }
36 |
37 | func doubleEncodeUTF8(string: String) -> String {
38 | if let latin1 = string.dataUsingEncoding(NSUTF8StringEncoding),
39 | utf8 = NSString(data: latin1, encoding: NSISOLatin1StringEncoding) {
40 | return utf8 as String
41 | } else {
42 | return string
43 | }
44 | }
45 |
--------------------------------------------------------------------------------
/Source/SocketAckEmitter.swift:
--------------------------------------------------------------------------------
1 | //
2 | // SocketAckEmitter.swift
3 | // Socket.IO-Client-Swift
4 | //
5 | // Created by Erik Little on 9/16/15.
6 | //
7 | // Permission is hereby granted, free of charge, to any person obtaining a copy
8 | // of this software and associated documentation files (the "Software"), to deal
9 | // in the Software without restriction, including without limitation the rights
10 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11 | // copies of the Software, and to permit persons to whom the Software is
12 | // furnished to do so, subject to the following conditions:
13 | //
14 | // The above copyright notice and this permission notice shall be included in
15 | // all copies or substantial portions of the Software.
16 | //
17 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
23 | // THE SOFTWARE.
24 |
25 | import Foundation
26 |
27 | public final class SocketAckEmitter: NSObject {
28 | let socket: SocketIOClient
29 | let ackNum: Int
30 |
31 | init(socket: SocketIOClient, ackNum: Int) {
32 | self.socket = socket
33 | self.ackNum = ackNum
34 | }
35 |
36 | public func with(items: AnyObject...) {
37 | guard ackNum != -1 else { return }
38 |
39 | socket.emitAck(ackNum, withItems: items)
40 | }
41 |
42 | public func with(items: [AnyObject]) {
43 | guard ackNum != -1 else { return }
44 |
45 | socket.emitAck(ackNum, withItems: items)
46 | }
47 | }
48 |
--------------------------------------------------------------------------------
/Source/SocketClientSpec.swift:
--------------------------------------------------------------------------------
1 | //
2 | // SocketClientSpec.swift
3 | // Socket.IO-Client-Swift
4 | //
5 | // Created by Erik Little on 1/3/16.
6 | //
7 | // Permission is hereby granted, free of charge, to any person obtaining a copy
8 | // of this software and associated documentation files (the "Software"), to deal
9 | // in the Software without restriction, including without limitation the rights
10 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11 | // copies of the Software, and to permit persons to whom the Software is
12 | // furnished to do so, subject to the following conditions:
13 | //
14 | // The above copyright notice and this permission notice shall be included in
15 | // all copies or substantial portions of the Software.
16 | //
17 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
23 | // THE SOFTWARE.
24 |
25 | protocol SocketClientSpec: class {
26 | var nsp: String { get set }
27 | var waitingData: [SocketPacket] { get set }
28 |
29 | func didConnect()
30 | func didDisconnect(reason: String)
31 | func didError(reason: String)
32 | func handleAck(ack: Int, data: [AnyObject])
33 | func handleEvent(event: String, data: [AnyObject], isInternalMessage: Bool, withAck ack: Int)
34 | func joinNamespace(namespace: String)
35 | }
36 |
37 | extension SocketClientSpec {
38 | func didError(reason: String) {
39 | DefaultSocketLogger.Logger.error("%@", type: "SocketIOClient", args: reason)
40 |
41 | handleEvent("error", data: [reason], isInternalMessage: true, withAck: -1)
42 | }
43 | }
44 |
--------------------------------------------------------------------------------
/SocketChat/AppDelegate.swift:
--------------------------------------------------------------------------------
1 | //
2 | // AppDelegate.swift
3 | // SocketChat
4 | //
5 | // Created by Gabriel Theodoropoulos on 1/31/16.
6 | // Copyright © 2016 AppCoda. 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: [NSObject: AnyObject]?) -> 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 throttle down OpenGL ES frame rates. 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 | SocketIOManager.sharedInstance.closeConnection()
32 | }
33 |
34 | func applicationWillEnterForeground(application: UIApplication) {
35 | // Called as part of the transition from the background to the inactive state; here you can undo many of the changes made on entering the background.
36 | }
37 |
38 | func applicationDidBecomeActive(application: UIApplication) {
39 | // 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.
40 |
41 | SocketIOManager.sharedInstance.establishConnection()
42 | }
43 |
44 | func applicationWillTerminate(application: UIApplication) {
45 | // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:.
46 | }
47 |
48 |
49 | }
50 |
51 |
--------------------------------------------------------------------------------
/Source/SocketLogger.swift:
--------------------------------------------------------------------------------
1 | //
2 | // SocketLogger.swift
3 | // Socket.IO-Client-Swift
4 | //
5 | // Created by Erik Little on 4/11/15.
6 | //
7 | // Permission is hereby granted, free of charge, to any person obtaining a copy
8 | // of this software and associated documentation files (the "Software"), to deal
9 | // in the Software without restriction, including without limitation the rights
10 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11 | // copies of the Software, and to permit persons to whom the Software is
12 | // furnished to do so, subject to the following conditions:
13 | //
14 | // The above copyright notice and this permission notice shall be included in
15 | // all copies or substantial portions of the Software.
16 | //
17 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
23 | // THE SOFTWARE.
24 |
25 | import Foundation
26 |
27 | public protocol SocketLogger: class {
28 | /// Whether to log or not
29 | var log: Bool {get set}
30 |
31 | /// Normal log messages
32 | func log(message: String, type: String, args: AnyObject...)
33 |
34 | /// Error Messages
35 | func error(message: String, type: String, args: AnyObject...)
36 | }
37 |
38 | public extension SocketLogger {
39 | func log(message: String, type: String, args: AnyObject...) {
40 | abstractLog("LOG", message: message, type: type, args: args)
41 | }
42 |
43 | func error(message: String, type: String, args: AnyObject...) {
44 | abstractLog("ERROR", message: message, type: type, args: args)
45 | }
46 |
47 | private func abstractLog(logType: String, message: String, type: String, args: [AnyObject]) {
48 | guard log else { return }
49 |
50 | let newArgs = args.map({arg -> CVarArgType in String(arg)})
51 | let replaced = String(format: message, arguments: newArgs)
52 |
53 | NSLog("%@ %@: %@", logType, type, replaced)
54 | }
55 | }
56 |
57 | class DefaultSocketLogger: SocketLogger {
58 | static var Logger: SocketLogger = DefaultSocketLogger()
59 |
60 | var log = false
61 | }
62 |
--------------------------------------------------------------------------------
/Source/SocketAckManager.swift:
--------------------------------------------------------------------------------
1 | //
2 | // SocketAckManager.swift
3 | // Socket.IO-Client-Swift
4 | //
5 | // Created by Erik Little on 4/3/15.
6 | //
7 | // Permission is hereby granted, free of charge, to any person obtaining a copy
8 | // of this software and associated documentation files (the "Software"), to deal
9 | // in the Software without restriction, including without limitation the rights
10 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11 | // copies of the Software, and to permit persons to whom the Software is
12 | // furnished to do so, subject to the following conditions:
13 | //
14 | // The above copyright notice and this permission notice shall be included in
15 | // all copies or substantial portions of the Software.
16 | //
17 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
23 | // THE SOFTWARE.
24 |
25 | import Foundation
26 |
27 | private struct SocketAck: Hashable, Equatable {
28 | let ack: Int
29 | var callback: AckCallback!
30 | var hashValue: Int {
31 | return ack.hashValue
32 | }
33 |
34 | init(ack: Int) {
35 | self.ack = ack
36 | }
37 |
38 | init(ack: Int, callback: AckCallback) {
39 | self.ack = ack
40 | self.callback = callback
41 | }
42 | }
43 |
44 | private func <(lhs: SocketAck, rhs: SocketAck) -> Bool {
45 | return lhs.ack < rhs.ack
46 | }
47 |
48 | private func ==(lhs: SocketAck, rhs: SocketAck) -> Bool {
49 | return lhs.ack == rhs.ack
50 | }
51 |
52 | struct SocketAckManager {
53 | private var acks = Set(minimumCapacity: 1)
54 |
55 | mutating func addAck(ack: Int, callback: AckCallback) {
56 | acks.insert(SocketAck(ack: ack, callback: callback))
57 | }
58 |
59 | mutating func executeAck(ack: Int, items: [AnyObject]) {
60 | let callback = acks.remove(SocketAck(ack: ack))
61 |
62 | dispatch_async(dispatch_get_main_queue()) {
63 | callback?.callback(items)
64 | }
65 | }
66 |
67 | mutating func timeoutAck(ack: Int) {
68 | let callback = acks.remove(SocketAck(ack: ack))
69 |
70 | dispatch_async(dispatch_get_main_queue()) {
71 | callback?.callback(["NO ACK"])
72 | }
73 | }
74 | }
75 |
--------------------------------------------------------------------------------
/Source/SocketStringReader.swift:
--------------------------------------------------------------------------------
1 | //
2 | // SocketStringReader.swift
3 | // Socket.IO-Client-Swift
4 | //
5 | // Created by Lukas Schmidt on 07.09.15.
6 | //
7 | // Permission is hereby granted, free of charge, to any person obtaining a copy
8 | // of this software and associated documentation files (the "Software"), to deal
9 | // in the Software without restriction, including without limitation the rights
10 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11 | // copies of the Software, and to permit persons to whom the Software is
12 | // furnished to do so, subject to the following conditions:
13 | //
14 | // The above copyright notice and this permission notice shall be included in
15 | // all copies or substantial portions of the Software.
16 | //
17 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
23 | // THE SOFTWARE.
24 |
25 | struct SocketStringReader {
26 | let message: String
27 | var currentIndex: String.Index
28 | var hasNext: Bool {
29 | return currentIndex != message.endIndex
30 | }
31 |
32 | var currentCharacter: String {
33 | return String(message[currentIndex])
34 | }
35 |
36 | init(message: String) {
37 | self.message = message
38 | currentIndex = message.startIndex
39 | }
40 |
41 | mutating func advanceIndexBy(n: Int) {
42 | currentIndex = currentIndex.advancedBy(n)
43 | }
44 |
45 | mutating func read(readLength: Int) -> String {
46 | let readString = message[currentIndex.. String {
53 | let substring = message[currentIndex.. String {
66 | return read(currentIndex.distanceTo(message.endIndex))
67 | }
68 | }
--------------------------------------------------------------------------------
/Source/SocketEngineWebsocket.swift:
--------------------------------------------------------------------------------
1 | //
2 | // SocketEngineWebsocket.swift
3 | // Socket.IO-Client-Swift
4 | //
5 | // Created by Erik Little on 1/15/16.
6 | //
7 | // Permission is hereby granted, free of charge, to any person obtaining a copy
8 | // of this software and associated documentation files (the "Software"), to deal
9 | // in the Software without restriction, including without limitation the rights
10 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11 | // copies of the Software, and to permit persons to whom the Software is
12 | // furnished to do so, subject to the following conditions:
13 | //
14 | // The above copyright notice and this permission notice shall be included in
15 | // all copies or substantial portions of the Software.
16 | //
17 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
23 | // THE SOFTWARE.
24 | //
25 |
26 | import Foundation
27 |
28 | /// Protocol that is used to implement socket.io WebSocket support
29 | public protocol SocketEngineWebsocket: SocketEngineSpec, WebSocketDelegate {
30 | var ws: WebSocket? { get }
31 |
32 | func sendWebSocketMessage(str: String, withType type: SocketEnginePacketType, withData datas: [NSData])
33 | }
34 |
35 | // WebSocket methods
36 | extension SocketEngineWebsocket {
37 | func probeWebSocket() {
38 | if ws?.isConnected ?? false {
39 | sendWebSocketMessage("probe", withType: .Ping, withData: [])
40 | }
41 | }
42 |
43 | /// Send message on WebSockets
44 | /// Only call on emitQueue
45 | public func sendWebSocketMessage(str: String, withType type: SocketEnginePacketType, withData datas: [NSData]) {
46 | DefaultSocketLogger.Logger.log("Sending ws: %@ as type: %@", type: "SocketEngine", args: str, type.rawValue)
47 |
48 | ws?.writeString("\(type.rawValue)\(str)")
49 |
50 | for data in datas {
51 | if case let .Left(bin) = createBinaryDataForSend(data) {
52 | ws?.writeData(bin)
53 | }
54 | }
55 | }
56 |
57 | public func websocketDidReceiveMessage(socket: WebSocket, text: String) {
58 | parseEngineMessage(text, fromPolling: false)
59 | }
60 |
61 | public func websocketDidReceiveData(socket: WebSocket, data: NSData) {
62 | parseEngineData(data)
63 | }
64 | }
65 |
--------------------------------------------------------------------------------
/SocketChat/SocketIOManager.swift:
--------------------------------------------------------------------------------
1 | //
2 | // SocketIOManager.swift
3 | // SocketChat
4 | //
5 | // Created by Gabriel Theodoropoulos on 1/31/16.
6 | // Copyright © 2016 AppCoda. All rights reserved.
7 | //
8 |
9 | import UIKit
10 |
11 | class SocketIOManager: NSObject {
12 | static let sharedInstance = SocketIOManager()
13 |
14 | var socket: SocketIOClient = SocketIOClient(socketURL: NSURL(string: "http://192.168.1.XXX:3000")!)
15 |
16 |
17 | override init() {
18 | super.init()
19 | }
20 |
21 |
22 | func establishConnection() {
23 | socket.connect()
24 | }
25 |
26 |
27 | func closeConnection() {
28 | socket.disconnect()
29 | }
30 |
31 |
32 | func connectToServerWithNickname(nickname: String, completionHandler: (userList: [[String: AnyObject]]!) -> Void) {
33 | socket.emit("connectUser", nickname)
34 |
35 | socket.on("userList") { ( dataArray, ack) -> Void in
36 | completionHandler(userList: dataArray[0] as! [[String: AnyObject]])
37 | }
38 |
39 | listenForOtherMessages()
40 | }
41 |
42 |
43 | func exitChatWithNickname(nickname: String, completionHandler: () -> Void) {
44 | socket.emit("exitUser", nickname)
45 | completionHandler()
46 | }
47 |
48 |
49 | func sendMessage(message: String, withNickname nickname: String) {
50 | socket.emit("chatMessage", nickname, message)
51 | }
52 |
53 |
54 | func getChatMessage(completionHandler: (messageInfo: [String: AnyObject]) -> Void) {
55 | socket.on("newChatMessage") { (dataArray, socketAck) -> Void in
56 | var messageDictionary = [String: AnyObject]()
57 | messageDictionary["nickname"] = dataArray[0] as! String
58 | messageDictionary["message"] = dataArray[1] as! String
59 | messageDictionary["date"] = dataArray[2] as! String
60 |
61 | completionHandler(messageInfo: messageDictionary)
62 | }
63 | }
64 |
65 |
66 | private func listenForOtherMessages() {
67 | socket.on("userConnectUpdate") { (dataArray, socketAck) -> Void in
68 | NSNotificationCenter.defaultCenter().postNotificationName("userWasConnectedNotification", object: dataArray[0] as! [String: AnyObject])
69 | }
70 |
71 | socket.on("userExitUpdate") { (dataArray, socketAck) -> Void in
72 | NSNotificationCenter.defaultCenter().postNotificationName("userWasDisconnectedNotification", object: dataArray[0] as! String)
73 | }
74 |
75 | socket.on("userTypingUpdate") { (dataArray, socketAck) -> Void in
76 | NSNotificationCenter.defaultCenter().postNotificationName("userTypingNotification", object: dataArray[0] as? [String: AnyObject])
77 | }
78 | }
79 |
80 |
81 | func sendStartTypingMessage(nickname: String) {
82 | socket.emit("startType", nickname)
83 | }
84 |
85 |
86 | func sendStopTypingMessage(nickname: String) {
87 | socket.emit("stopType", nickname)
88 | }
89 | }
90 |
--------------------------------------------------------------------------------
/SocketChat/UserCell.xib:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
24 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
--------------------------------------------------------------------------------
/Source/SocketEngineSpec.swift:
--------------------------------------------------------------------------------
1 | //
2 | // SocketEngineSpec.swift
3 | // Socket.IO-Client-Swift
4 | //
5 | // Created by Erik Little on 10/7/15.
6 | //
7 | // Permission is hereby granted, free of charge, to any person obtaining a copy
8 | // of this software and associated documentation files (the "Software"), to deal
9 | // in the Software without restriction, including without limitation the rights
10 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11 | // copies of the Software, and to permit persons to whom the Software is
12 | // furnished to do so, subject to the following conditions:
13 | //
14 | // The above copyright notice and this permission notice shall be included in
15 | // all copies or substantial portions of the Software.
16 | //
17 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
23 | // THE SOFTWARE.
24 | //
25 |
26 | import Foundation
27 |
28 | @objc public protocol SocketEngineSpec {
29 | weak var client: SocketEngineClient? { get set }
30 | var closed: Bool { get }
31 | var connected: Bool { get }
32 | var connectParams: [String: AnyObject]? { get set }
33 | var cookies: [NSHTTPCookie]? { get }
34 | var extraHeaders: [String: String]? { get }
35 | var fastUpgrade: Bool { get }
36 | var forcePolling: Bool { get }
37 | var forceWebsockets: Bool { get }
38 | var parseQueue: dispatch_queue_t! { get }
39 | var pingTimer: NSTimer? { get }
40 | var polling: Bool { get }
41 | var probing: Bool { get }
42 | var emitQueue: dispatch_queue_t! { get }
43 | var handleQueue: dispatch_queue_t! { get }
44 | var sid: String { get }
45 | var socketPath: String { get }
46 | var urlPolling: NSURL { get }
47 | var urlWebSocket: NSURL { get }
48 | var websocket: Bool { get }
49 |
50 | init(client: SocketEngineClient, url: NSURL, options: NSDictionary?)
51 |
52 | func close(reason: String)
53 | func didError(error: String)
54 | func doFastUpgrade()
55 | func flushWaitingForPostToWebSocket()
56 | func open()
57 | func parseEngineData(data: NSData)
58 | func parseEngineMessage(message: String, fromPolling: Bool)
59 | func write(msg: String, withType type: SocketEnginePacketType, withData data: [NSData])
60 | }
61 |
62 | extension SocketEngineSpec {
63 | var urlPollingWithSid: NSURL {
64 | let com = NSURLComponents(URL: urlPolling, resolvingAgainstBaseURL: false)!
65 | com.query = com.query! + "&sid=\(sid)"
66 |
67 | return com.URL!
68 | }
69 |
70 | func createBinaryDataForSend(data: NSData) -> Either {
71 | if websocket {
72 | var byteArray = [UInt8](count: 1, repeatedValue: 0x4)
73 | let mutData = NSMutableData(bytes: &byteArray, length: 1)
74 |
75 | mutData.appendData(data)
76 |
77 | return .Left(mutData)
78 | } else {
79 | let str = "b4" + data.base64EncodedStringWithOptions(.Encoding64CharacterLineLength)
80 |
81 | return .Right(str)
82 | }
83 | }
84 |
85 | /// Send an engine message (4)
86 | func send(msg: String, withData datas: [NSData]) {
87 | write(msg, withType: .Message, withData: datas)
88 | }
89 | }
90 |
--------------------------------------------------------------------------------
/SocketChat.xcodeproj/xcuserdata/gabriel.xcuserdatad/xcschemes/SocketChat.xcscheme:
--------------------------------------------------------------------------------
1 |
2 |
5 |
8 |
9 |
15 |
21 |
22 |
23 |
24 |
25 |
30 |
31 |
32 |
33 |
39 |
40 |
41 |
42 |
43 |
44 |
54 |
56 |
62 |
63 |
64 |
65 |
66 |
67 |
73 |
75 |
81 |
82 |
83 |
84 |
86 |
87 |
90 |
91 |
92 |
--------------------------------------------------------------------------------
/SocketChat.xcodeproj/xcuserdata/simon.xcuserdatad/xcschemes/SocketChat.xcscheme:
--------------------------------------------------------------------------------
1 |
2 |
5 |
8 |
9 |
15 |
21 |
22 |
23 |
24 |
25 |
30 |
31 |
32 |
33 |
39 |
40 |
41 |
42 |
43 |
44 |
54 |
56 |
62 |
63 |
64 |
65 |
66 |
67 |
73 |
75 |
81 |
82 |
83 |
84 |
86 |
87 |
90 |
91 |
92 |
--------------------------------------------------------------------------------
/SocketChat/UsersViewController.swift:
--------------------------------------------------------------------------------
1 | //
2 | // UsersViewController.swift
3 | // SocketChat
4 | //
5 | // Created by Gabriel Theodoropoulos on 1/31/16.
6 | // Copyright © 2016 AppCoda. All rights reserved.
7 | //
8 |
9 | import UIKit
10 |
11 | class UsersViewController: UIViewController, UITableViewDelegate, UITableViewDataSource {
12 |
13 | @IBOutlet weak var tblUserList: UITableView!
14 |
15 |
16 | var users = [[String: AnyObject]]()
17 |
18 | var nickname: String!
19 |
20 | var configurationOK = false
21 |
22 |
23 | override func viewDidLoad() {
24 | super.viewDidLoad()
25 |
26 | // Do any additional setup after loading the view.
27 | }
28 |
29 |
30 | override func viewWillAppear(animated: Bool) {
31 | super.viewWillAppear(animated)
32 |
33 | if !configurationOK {
34 | configureNavigationBar()
35 | configureTableView()
36 | configurationOK = true
37 | }
38 |
39 | }
40 |
41 |
42 | override func viewDidAppear(animated: Bool) {
43 | super.viewDidAppear(animated)
44 |
45 | if nickname == nil {
46 | askForNickname()
47 | }
48 | }
49 |
50 |
51 | override func didReceiveMemoryWarning() {
52 | super.didReceiveMemoryWarning()
53 | // Dispose of any resources that can be recreated.
54 | }
55 |
56 |
57 |
58 | // MARK: - Navigation
59 |
60 | // In a storyboard-based application, you will often want to do a little preparation before navigation
61 | override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
62 | if let identifier = segue.identifier {
63 | if identifier == "idSegueJoinChat" {
64 | let chatViewController = segue.destinationViewController as! ChatViewController
65 | chatViewController.nickname = nickname
66 | }
67 | }
68 | }
69 |
70 |
71 | // MARK: IBAction Methods
72 |
73 | @IBAction func exitChat(sender: AnyObject) {
74 | SocketIOManager.sharedInstance.exitChatWithNickname(nickname) { () -> Void in
75 | dispatch_async(dispatch_get_main_queue(), { () -> Void in
76 | self.nickname = nil
77 | self.users.removeAll()
78 | self.tblUserList.hidden = true
79 | self.askForNickname()
80 | })
81 | }
82 | }
83 |
84 |
85 |
86 | // MARK: Custom Methods
87 |
88 | func configureNavigationBar() {
89 | navigationItem.title = "SocketChat"
90 | }
91 |
92 |
93 | func configureTableView() {
94 | tblUserList.delegate = self
95 | tblUserList.dataSource = self
96 | tblUserList.registerNib(UINib(nibName: "UserCell", bundle: nil), forCellReuseIdentifier: "idCellUser")
97 | tblUserList.hidden = true
98 | tblUserList.tableFooterView = UIView(frame: CGRectZero)
99 | }
100 |
101 |
102 | func askForNickname() {
103 | let alertController = UIAlertController(title: "SocketChat", message: "Please enter a nickname:", preferredStyle: UIAlertControllerStyle.Alert)
104 |
105 | alertController.addTextFieldWithConfigurationHandler(nil)
106 |
107 | let OKAction = UIAlertAction(title: "OK", style: UIAlertActionStyle.Default) { (action) -> Void in
108 | let textfield = alertController.textFields![0]
109 | if textfield.text?.characters.count == 0 {
110 | self.askForNickname()
111 | }
112 | else {
113 | self.nickname = textfield.text
114 |
115 | SocketIOManager.sharedInstance.connectToServerWithNickname(self.nickname, completionHandler: { (userList) -> Void in
116 | dispatch_async(dispatch_get_main_queue(), { () -> Void in
117 | if userList != nil {
118 | self.users = userList
119 | self.tblUserList.reloadData()
120 | self.tblUserList.hidden = false
121 | }
122 | })
123 | })
124 | }
125 | }
126 |
127 | alertController.addAction(OKAction)
128 | presentViewController(alertController, animated: true, completion: nil)
129 | }
130 |
131 |
132 | // MARK: UITableView Delegate and Datasource methods
133 |
134 | func numberOfSectionsInTableView(tableView: UITableView) -> Int {
135 | return 1
136 | }
137 |
138 |
139 | func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
140 | return users.count
141 | }
142 |
143 |
144 | func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
145 | let cell = tableView.dequeueReusableCellWithIdentifier("idCellUser", forIndexPath: indexPath) as! UserCell
146 |
147 | cell.textLabel?.text = users[indexPath.row]["nickname"] as? String
148 | cell.detailTextLabel?.text = (users[indexPath.row]["isConnected"] as! Bool) ? "Online" : "Offline"
149 | cell.detailTextLabel?.textColor = (users[indexPath.row]["isConnected"] as! Bool) ? UIColor.greenColor() : UIColor.redColor()
150 |
151 | return cell
152 | }
153 |
154 |
155 | func tableView(tableView: UITableView, heightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat {
156 | return 44.0
157 | }
158 |
159 | }
160 |
--------------------------------------------------------------------------------
/SocketChat/ChatCell.xib:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
24 |
25 |
26 |
27 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
--------------------------------------------------------------------------------
/Source/SwiftRegex.swift:
--------------------------------------------------------------------------------
1 | //
2 | // SwiftRegex.swift
3 | // SwiftRegex
4 | //
5 | // Created by John Holdsworth on 26/06/2014.
6 | // Copyright (c) 2014 John Holdsworth.
7 | //
8 | // $Id: //depot/SwiftRegex/SwiftRegex.swift#37 $
9 | //
10 | // This code is in the public domain from:
11 | // https://github.com/johnno1962/SwiftRegex
12 | //
13 |
14 | import Foundation
15 |
16 | infix operator <~ { associativity none precedence 130 }
17 |
18 | private let lock = dispatch_semaphore_create(1)
19 | private var swiftRegexCache = [String: NSRegularExpression]()
20 |
21 | internal final class SwiftRegex: NSObject, BooleanType {
22 | var target:String
23 | var regex: NSRegularExpression
24 |
25 | init(target:String, pattern:String, options:NSRegularExpressionOptions?) {
26 | if dispatch_semaphore_wait(lock, dispatch_time(DISPATCH_TIME_NOW, Int64(10 * NSEC_PER_MSEC))) != 0 {
27 | fatalError("This should never happen")
28 | }
29 |
30 | self.target = target
31 | if let regex = swiftRegexCache[pattern] {
32 | self.regex = regex
33 | } else {
34 | do {
35 | let regex = try NSRegularExpression(pattern: pattern, options:
36 | NSRegularExpressionOptions.DotMatchesLineSeparators)
37 | swiftRegexCache[pattern] = regex
38 | self.regex = regex
39 | } catch let error as NSError {
40 | SwiftRegex.failure("Error in pattern: \(pattern) - \(error)")
41 | self.regex = NSRegularExpression()
42 | }
43 | }
44 | dispatch_semaphore_signal(lock)
45 | super.init()
46 | }
47 |
48 | private static func failure(message: String) {
49 | fatalError("SwiftRegex: \(message)")
50 | }
51 |
52 | private var targetRange: NSRange {
53 | return NSRange(location: 0,length: target.utf16.count)
54 | }
55 |
56 | private func substring(range: NSRange) -> String? {
57 | if range.location != NSNotFound {
58 | return (target as NSString).substringWithRange(range)
59 | } else {
60 | return nil
61 | }
62 | }
63 |
64 | func doesMatch(options: NSMatchingOptions!) -> Bool {
65 | return range(options).location != NSNotFound
66 | }
67 |
68 | func range(options: NSMatchingOptions) -> NSRange {
69 | return regex.rangeOfFirstMatchInString(target as String, options: [], range: targetRange)
70 | }
71 |
72 | func match(options: NSMatchingOptions) -> String? {
73 | return substring(range(options))
74 | }
75 |
76 | func groups() -> [String]? {
77 | return groupsForMatch(regex.firstMatchInString(target as String, options:
78 | NSMatchingOptions.WithoutAnchoringBounds, range: targetRange))
79 | }
80 |
81 | private func groupsForMatch(match: NSTextCheckingResult?) -> [String]? {
82 | guard let match = match else {
83 | return nil
84 | }
85 | var groups = [String]()
86 | for groupno in 0...regex.numberOfCaptureGroups {
87 | if let group = substring(match.rangeAtIndex(groupno)) {
88 | groups += [group]
89 | } else {
90 | groups += ["_"] // avoids bridging problems
91 | }
92 | }
93 | return groups
94 | }
95 |
96 | subscript(groupno: Int) -> String? {
97 | get {
98 | return groups()?[groupno]
99 | }
100 |
101 | set(newValue) {
102 | if newValue == nil {
103 | return
104 | }
105 |
106 | for match in Array(matchResults().reverse()) {
107 | let replacement = regex.replacementStringForResult(match,
108 | inString: target as String, offset: 0, template: newValue!)
109 | let mut = NSMutableString(string: target)
110 | mut.replaceCharactersInRange(match.rangeAtIndex(groupno), withString: replacement)
111 |
112 | target = mut as String
113 | }
114 | }
115 | }
116 |
117 | func matchResults() -> [NSTextCheckingResult] {
118 | let matches = regex.matchesInString(target as String, options:
119 | NSMatchingOptions.WithoutAnchoringBounds, range: targetRange)
120 | as [NSTextCheckingResult]
121 |
122 | return matches
123 | }
124 |
125 | func ranges() -> [NSRange] {
126 | return matchResults().map { $0.range }
127 | }
128 |
129 | func matches() -> [String] {
130 | return matchResults().map( { self.substring($0.range)!})
131 | }
132 |
133 | func allGroups() -> [[String]?] {
134 | return matchResults().map { self.groupsForMatch($0) }
135 | }
136 |
137 | func dictionary(options: NSMatchingOptions!) -> Dictionary {
138 | var out = Dictionary()
139 | for match in matchResults() {
140 | out[substring(match.rangeAtIndex(1))!] = substring(match.rangeAtIndex(2))!
141 | }
142 | return out
143 | }
144 |
145 | func substituteMatches(substitution: ((NSTextCheckingResult, UnsafeMutablePointer) -> String),
146 | options:NSMatchingOptions) -> String {
147 | let out = NSMutableString()
148 | var pos = 0
149 |
150 | regex.enumerateMatchesInString(target as String, options: options, range: targetRange ) {match, flags, stop in
151 | let matchRange = match!.range
152 | out.appendString( self.substring(NSRange(location:pos, length:matchRange.location-pos))!)
153 | out.appendString( substitution(match!, stop) )
154 | pos = matchRange.location + matchRange.length
155 | }
156 |
157 | out.appendString(substring(NSRange(location:pos, length:targetRange.length-pos))!)
158 |
159 | return out as String
160 | }
161 |
162 | var boolValue: Bool {
163 | return doesMatch(nil)
164 | }
165 | }
166 |
167 | extension String {
168 | subscript(pattern: String, options: NSRegularExpressionOptions) -> SwiftRegex {
169 | return SwiftRegex(target: self, pattern: pattern, options: options)
170 | }
171 | }
172 |
173 | extension String {
174 | subscript(pattern: String) -> SwiftRegex {
175 | return SwiftRegex(target: self, pattern: pattern, options: nil)
176 | }
177 | }
178 |
179 | func <~ (left: SwiftRegex, right: String) -> String {
180 | return left.substituteMatches({match, stop in
181 | return left.regex.replacementStringForResult( match,
182 | inString: left.target as String, offset: 0, template: right )
183 | }, options: [])
184 | }
185 |
--------------------------------------------------------------------------------
/Source/SocketParsable.swift:
--------------------------------------------------------------------------------
1 | //
2 | // SocketParsable.swift
3 | // Socket.IO-Client-Swift
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 | import Foundation
24 |
25 | protocol SocketParsable: SocketClientSpec {
26 | func parseBinaryData(data: NSData)
27 | func parseSocketMessage(message: String)
28 | }
29 |
30 | extension SocketParsable {
31 | private func isCorrectNamespace(nsp: String) -> Bool {
32 | return nsp == self.nsp
33 | }
34 |
35 | private func handleConnect(p: SocketPacket) {
36 | if p.nsp == "/" && nsp != "/" {
37 | joinNamespace(nsp)
38 | } else if p.nsp != "/" && nsp == "/" {
39 | didConnect()
40 | } else {
41 | didConnect()
42 | }
43 | }
44 |
45 | private func handlePacket(pack: SocketPacket) {
46 | switch pack.type {
47 | case .Event where isCorrectNamespace(pack.nsp):
48 | handleEvent(pack.event, data: pack.args,
49 | isInternalMessage: false, withAck: pack.id)
50 | case .Ack where isCorrectNamespace(pack.nsp):
51 | handleAck(pack.id, data: pack.data)
52 | case .BinaryEvent where isCorrectNamespace(pack.nsp):
53 | waitingData.append(pack)
54 | case .BinaryAck where isCorrectNamespace(pack.nsp):
55 | waitingData.append(pack)
56 | case .Connect:
57 | handleConnect(pack)
58 | case .Disconnect:
59 | didDisconnect("Got Disconnect")
60 | case .Error:
61 | handleEvent("error", data: pack.data, isInternalMessage: true, withAck: pack.id)
62 | default:
63 | DefaultSocketLogger.Logger.log("Got invalid packet: %@", type: "SocketParser", args: pack.description)
64 | }
65 | }
66 |
67 | /// Parses a messsage from the engine. Returning either a string error or a complete SocketPacket
68 | func parseString(message: String) -> Either {
69 | var parser = SocketStringReader(message: message)
70 |
71 | guard let type = SocketPacket.PacketType(rawValue: Int(parser.read(1)) ?? -1) else {
72 | return .Left("Invalid packet type")
73 | }
74 |
75 | if !parser.hasNext {
76 | return .Right(SocketPacket(type: type, nsp: "/"))
77 | }
78 |
79 | var namespace: String?
80 | var placeholders = -1
81 |
82 | if type == .BinaryEvent || type == .BinaryAck {
83 | if let holders = Int(parser.readUntilStringOccurence("-")) {
84 | placeholders = holders
85 | } else {
86 | return .Left("Invalid packet")
87 | }
88 | }
89 |
90 | if parser.currentCharacter == "/" {
91 | namespace = parser.readUntilStringOccurence(",") ?? parser.readUntilEnd()
92 | }
93 |
94 | if !parser.hasNext {
95 | return .Right(SocketPacket(type: type, id: -1,
96 | nsp: namespace ?? "/", placeholders: placeholders))
97 | }
98 |
99 | var idString = ""
100 |
101 | if type == .Error {
102 | parser.advanceIndexBy(-1)
103 | }
104 |
105 | while parser.hasNext && type != .Error {
106 | if let int = Int(parser.read(1)) {
107 | idString += String(int)
108 | } else {
109 | parser.advanceIndexBy(-2)
110 | break
111 | }
112 | }
113 |
114 | let d = message[parser.currentIndex.advancedBy(1).. Either {
134 | let stringData = data.dataUsingEncoding(NSUTF8StringEncoding, allowLossyConversion: false)
135 | do {
136 | if let arr = try NSJSONSerialization.JSONObjectWithData(stringData!,
137 | options: NSJSONReadingOptions.MutableContainers) as? [AnyObject] {
138 | return .Right(arr)
139 | } else {
140 | return .Left("Expected data array")
141 | }
142 | } catch {
143 | return .Left("Error parsing data for packet")
144 | }
145 | }
146 |
147 | // Parses messages recieved
148 | func parseSocketMessage(message: String) {
149 | guard !message.isEmpty else { return }
150 |
151 | DefaultSocketLogger.Logger.log("Parsing %@", type: "SocketParser", args: message)
152 |
153 | switch parseString(message) {
154 | case let .Left(err):
155 | DefaultSocketLogger.Logger.error("\(err): %@", type: "SocketParser", args: message)
156 | case let .Right(pack):
157 | DefaultSocketLogger.Logger.log("Decoded packet as: %@", type: "SocketParser", args: pack.description)
158 | handlePacket(pack)
159 | }
160 | }
161 |
162 | func parseBinaryData(data: NSData) {
163 | guard !waitingData.isEmpty else {
164 | DefaultSocketLogger.Logger.error("Got data when not remaking packet", type: "SocketParser")
165 | return
166 | }
167 |
168 | // Should execute event?
169 | guard waitingData[waitingData.count - 1].addData(data) else {
170 | return
171 | }
172 |
173 | let packet = waitingData.removeLast()
174 |
175 | if packet.type != .BinaryAck {
176 | handleEvent(packet.event, data: packet.args ?? [],
177 | isInternalMessage: false, withAck: packet.id)
178 | } else {
179 | handleAck(packet.id, data: packet.args)
180 | }
181 | }
182 | }
183 |
--------------------------------------------------------------------------------
/Source/SocketIOClientOption.swift:
--------------------------------------------------------------------------------
1 | //
2 | // SocketIOClientOption .swift
3 | // Socket.IO-Client-Swift
4 | //
5 | // Created by Erik Little on 10/17/15.
6 | //
7 | // Permission is hereby granted, free of charge, to any person obtaining a copy
8 | // of this software and associated documentation files (the "Software"), to deal
9 | // in the Software without restriction, including without limitation the rights
10 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11 | // copies of the Software, and to permit persons to whom the Software is
12 | // furnished to do so, subject to the following conditions:
13 | //
14 | // The above copyright notice and this permission notice shall be included in
15 | // all copies or substantial portions of the Software.
16 | //
17 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
23 | // THE SOFTWARE.
24 |
25 | import Foundation
26 |
27 | protocol ClientOption: CustomStringConvertible, Hashable {
28 | func getSocketIOOptionValue() -> AnyObject
29 | }
30 |
31 | public enum SocketIOClientOption: ClientOption {
32 | case ConnectParams([String: AnyObject])
33 | case Cookies([NSHTTPCookie])
34 | case ExtraHeaders([String: String])
35 | case ForceNew(Bool)
36 | case ForcePolling(Bool)
37 | case ForceWebsockets(Bool)
38 | case HandleQueue(dispatch_queue_t)
39 | case Log(Bool)
40 | case Logger(SocketLogger)
41 | case Nsp(String)
42 | case Path(String)
43 | case Reconnects(Bool)
44 | case ReconnectAttempts(Int)
45 | case ReconnectWait(Int)
46 | case Secure(Bool)
47 | case SelfSigned(Bool)
48 | case SessionDelegate(NSURLSessionDelegate)
49 | case VoipEnabled(Bool)
50 |
51 | public var description: String {
52 | let description: String
53 |
54 | switch self {
55 | case .ConnectParams:
56 | description = "connectParams"
57 | case .Cookies:
58 | description = "cookies"
59 | case .ExtraHeaders:
60 | description = "extraHeaders"
61 | case .ForceNew:
62 | description = "forceNew"
63 | case .ForcePolling:
64 | description = "forcePolling"
65 | case .ForceWebsockets:
66 | description = "forceWebsockets"
67 | case .HandleQueue:
68 | description = "handleQueue"
69 | case .Log:
70 | description = "log"
71 | case .Logger:
72 | description = "logger"
73 | case .Nsp:
74 | description = "nsp"
75 | case .Path:
76 | description = "path"
77 | case .Reconnects:
78 | description = "reconnects"
79 | case .ReconnectAttempts:
80 | description = "reconnectAttempts"
81 | case .ReconnectWait:
82 | description = "reconnectWait"
83 | case .Secure:
84 | description = "secure"
85 | case .SelfSigned:
86 | description = "selfSigned"
87 | case .SessionDelegate:
88 | description = "sessionDelegate"
89 | case .VoipEnabled:
90 | description = "voipEnabled"
91 | }
92 |
93 | return description
94 | }
95 |
96 | public var hashValue: Int {
97 | return description.hashValue
98 | }
99 |
100 | func getSocketIOOptionValue() -> AnyObject {
101 | let value: AnyObject
102 |
103 | switch self {
104 | case let .ConnectParams(params):
105 | value = params
106 | case let .Cookies(cookies):
107 | value = cookies
108 | case let .ExtraHeaders(headers):
109 | value = headers
110 | case let .ForceNew(force):
111 | value = force
112 | case let .ForcePolling(force):
113 | value = force
114 | case let .ForceWebsockets(force):
115 | value = force
116 | case let .HandleQueue(queue):
117 | value = queue
118 | case let .Log(log):
119 | value = log
120 | case let .Logger(logger):
121 | value = logger
122 | case let .Nsp(nsp):
123 | value = nsp
124 | case let .Path(path):
125 | value = path
126 | case let .Reconnects(reconnects):
127 | value = reconnects
128 | case let .ReconnectAttempts(attempts):
129 | value = attempts
130 | case let .ReconnectWait(wait):
131 | value = wait
132 | case let .Secure(secure):
133 | value = secure
134 | case let .SelfSigned(signed):
135 | value = signed
136 | case let .SessionDelegate(delegate):
137 | value = delegate
138 | case let .VoipEnabled(enabled):
139 | value = enabled
140 | }
141 |
142 | return value
143 | }
144 | }
145 |
146 | public func ==(lhs: SocketIOClientOption, rhs: SocketIOClientOption) -> Bool {
147 | return lhs.description == rhs.description
148 | }
149 |
150 | extension Set where Element: ClientOption {
151 | mutating func insertIgnore(element: Element) {
152 | if !contains(element) {
153 | insert(element)
154 | }
155 | }
156 | }
157 |
158 | extension NSDictionary {
159 | static func keyValueToSocketIOClientOption(key: String, value: AnyObject) -> SocketIOClientOption? {
160 | switch (key, value) {
161 | case let ("connectParams", params as [String: AnyObject]):
162 | return .ConnectParams(params)
163 | case let ("reconnects", reconnects as Bool):
164 | return .Reconnects(reconnects)
165 | case let ("reconnectAttempts", attempts as Int):
166 | return .ReconnectAttempts(attempts)
167 | case let ("reconnectWait", wait as Int):
168 | return .ReconnectWait(wait)
169 | case let ("forceNew", force as Bool):
170 | return .ForceNew(force)
171 | case let ("forcePolling", force as Bool):
172 | return .ForcePolling(force)
173 | case let ("forceWebsockets", force as Bool):
174 | return .ForceWebsockets(force)
175 | case let ("nsp", nsp as String):
176 | return .Nsp(nsp)
177 | case let ("cookies", cookies as [NSHTTPCookie]):
178 | return .Cookies(cookies)
179 | case let ("log", log as Bool):
180 | return .Log(log)
181 | case let ("logger", logger as SocketLogger):
182 | return .Logger(logger)
183 | case let ("sessionDelegate", delegate as NSURLSessionDelegate):
184 | return .SessionDelegate(delegate)
185 | case let ("path", path as String):
186 | return .Path(path)
187 | case let ("extraHeaders", headers as [String: String]):
188 | return .ExtraHeaders(headers)
189 | case let ("handleQueue", queue as dispatch_queue_t):
190 | return .HandleQueue(queue)
191 | case let ("voipEnabled", enable as Bool):
192 | return .VoipEnabled(enable)
193 | case let ("secure", secure as Bool):
194 | return .Secure(secure)
195 | case let ("selfSigned", selfSigned as Bool):
196 | return .SelfSigned(selfSigned)
197 | default:
198 | return nil
199 | }
200 | }
201 |
202 | func toSocketOptionsSet() -> Set {
203 | var options = Set()
204 |
205 | for (rawKey, value) in self {
206 | if let key = rawKey as? String, opt = NSDictionary.keyValueToSocketIOClientOption(key, value: value) {
207 | options.insertIgnore(opt)
208 | }
209 | }
210 |
211 | return options
212 | }
213 | }
214 |
--------------------------------------------------------------------------------
/Source/SocketEnginePollable.swift:
--------------------------------------------------------------------------------
1 | //
2 | // SocketEnginePollable.swift
3 | // Socket.IO-Client-Swift
4 | //
5 | // Created by Erik Little on 1/15/16.
6 | //
7 | // Permission is hereby granted, free of charge, to any person obtaining a copy
8 | // of this software and associated documentation files (the "Software"), to deal
9 | // in the Software without restriction, including without limitation the rights
10 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11 | // copies of the Software, and to permit persons to whom the Software is
12 | // furnished to do so, subject to the following conditions:
13 | //
14 | // The above copyright notice and this permission notice shall be included in
15 | // all copies or substantial portions of the Software.
16 | //
17 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
23 | // THE SOFTWARE.
24 |
25 | import Foundation
26 |
27 | /// Protocol that is used to implement socket.io polling support
28 | public protocol SocketEnginePollable: SocketEngineSpec {
29 | var invalidated: Bool { get }
30 | /// Holds strings waiting to be sent over polling.
31 | /// You shouldn't need to mess with this.
32 | var postWait: [String] { get set }
33 | var session: NSURLSession? { get }
34 | /// Because socket.io doesn't let you send two polling request at the same time
35 | /// we have to keep track if there's an outstanding poll
36 | var waitingForPoll: Bool { get set }
37 | /// Because socket.io doesn't let you send two post request at the same time
38 | /// we have to keep track if there's an outstanding post
39 | var waitingForPost: Bool { get set }
40 |
41 | func doPoll()
42 | func sendPollMessage(message: String, withType type: SocketEnginePacketType, withData datas: [NSData])
43 | func stopPolling()
44 | }
45 |
46 | // Default polling methods
47 | extension SocketEnginePollable {
48 | private func addHeaders(req: NSMutableURLRequest) {
49 | if cookies != nil {
50 | let headers = NSHTTPCookie.requestHeaderFieldsWithCookies(cookies!)
51 | req.allHTTPHeaderFields = headers
52 | }
53 |
54 | if extraHeaders != nil {
55 | for (headerName, value) in extraHeaders! {
56 | req.setValue(value, forHTTPHeaderField: headerName)
57 | }
58 | }
59 | }
60 |
61 | func createRequestForPostWithPostWait() -> NSURLRequest {
62 | var postStr = ""
63 |
64 | for packet in postWait {
65 | let len = packet.characters.count
66 |
67 | postStr += "\(len):\(packet)"
68 | }
69 |
70 | DefaultSocketLogger.Logger.log("Created POST string: %@", type: "SocketEnginePolling", args: postStr)
71 |
72 | postWait.removeAll(keepCapacity: false)
73 |
74 | let req = NSMutableURLRequest(URL: urlPollingWithSid)
75 |
76 | addHeaders(req)
77 |
78 | req.HTTPMethod = "POST"
79 | req.setValue("text/plain; charset=UTF-8", forHTTPHeaderField: "Content-Type")
80 |
81 | let postData = postStr.dataUsingEncoding(NSUTF8StringEncoding,
82 | allowLossyConversion: false)!
83 |
84 | req.HTTPBody = postData
85 | req.setValue(String(postData.length), forHTTPHeaderField: "Content-Length")
86 |
87 | return req
88 | }
89 |
90 | public func doPoll() {
91 | if websocket || waitingForPoll || !connected || closed {
92 | return
93 | }
94 |
95 | waitingForPoll = true
96 | let req = NSMutableURLRequest(URL: urlPollingWithSid)
97 |
98 | addHeaders(req)
99 | doLongPoll(req)
100 | }
101 |
102 | func doRequest(req: NSURLRequest, withCallback callback: (NSData?, NSURLResponse?, NSError?) -> Void) {
103 | if !polling || closed || invalidated {
104 | DefaultSocketLogger.Logger.error("Tried to do polling request when not supposed to", type: "SocketEnginePolling")
105 | return
106 | }
107 |
108 | DefaultSocketLogger.Logger.log("Doing polling request", type: "SocketEnginePolling")
109 |
110 | session?.dataTaskWithRequest(req, completionHandler: callback).resume()
111 | }
112 |
113 | func doLongPoll(req: NSURLRequest) {
114 | doRequest(req) {[weak self] data, res, err in
115 | guard let this = self else { return }
116 |
117 | if err != nil || data == nil {
118 | DefaultSocketLogger.Logger.error(err?.localizedDescription ?? "Error", type: "SocketEnginePolling")
119 |
120 | if this.polling {
121 | this.didError(err?.localizedDescription ?? "Error")
122 | }
123 |
124 | return
125 | }
126 |
127 | DefaultSocketLogger.Logger.log("Got polling response", type: "SocketEnginePolling")
128 |
129 | if let str = String(data: data!, encoding: NSUTF8StringEncoding) {
130 | dispatch_async(this.parseQueue) {
131 | this.parsePollingMessage(str)
132 | }
133 | }
134 |
135 | this.waitingForPoll = false
136 |
137 | if this.fastUpgrade {
138 | this.doFastUpgrade()
139 | } else if !this.closed && this.polling {
140 | this.doPoll()
141 | }
142 | }
143 | }
144 |
145 | private func flushWaitingForPost() {
146 | if postWait.count == 0 || !connected {
147 | return
148 | } else if websocket {
149 | flushWaitingForPostToWebSocket()
150 | return
151 | }
152 |
153 | let req = createRequestForPostWithPostWait()
154 |
155 | waitingForPost = true
156 |
157 | DefaultSocketLogger.Logger.log("POSTing", type: "SocketEnginePolling")
158 |
159 | doRequest(req) {[weak self] data, res, err in
160 | guard let this = self else { return }
161 |
162 | if err != nil {
163 | DefaultSocketLogger.Logger.error(err?.localizedDescription ?? "Error", type: "SocketEnginePolling")
164 |
165 | if this.polling {
166 | this.didError(err?.localizedDescription ?? "Error")
167 | }
168 |
169 | return
170 | }
171 |
172 | this.waitingForPost = false
173 |
174 | dispatch_async(this.emitQueue) {
175 | if !this.fastUpgrade {
176 | this.flushWaitingForPost()
177 | this.doPoll()
178 | }
179 | }
180 | }
181 | }
182 |
183 | func parsePollingMessage(str: String) {
184 | guard str.characters.count != 1 else {
185 | return
186 | }
187 |
188 | var reader = SocketStringReader(message: str)
189 |
190 | while reader.hasNext {
191 | if let n = Int(reader.readUntilStringOccurence(":")) {
192 | let str = reader.read(n)
193 |
194 | dispatch_async(handleQueue) {
195 | self.parseEngineMessage(str, fromPolling: true)
196 | }
197 | } else {
198 | dispatch_async(handleQueue) {
199 | self.parseEngineMessage(str, fromPolling: true)
200 | }
201 | break
202 | }
203 | }
204 | }
205 |
206 | /// Send polling message.
207 | /// Only call on emitQueue
208 | public func sendPollMessage(message: String, withType type: SocketEnginePacketType, withData datas: [NSData]) {
209 | DefaultSocketLogger.Logger.log("Sending poll: %@ as type: %@", type: "SocketEnginePolling", args: message, type.rawValue)
210 | let fixedMessage = doubleEncodeUTF8(message)
211 | let strMsg = "\(type.rawValue)\(fixedMessage)"
212 |
213 | postWait.append(strMsg)
214 |
215 | for data in datas {
216 | if case let .Right(bin) = createBinaryDataForSend(data) {
217 | postWait.append(bin)
218 | }
219 | }
220 |
221 | if !waitingForPost {
222 | flushWaitingForPost()
223 | }
224 | }
225 |
226 | public func stopPolling() {
227 | waitingForPoll = false
228 | waitingForPost = false
229 | session?.finishTasksAndInvalidate()
230 | }
231 | }
232 |
--------------------------------------------------------------------------------
/Source/SocketPacket.swift:
--------------------------------------------------------------------------------
1 | //
2 | // SocketPacket.swift
3 | // Socket.IO-Client-Swift
4 | //
5 | // Created by Erik Little on 1/18/15.
6 | //
7 | // Permission is hereby granted, free of charge, to any person obtaining a copy
8 | // of this software and associated documentation files (the "Software"), to deal
9 | // in the Software without restriction, including without limitation the rights
10 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11 | // copies of the Software, and to permit persons to whom the Software is
12 | // furnished to do so, subject to the following conditions:
13 | //
14 | // The above copyright notice and this permission notice shall be included in
15 | // all copies or substantial portions of the Software.
16 | //
17 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
23 | // THE SOFTWARE.
24 | //
25 |
26 | import Foundation
27 |
28 | struct SocketPacket {
29 | private let placeholders: Int
30 |
31 | private static let logType = "SocketPacket"
32 |
33 | let nsp: String
34 | let id: Int
35 | let type: PacketType
36 |
37 | enum PacketType: Int {
38 | case Connect, Disconnect, Event, Ack, Error, BinaryEvent, BinaryAck
39 | }
40 |
41 | var args: [AnyObject] {
42 | if type == .Event || type == .BinaryEvent && data.count != 0 {
43 | return Array(data.dropFirst())
44 | } else {
45 | return data
46 | }
47 | }
48 |
49 | var binary: [NSData]
50 | var data: [AnyObject]
51 | var description: String {
52 | return "SocketPacket {type: \(String(type.rawValue)); data: " +
53 | "\(String(data)); id: \(id); placeholders: \(placeholders); nsp: \(nsp)}"
54 | }
55 |
56 | var event: String {
57 | return String(data[0])
58 | }
59 |
60 | var packetString: String {
61 | return createPacketString()
62 | }
63 |
64 | init(type: SocketPacket.PacketType, data: [AnyObject] = [AnyObject](), id: Int = -1,
65 | nsp: String, placeholders: Int = 0, binary: [NSData] = [NSData]()) {
66 | self.data = data
67 | self.id = id
68 | self.nsp = nsp
69 | self.type = type
70 | self.placeholders = placeholders
71 | self.binary = binary
72 | }
73 |
74 | mutating func addData(data: NSData) -> Bool {
75 | if placeholders == binary.count {
76 | return true
77 | }
78 |
79 | binary.append(data)
80 |
81 | if placeholders == binary.count {
82 | fillInPlaceholders()
83 | return true
84 | } else {
85 | return false
86 | }
87 | }
88 |
89 | private func completeMessage(message: String, ack: Bool) -> String {
90 | var restOfMessage = ""
91 |
92 | if data.count == 0 {
93 | return message + "]"
94 | }
95 |
96 | for arg in data {
97 | if arg is NSDictionary || arg is [AnyObject] {
98 | do {
99 | let jsonSend = try NSJSONSerialization.dataWithJSONObject(arg,
100 | options: NSJSONWritingOptions(rawValue: 0))
101 | let jsonString = String(data: jsonSend, encoding: NSUTF8StringEncoding)
102 |
103 | restOfMessage += jsonString! + ","
104 | } catch {
105 | DefaultSocketLogger.Logger.error("Error creating JSON object in SocketPacket.completeMessage",
106 | type: SocketPacket.logType)
107 | }
108 | } else if let str = arg as? String {
109 | restOfMessage += "\"" + ((str["\n"] <~ "\\\\n")["\r"] <~ "\\\\r") + "\","
110 | } else if arg is NSNull {
111 | restOfMessage += "null,"
112 | } else {
113 | restOfMessage += "\(arg),"
114 | }
115 | }
116 |
117 | if restOfMessage != "" {
118 | restOfMessage.removeAtIndex(restOfMessage.endIndex.predecessor())
119 | }
120 |
121 | return message + restOfMessage + "]"
122 | }
123 |
124 | private func createAck() -> String {
125 | let message: String
126 |
127 | if type == .Ack {
128 | if nsp == "/" {
129 | message = "3\(id)["
130 | } else {
131 | message = "3\(nsp),\(id)["
132 | }
133 | } else {
134 | if nsp == "/" {
135 | message = "6\(binary.count)-\(id)["
136 | } else {
137 | message = "6\(binary.count)-\(nsp),\(id)["
138 | }
139 | }
140 |
141 | return completeMessage(message, ack: true)
142 | }
143 |
144 |
145 | private func createMessageForEvent() -> String {
146 | let message: String
147 |
148 | if type == .Event {
149 | if nsp == "/" {
150 | if id == -1 {
151 | message = "2["
152 | } else {
153 | message = "2\(id)["
154 | }
155 | } else {
156 | if id == -1 {
157 | message = "2\(nsp),["
158 | } else {
159 | message = "2\(nsp),\(id)["
160 | }
161 | }
162 | } else {
163 | if nsp == "/" {
164 | if id == -1 {
165 | message = "5\(binary.count)-["
166 | } else {
167 | message = "5\(binary.count)-\(id)["
168 | }
169 | } else {
170 | if id == -1 {
171 | message = "5\(binary.count)-\(nsp),["
172 | } else {
173 | message = "5\(binary.count)-\(nsp),\(id)["
174 | }
175 | }
176 | }
177 |
178 | return completeMessage(message, ack: false)
179 | }
180 |
181 | private func createPacketString() -> String {
182 | let str: String
183 |
184 | if type == .Event || type == .BinaryEvent {
185 | str = createMessageForEvent()
186 | } else {
187 | str = createAck()
188 | }
189 |
190 | return str
191 | }
192 |
193 | // Called when we have all the binary data for a packet
194 | // calls _fillInPlaceholders, which replaces placeholders with the
195 | // corresponding binary
196 | private mutating func fillInPlaceholders() {
197 | data = data.map(_fillInPlaceholders)
198 | }
199 |
200 | // Helper method that looks for placeholder strings
201 | // If object is a collection it will recurse
202 | // Returns the object if it is not a placeholder string or the corresponding
203 | // binary data
204 | private func _fillInPlaceholders(object: AnyObject) -> AnyObject {
205 | switch object {
206 | case let string as String where string["~~(\\d)"].groups() != nil:
207 | return binary[Int(string["~~(\\d)"].groups()![1])!]
208 | case let dict as NSDictionary:
209 | return dict.reduce(NSMutableDictionary(), combine: {cur, keyValue in
210 | cur[keyValue.0 as! NSCopying] = _fillInPlaceholders(keyValue.1)
211 | return cur
212 | })
213 | case let arr as [AnyObject]:
214 | return arr.map(_fillInPlaceholders)
215 | default:
216 | return object
217 | }
218 | }
219 | }
220 |
221 | extension SocketPacket {
222 | private static func findType(binCount: Int, ack: Bool) -> PacketType {
223 | switch binCount {
224 | case 0 where !ack:
225 | return .Event
226 | case 0 where ack:
227 | return .Ack
228 | case _ where !ack:
229 | return .BinaryEvent
230 | case _ where ack:
231 | return .BinaryAck
232 | default:
233 | return .Error
234 | }
235 | }
236 |
237 | static func packetFromEmit(items: [AnyObject], id: Int, nsp: String, ack: Bool) -> SocketPacket {
238 | let (parsedData, binary) = deconstructData(items)
239 | let packet = SocketPacket(type: findType(binary.count, ack: ack), data: parsedData,
240 | id: id, nsp: nsp, placeholders: -1, binary: binary)
241 |
242 | return packet
243 | }
244 | }
245 |
246 | private extension SocketPacket {
247 | // Recursive function that looks for NSData in collections
248 | static func shred(data: AnyObject, inout binary: [NSData]) -> AnyObject {
249 | let placeholder = ["_placeholder": true, "num": binary.count]
250 |
251 | switch data {
252 | case let bin as NSData:
253 | binary.append(bin)
254 | return placeholder
255 | case let arr as [AnyObject]:
256 | return arr.map({shred($0, binary: &binary)})
257 | case let dict as NSDictionary:
258 | return dict.reduce(NSMutableDictionary(), combine: {cur, keyValue in
259 | cur[keyValue.0 as! NSCopying] = shred(keyValue.1, binary: &binary)
260 | return cur
261 | })
262 | default:
263 | return data
264 | }
265 | }
266 |
267 | // Removes binary data from emit data
268 | // Returns a type containing the de-binaryed data and the binary
269 | static func deconstructData(data: [AnyObject]) -> ([AnyObject], [NSData]) {
270 | var binary = [NSData]()
271 |
272 | return (data.map({shred($0, binary: &binary)}), binary)
273 | }
274 | }
275 |
--------------------------------------------------------------------------------
/SocketChat/ChatViewController.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ChatViewController.swift
3 | // SocketChat
4 | //
5 | // Created by Gabriel Theodoropoulos on 1/31/16.
6 | // Copyright © 2016 AppCoda. All rights reserved.
7 | //
8 |
9 | import UIKit
10 |
11 | class ChatViewController: UIViewController, UITableViewDelegate, UITableViewDataSource, UITextViewDelegate, UIGestureRecognizerDelegate {
12 |
13 | @IBOutlet weak var tblChat: UITableView!
14 |
15 | @IBOutlet weak var lblOtherUserActivityStatus: UILabel!
16 |
17 | @IBOutlet weak var tvMessageEditor: UITextView!
18 |
19 | @IBOutlet weak var conBottomEditor: NSLayoutConstraint!
20 |
21 | @IBOutlet weak var lblNewsBanner: UILabel!
22 |
23 |
24 |
25 | var nickname: String!
26 |
27 | var chatMessages = [[String: AnyObject]]()
28 |
29 | var bannerLabelTimer: NSTimer!
30 |
31 |
32 | override func viewDidLoad() {
33 | super.viewDidLoad()
34 |
35 | // Do any additional setup after loading the view.
36 |
37 | NSNotificationCenter.defaultCenter().addObserver(self, selector: "handleKeyboardDidShowNotification:", name: UIKeyboardDidShowNotification, object: nil)
38 | NSNotificationCenter.defaultCenter().addObserver(self, selector: "handleKeyboardDidHideNotification:", name: UIKeyboardDidHideNotification, object: nil)
39 |
40 | NSNotificationCenter.defaultCenter().addObserver(self, selector: "handleConnectedUserUpdateNotification:", name: "userWasConnectedNotification", object: nil)
41 | NSNotificationCenter.defaultCenter().addObserver(self, selector: "handleDisconnectedUserUpdateNotification:", name: "userWasDisconnectedNotification", object: nil)
42 | NSNotificationCenter.defaultCenter().addObserver(self, selector: "handleUserTypingNotification:", name: "userTypingNotification", object: nil)
43 |
44 |
45 | let swipeGestureRecognizer = UISwipeGestureRecognizer(target: self, action: "dismissKeyboard")
46 | swipeGestureRecognizer.direction = UISwipeGestureRecognizerDirection.Down
47 | swipeGestureRecognizer.delegate = self
48 | view.addGestureRecognizer(swipeGestureRecognizer)
49 | }
50 |
51 |
52 | override func viewWillAppear(animated: Bool) {
53 | super.viewWillAppear(animated)
54 |
55 | configureTableView()
56 | configureNewsBannerLabel()
57 | configureOtherUserActivityLabel()
58 |
59 | tvMessageEditor.delegate = self
60 | }
61 |
62 |
63 | override func viewDidAppear(animated: Bool) {
64 | super.viewDidAppear(animated)
65 |
66 | SocketIOManager.sharedInstance.getChatMessage { (messageInfo) -> Void in
67 | dispatch_async(dispatch_get_main_queue(), { () -> Void in
68 | self.chatMessages.append(messageInfo)
69 | self.tblChat.reloadData()
70 | self.scrollToBottom()
71 | })
72 | }
73 | }
74 |
75 |
76 | override func didReceiveMemoryWarning() {
77 | super.didReceiveMemoryWarning()
78 | // Dispose of any resources that can be recreated.
79 | }
80 |
81 |
82 | deinit {
83 | NSNotificationCenter.defaultCenter().removeObserver(self)
84 | }
85 |
86 |
87 | /*
88 | // MARK: - Navigation
89 |
90 | // In a storyboard-based application, you will often want to do a little preparation before navigation
91 | override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
92 | // Get the new view controller using segue.destinationViewController.
93 | // Pass the selected object to the new view controller.
94 | }
95 | */
96 |
97 |
98 | // MARK: IBAction Methods
99 |
100 | @IBAction func sendMessage(sender: AnyObject) {
101 | if tvMessageEditor.text.characters.count > 0 {
102 | SocketIOManager.sharedInstance.sendMessage(tvMessageEditor.text!, withNickname: nickname)
103 | tvMessageEditor.text = ""
104 | tvMessageEditor.resignFirstResponder()
105 | }
106 | }
107 |
108 |
109 | // MARK: Custom Methods
110 |
111 | func configureTableView() {
112 | tblChat.delegate = self
113 | tblChat.dataSource = self
114 | tblChat.registerNib(UINib(nibName: "ChatCell", bundle: nil), forCellReuseIdentifier: "idCellChat")
115 | tblChat.estimatedRowHeight = 90.0
116 | tblChat.rowHeight = UITableViewAutomaticDimension
117 | tblChat.tableFooterView = UIView(frame: CGRectZero)
118 | }
119 |
120 |
121 | func configureNewsBannerLabel() {
122 | lblNewsBanner.layer.cornerRadius = 15.0
123 | lblNewsBanner.clipsToBounds = true
124 | lblNewsBanner.alpha = 0.0
125 | }
126 |
127 |
128 | func configureOtherUserActivityLabel() {
129 | lblOtherUserActivityStatus.hidden = true
130 | lblOtherUserActivityStatus.text = ""
131 | }
132 |
133 |
134 | func handleKeyboardDidShowNotification(notification: NSNotification) {
135 | if let userInfo = notification.userInfo {
136 | if let keyboardFrame = (userInfo[UIKeyboardFrameBeginUserInfoKey] as? NSValue)?.CGRectValue() {
137 | conBottomEditor.constant = keyboardFrame.size.height
138 | view.layoutIfNeeded()
139 | }
140 | }
141 | }
142 |
143 |
144 | func handleKeyboardDidHideNotification(notification: NSNotification) {
145 | conBottomEditor.constant = 0
146 | view.layoutIfNeeded()
147 | }
148 |
149 |
150 | func scrollToBottom() {
151 | let delay = 0.1 * Double(NSEC_PER_SEC)
152 |
153 | dispatch_after(dispatch_time(DISPATCH_TIME_NOW, Int64(delay)), dispatch_get_main_queue()) { () -> Void in
154 | if self.chatMessages.count > 0 {
155 | let lastRowIndexPath = NSIndexPath(forRow: self.chatMessages.count - 1, inSection: 0)
156 | self.tblChat.scrollToRowAtIndexPath(lastRowIndexPath, atScrollPosition: UITableViewScrollPosition.Bottom, animated: true)
157 | }
158 | }
159 | }
160 |
161 |
162 | func showBannerLabelAnimated() {
163 | UIView.animateWithDuration(0.75, animations: { () -> Void in
164 | self.lblNewsBanner.alpha = 1.0
165 |
166 | }) { (finished) -> Void in
167 | self.bannerLabelTimer = NSTimer.scheduledTimerWithTimeInterval(2.0, target: self, selector: "hideBannerLabel", userInfo: nil, repeats: false)
168 | }
169 | }
170 |
171 |
172 | func hideBannerLabel() {
173 | if bannerLabelTimer != nil {
174 | bannerLabelTimer.invalidate()
175 | bannerLabelTimer = nil
176 | }
177 |
178 | UIView.animateWithDuration(0.75, animations: { () -> Void in
179 | self.lblNewsBanner.alpha = 0.0
180 |
181 | }) { (finished) -> Void in
182 | }
183 | }
184 |
185 |
186 |
187 | func dismissKeyboard() {
188 | if tvMessageEditor.isFirstResponder() {
189 | tvMessageEditor.resignFirstResponder()
190 |
191 | SocketIOManager.sharedInstance.sendStopTypingMessage(nickname)
192 | }
193 | }
194 |
195 |
196 | func handleConnectedUserUpdateNotification(notification: NSNotification) {
197 | let connectedUserInfo = notification.object as! [String: AnyObject]
198 | let connectedUserNickname = connectedUserInfo["nickname"] as? String
199 | lblNewsBanner.text = "User \(connectedUserNickname!.uppercaseString) was just connected."
200 | showBannerLabelAnimated()
201 | }
202 |
203 |
204 | func handleDisconnectedUserUpdateNotification(notification: NSNotification) {
205 | let disconnectedUserNickname = notification.object as! String
206 | lblNewsBanner.text = "User \(disconnectedUserNickname.uppercaseString) has left."
207 | showBannerLabelAnimated()
208 | }
209 |
210 |
211 | func handleUserTypingNotification(notification: NSNotification) {
212 | if let typingUsersDictionary = notification.object as? [String: AnyObject] {
213 | var names = ""
214 | var totalTypingUsers = 0
215 | for (typingUser, _) in typingUsersDictionary {
216 | if typingUser != nickname {
217 | names = (names == "") ? typingUser : "\(names), \(typingUser)"
218 | totalTypingUsers += 1
219 | }
220 | }
221 |
222 | if totalTypingUsers > 0 {
223 | let verb = (totalTypingUsers == 1) ? "is" : "are"
224 |
225 | lblOtherUserActivityStatus.text = "\(names) \(verb) now typing a message..."
226 | lblOtherUserActivityStatus.hidden = false
227 | }
228 | else {
229 | lblOtherUserActivityStatus.hidden = true
230 | }
231 | }
232 |
233 | }
234 |
235 |
236 | // MARK: UITableView Delegate and Datasource Methods
237 |
238 | func numberOfSectionsInTableView(tableView: UITableView) -> Int {
239 | return 1
240 | }
241 |
242 |
243 | func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
244 | return chatMessages.count
245 | }
246 |
247 |
248 | func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
249 | let cell = tableView.dequeueReusableCellWithIdentifier("idCellChat", forIndexPath: indexPath) as! ChatCell
250 |
251 | let currentChatMessage = chatMessages[indexPath.row]
252 | let senderNickname = currentChatMessage["nickname"] as! String
253 | let message = currentChatMessage["message"] as! String
254 | let messageDate = currentChatMessage["date"] as! String
255 |
256 | if senderNickname == nickname {
257 | cell.lblChatMessage.textAlignment = NSTextAlignment.Right
258 | cell.lblMessageDetails.textAlignment = NSTextAlignment.Right
259 |
260 | cell.lblChatMessage.textColor = lblNewsBanner.backgroundColor
261 | }
262 |
263 | cell.lblChatMessage.text = message
264 | cell.lblMessageDetails.text = "by \(senderNickname.uppercaseString) @ \(messageDate)"
265 |
266 | cell.lblChatMessage.textColor = UIColor.darkGrayColor()
267 |
268 | return cell
269 | }
270 |
271 |
272 | // MARK: UITextViewDelegate Methods
273 |
274 | func textViewShouldBeginEditing(textView: UITextView) -> Bool {
275 | SocketIOManager.sharedInstance.sendStartTypingMessage(nickname)
276 |
277 | return true
278 | }
279 |
280 |
281 | // MARK: UIGestureRecognizerDelegate Methods
282 |
283 | func gestureRecognizer(gestureRecognizer: UIGestureRecognizer, shouldRecognizeSimultaneouslyWithGestureRecognizer otherGestureRecognizer: UIGestureRecognizer) -> Bool {
284 | return true
285 | }
286 |
287 | }
288 |
--------------------------------------------------------------------------------
/SocketChat/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 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
92 |
93 |
94 |
95 |
96 |
97 |
98 |
99 |
100 |
101 |
113 |
114 |
115 |
116 |
117 |
118 |
119 |
120 |
121 |
122 |
123 |
124 |
125 |
126 |
135 |
136 |
137 |
138 |
139 |
140 |
141 |
142 |
143 |
144 |
145 |
146 |
147 |
148 |
149 |
150 |
151 |
152 |
153 |
154 |
155 |
156 |
157 |
158 |
159 |
160 |
161 |
162 |
163 |
164 |
165 |
166 |
167 |
168 |
169 |
170 |
171 |
172 |
173 |
174 |
175 |
176 |
177 |
178 |
179 |
180 |
181 |
182 |
183 |
184 |
185 |
186 |
--------------------------------------------------------------------------------
/Source/SocketIOClient.swift:
--------------------------------------------------------------------------------
1 | //
2 | // SocketIOClient.swift
3 | // Socket.IO-Client-Swift
4 | //
5 | // Created by Erik Little on 11/23/14.
6 | //
7 | // Permission is hereby granted, free of charge, to any person obtaining a copy
8 | // of this software and associated documentation files (the "Software"), to deal
9 | // in the Software without restriction, including without limitation the rights
10 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11 | // copies of the Software, and to permit persons to whom the Software is
12 | // furnished to do so, subject to the following conditions:
13 | //
14 | // The above copyright notice and this permission notice shall be included in
15 | // all copies or substantial portions of the Software.
16 | //
17 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
23 | // THE SOFTWARE.
24 |
25 | import Foundation
26 |
27 | public final class SocketIOClient: NSObject, SocketEngineClient, SocketParsable {
28 | public let socketURL: NSURL
29 |
30 | public private(set) var engine: SocketEngineSpec?
31 | public private(set) var status = SocketIOClientStatus.NotConnected
32 |
33 | public var forceNew = false
34 | public var nsp = "/"
35 | public var options: Set
36 | public var reconnects = true
37 | public var reconnectWait = 10
38 | public var sid: String? {
39 | return engine?.sid
40 | }
41 |
42 | private let emitQueue = dispatch_queue_create("com.socketio.emitQueue", DISPATCH_QUEUE_SERIAL)
43 | private let logType = "SocketIOClient"
44 | private let parseQueue = dispatch_queue_create("com.socketio.parseQueue", DISPATCH_QUEUE_SERIAL)
45 |
46 | private var anyHandler: ((SocketAnyEvent) -> Void)?
47 | private var currentReconnectAttempt = 0
48 | private var handlers = [SocketEventHandler]()
49 | private var reconnectTimer: NSTimer?
50 | private var ackHandlers = SocketAckManager()
51 |
52 | private(set) var currentAck = -1
53 | private(set) var handleQueue = dispatch_get_main_queue()
54 | private(set) var reconnectAttempts = -1
55 |
56 | var waitingData = [SocketPacket]()
57 |
58 | /**
59 | Type safe way to create a new SocketIOClient. opts can be omitted
60 | */
61 | public init(socketURL: NSURL, options: Set = []) {
62 | self.options = options
63 | self.socketURL = socketURL
64 |
65 | if socketURL.absoluteString.hasPrefix("https://") {
66 | self.options.insertIgnore(.Secure(true))
67 | }
68 |
69 | for option in options {
70 | switch option {
71 | case let .Reconnects(reconnects):
72 | self.reconnects = reconnects
73 | case let .ReconnectAttempts(attempts):
74 | reconnectAttempts = attempts
75 | case let .ReconnectWait(wait):
76 | reconnectWait = abs(wait)
77 | case let .Nsp(nsp):
78 | self.nsp = nsp
79 | case let .Log(log):
80 | DefaultSocketLogger.Logger.log = log
81 | case let .Logger(logger):
82 | DefaultSocketLogger.Logger = logger
83 | case let .HandleQueue(queue):
84 | handleQueue = queue
85 | case let .ForceNew(force):
86 | forceNew = force
87 | default:
88 | continue
89 | }
90 | }
91 |
92 | self.options.insertIgnore(.Path("/socket.io/"))
93 |
94 | super.init()
95 | }
96 |
97 | /**
98 | Not so type safe way to create a SocketIOClient, meant for Objective-C compatiblity.
99 | If using Swift it's recommended to use `init(socketURL: NSURL, options: Set)`
100 | */
101 | public convenience init(socketURL: NSURL, options: NSDictionary?) {
102 | self.init(socketURL: socketURL, options: options?.toSocketOptionsSet() ?? [])
103 | }
104 |
105 | /// Please use the NSURL based init
106 | @available(*, deprecated=5.3)
107 | public convenience init(socketURLString: String, options: Set = []) {
108 | guard let url = NSURL(string: socketURLString) else { fatalError("Incorrect url") }
109 | self.init(socketURL: url, options: options)
110 | }
111 |
112 | /// Please use the NSURL based init
113 | @available(*, deprecated=5.3)
114 | public convenience init(socketURLString: String, options: NSDictionary?) {
115 | guard let url = NSURL(string: socketURLString) else { fatalError("Incorrect url") }
116 | self.init(socketURL: url, options: options?.toSocketOptionsSet() ?? [])
117 | }
118 |
119 | deinit {
120 | DefaultSocketLogger.Logger.log("Client is being released", type: logType)
121 | engine?.close("Client Deinit")
122 | }
123 |
124 | private func addEngine() -> SocketEngineSpec {
125 | DefaultSocketLogger.Logger.log("Adding engine", type: logType)
126 |
127 | engine = SocketEngine(client: self, url: socketURL, options: options)
128 |
129 | return engine!
130 | }
131 |
132 | private func clearReconnectTimer() {
133 | reconnectTimer?.invalidate()
134 | reconnectTimer = nil
135 | }
136 |
137 | @available(*, deprecated=6.0)
138 | public func close() {
139 | disconnect()
140 | }
141 |
142 | /**
143 | Connect to the server.
144 | */
145 | public func connect() {
146 | connect(timeoutAfter: 0, withTimeoutHandler: nil)
147 | }
148 |
149 | /**
150 | Connect to the server. If we aren't connected after timeoutAfter, call handler
151 | */
152 | public func connect(timeoutAfter timeoutAfter: Int, withTimeoutHandler handler: (() -> Void)?) {
153 | assert(timeoutAfter >= 0, "Invalid timeout: \(timeoutAfter)")
154 |
155 | guard status != .Connected else {
156 | DefaultSocketLogger.Logger.log("Tried connecting on an already connected socket",
157 | type: logType)
158 | return
159 | }
160 |
161 | status = .Connecting
162 |
163 | if engine == nil || forceNew {
164 | addEngine().open()
165 | } else {
166 | engine?.open()
167 | }
168 |
169 | guard timeoutAfter != 0 else { return }
170 |
171 | let time = dispatch_time(DISPATCH_TIME_NOW, Int64(timeoutAfter) * Int64(NSEC_PER_SEC))
172 |
173 | dispatch_after(time, handleQueue) {[weak self] in
174 | if let this = self where this.status != .Connected {
175 | this.status = .Closed
176 | this.engine?.close("Connect timeout")
177 |
178 | handler?()
179 | }
180 | }
181 | }
182 |
183 | private func createOnAck(items: [AnyObject]) -> OnAckCallback {
184 | currentAck += 1
185 |
186 | return {[weak self, ack = currentAck] timeout, callback in
187 | if let this = self {
188 | this.ackHandlers.addAck(ack, callback: callback)
189 |
190 | dispatch_async(this.emitQueue) {
191 | this._emit(items, ack: ack)
192 | }
193 |
194 | if timeout != 0 {
195 | let time = dispatch_time(DISPATCH_TIME_NOW, Int64(timeout * NSEC_PER_SEC))
196 |
197 | dispatch_after(time, this.handleQueue) {
198 | this.ackHandlers.timeoutAck(ack)
199 | }
200 | }
201 | }
202 | }
203 | }
204 |
205 | func didConnect() {
206 | DefaultSocketLogger.Logger.log("Socket connected", type: logType)
207 | status = .Connected
208 | currentReconnectAttempt = 0
209 | clearReconnectTimer()
210 |
211 | // Don't handle as internal because something crazy could happen where
212 | // we disconnect before it's handled
213 | handleEvent("connect", data: [], isInternalMessage: false)
214 | }
215 |
216 | func didDisconnect(reason: String) {
217 | guard status != .Closed else {
218 | return
219 | }
220 |
221 | DefaultSocketLogger.Logger.log("Disconnected: %@", type: logType, args: reason)
222 |
223 | status = .Closed
224 | reconnects = false
225 |
226 | // Make sure the engine is actually dead.
227 | engine?.close(reason)
228 | handleEvent("disconnect", data: [reason], isInternalMessage: true)
229 | }
230 |
231 | /**
232 | Disconnects the socket. Only reconnect the same socket if you know what you're doing.
233 | Will turn off automatic reconnects.
234 | */
235 | public func disconnect() {
236 | DefaultSocketLogger.Logger.log("Closing socket", type: logType)
237 |
238 | reconnects = false
239 | didDisconnect("Disconnect")
240 | }
241 |
242 | /**
243 | Send a message to the server
244 | */
245 | public func emit(event: String, _ items: AnyObject...) {
246 | emit(event, withItems: items)
247 | }
248 |
249 | /**
250 | Same as emit, but meant for Objective-C
251 | */
252 | public func emit(event: String, withItems items: [AnyObject]) {
253 | guard status == .Connected else {
254 | handleEvent("error", data: ["Tried emitting \(event) when not connected"], isInternalMessage: true)
255 | return
256 | }
257 |
258 | dispatch_async(emitQueue) {
259 | self._emit([event] + items)
260 | }
261 | }
262 |
263 | /**
264 | Sends a message to the server, requesting an ack. Use the onAck method of SocketAckHandler to add
265 | an ack.
266 | */
267 | public func emitWithAck(event: String, _ items: AnyObject...) -> OnAckCallback {
268 | return emitWithAck(event, withItems: items)
269 | }
270 |
271 | /**
272 | Same as emitWithAck, but for Objective-C
273 | */
274 | public func emitWithAck(event: String, withItems items: [AnyObject]) -> OnAckCallback {
275 | return createOnAck([event] + items)
276 | }
277 |
278 | private func _emit(data: [AnyObject], ack: Int? = nil) {
279 | guard status == .Connected else {
280 | handleEvent("error", data: ["Tried emitting when not connected"], isInternalMessage: true)
281 | return
282 | }
283 |
284 | let packet = SocketPacket.packetFromEmit(data, id: ack ?? -1, nsp: nsp, ack: false)
285 | let str = packet.packetString
286 |
287 | DefaultSocketLogger.Logger.log("Emitting: %@", type: logType, args: str)
288 |
289 | engine?.send(str, withData: packet.binary)
290 | }
291 |
292 | // If the server wants to know that the client received data
293 | func emitAck(ack: Int, withItems items: [AnyObject]) {
294 | dispatch_async(emitQueue) {
295 | if self.status == .Connected {
296 | let packet = SocketPacket.packetFromEmit(items, id: ack ?? -1, nsp: self.nsp, ack: true)
297 | let str = packet.packetString
298 |
299 | DefaultSocketLogger.Logger.log("Emitting Ack: %@", type: self.logType, args: str)
300 |
301 | self.engine?.send(str, withData: packet.binary)
302 | }
303 | }
304 | }
305 |
306 | public func engineDidClose(reason: String) {
307 | waitingData.removeAll()
308 |
309 | if status == .Closed || !reconnects {
310 | didDisconnect(reason)
311 | } else if status != .Reconnecting {
312 | status = .Reconnecting
313 | handleEvent("reconnect", data: [reason], isInternalMessage: true)
314 | tryReconnect()
315 | }
316 | }
317 |
318 | /// error
319 | public func engineDidError(reason: String) {
320 | DefaultSocketLogger.Logger.error("%@", type: logType, args: reason)
321 |
322 | handleEvent("error", data: [reason], isInternalMessage: true)
323 | }
324 |
325 | // Called when the socket gets an ack for something it sent
326 | func handleAck(ack: Int, data: [AnyObject]) {
327 | guard status == .Connected else {return}
328 |
329 | DefaultSocketLogger.Logger.log("Handling ack: %@ with data: %@", type: logType, args: ack, data ?? "")
330 |
331 | ackHandlers.executeAck(ack, items: data)
332 | }
333 |
334 | /**
335 | Causes an event to be handled. Only use if you know what you're doing.
336 | */
337 | public func handleEvent(event: String, data: [AnyObject], isInternalMessage: Bool, withAck ack: Int = -1) {
338 | guard status == .Connected || isInternalMessage else {
339 | return
340 | }
341 |
342 | DefaultSocketLogger.Logger.log("Handling event: %@ with data: %@", type: logType, args: event, data ?? "")
343 |
344 | dispatch_async(handleQueue) {
345 | self.anyHandler?(SocketAnyEvent(event: event, items: data))
346 |
347 | for handler in self.handlers where handler.event == event {
348 | handler.executeCallback(data, withAck: ack, withSocket: self)
349 | }
350 | }
351 | }
352 |
353 | /**
354 | Leaves nsp and goes back to /
355 | */
356 | public func leaveNamespace() {
357 | if nsp != "/" {
358 | engine?.send("1\(nsp)", withData: [])
359 | nsp = "/"
360 | }
361 | }
362 |
363 | /**
364 | Joins namespace
365 | */
366 | public func joinNamespace(namespace: String) {
367 | nsp = namespace
368 |
369 | if nsp != "/" {
370 | DefaultSocketLogger.Logger.log("Joining namespace", type: logType)
371 | engine?.send("0\(nsp)", withData: [])
372 | }
373 | }
374 |
375 | /**
376 | Removes handler(s)
377 | */
378 | public func off(event: String) {
379 | DefaultSocketLogger.Logger.log("Removing handler for event: %@", type: logType, args: event)
380 |
381 | handlers = handlers.filter { $0.event != event }
382 | }
383 |
384 | /**
385 | Removes a handler with the specified UUID gotten from an `on` or `once`
386 | */
387 | public func off(id id: NSUUID) {
388 | DefaultSocketLogger.Logger.log("Removing handler with id: %@", type: logType, args: id)
389 |
390 | handlers = handlers.filter { $0.id != id }
391 | }
392 |
393 | /**
394 | Adds a handler for an event.
395 | Returns: A unique id for the handler
396 | */
397 | public func on(event: String, callback: NormalCallback) -> NSUUID {
398 | DefaultSocketLogger.Logger.log("Adding handler for event: %@", type: logType, args: event)
399 |
400 | let handler = SocketEventHandler(event: event, id: NSUUID(), callback: callback)
401 | handlers.append(handler)
402 |
403 | return handler.id
404 | }
405 |
406 | /**
407 | Adds a single-use handler for an event.
408 | Returns: A unique id for the handler
409 | */
410 | public func once(event: String, callback: NormalCallback) -> NSUUID {
411 | DefaultSocketLogger.Logger.log("Adding once handler for event: %@", type: logType, args: event)
412 |
413 | let id = NSUUID()
414 |
415 | let handler = SocketEventHandler(event: event, id: id) {[weak self] data, ack in
416 | guard let this = self else {return}
417 | this.off(id: id)
418 | callback(data, ack)
419 | }
420 |
421 | handlers.append(handler)
422 |
423 | return handler.id
424 | }
425 |
426 | /**
427 | Adds a handler that will be called on every event.
428 | */
429 | public func onAny(handler: (SocketAnyEvent) -> Void) {
430 | anyHandler = handler
431 | }
432 |
433 | /**
434 | Same as connect
435 | */
436 | @available(*, deprecated=6.0)
437 | public func open() {
438 | connect()
439 | }
440 |
441 | public func parseEngineMessage(msg: String) {
442 | DefaultSocketLogger.Logger.log("Should parse message: %@", type: "SocketIOClient", args: msg)
443 |
444 | dispatch_async(parseQueue) {
445 | self.parseSocketMessage(msg)
446 | }
447 | }
448 |
449 | public func parseEngineBinaryData(data: NSData) {
450 | dispatch_async(parseQueue) {
451 | self.parseBinaryData(data)
452 | }
453 | }
454 |
455 | /**
456 | Tries to reconnect to the server.
457 | */
458 | public func reconnect() {
459 | tryReconnect()
460 | }
461 |
462 | /**
463 | Removes all handlers.
464 | Can be used after disconnecting to break any potential remaining retain cycles.
465 | */
466 | public func removeAllHandlers() {
467 | handlers.removeAll(keepCapacity: false)
468 | }
469 |
470 | private func tryReconnect() {
471 | if reconnectTimer == nil {
472 | DefaultSocketLogger.Logger.log("Starting reconnect", type: logType)
473 |
474 | status = .Reconnecting
475 |
476 | dispatch_async(dispatch_get_main_queue()) {
477 | self.reconnectTimer = NSTimer.scheduledTimerWithTimeInterval(Double(self.reconnectWait),
478 | target: self, selector: "_tryReconnect", userInfo: nil, repeats: true)
479 | }
480 | }
481 | }
482 |
483 | @objc private func _tryReconnect() {
484 | if status == .Connected {
485 | clearReconnectTimer()
486 |
487 | return
488 | }
489 |
490 | if reconnectAttempts != -1 && currentReconnectAttempt + 1 > reconnectAttempts || !reconnects {
491 | clearReconnectTimer()
492 | didDisconnect("Reconnect Failed")
493 |
494 | return
495 | }
496 |
497 | DefaultSocketLogger.Logger.log("Trying to reconnect", type: logType)
498 | handleEvent("reconnectAttempt", data: [reconnectAttempts - currentReconnectAttempt],
499 | isInternalMessage: true)
500 |
501 | currentReconnectAttempt += 1
502 | connect()
503 | }
504 | }
505 |
506 | // Test extensions
507 | extension SocketIOClient {
508 | var testHandlers: [SocketEventHandler] {
509 | return handlers
510 | }
511 |
512 | func setTestable() {
513 | status = .Connected
514 | }
515 |
516 | func setTestEngine(engine: SocketEngineSpec?) {
517 | self.engine = engine
518 | }
519 |
520 | func emitTest(event: String, _ data: AnyObject...) {
521 | self._emit([event] + data)
522 | }
523 | }
524 |
--------------------------------------------------------------------------------
/Source/SocketEngine.swift:
--------------------------------------------------------------------------------
1 | //
2 | // SocketEngine.swift
3 | // Socket.IO-Client-Swift
4 | //
5 | // Created by Erik Little on 3/3/15.
6 | //
7 | // Permission is hereby granted, free of charge, to any person obtaining a copy
8 | // of this software and associated documentation files (the "Software"), to deal
9 | // in the Software without restriction, including without limitation the rights
10 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11 | // copies of the Software, and to permit persons to whom the Software is
12 | // furnished to do so, subject to the following conditions:
13 | //
14 | // The above copyright notice and this permission notice shall be included in
15 | // all copies or substantial portions of the Software.
16 | //
17 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
23 | // THE SOFTWARE.
24 |
25 | import Foundation
26 |
27 | public final class SocketEngine: NSObject, SocketEnginePollable, SocketEngineWebsocket {
28 | public let emitQueue = dispatch_queue_create("com.socketio.engineEmitQueue", DISPATCH_QUEUE_SERIAL)
29 | public let handleQueue = dispatch_queue_create("com.socketio.engineHandleQueue", DISPATCH_QUEUE_SERIAL)
30 | public let parseQueue = dispatch_queue_create("com.socketio.engineParseQueue", DISPATCH_QUEUE_SERIAL)
31 |
32 | public var connectParams: [String: AnyObject]? {
33 | didSet {
34 | (urlPolling, urlWebSocket) = createURLs()
35 | }
36 | }
37 | public var postWait = [String]()
38 | public var waitingForPoll = false
39 | public var waitingForPost = false
40 |
41 | public private(set) var closed = false
42 | public private(set) var connected = false
43 | public private(set) var cookies: [NSHTTPCookie]?
44 | public private(set) var extraHeaders: [String: String]?
45 | public private(set) var fastUpgrade = false
46 | public private(set) var forcePolling = false
47 | public private(set) var forceWebsockets = false
48 | public private(set) var invalidated = false
49 | public private(set) var pingTimer: NSTimer?
50 | public private(set) var polling = true
51 | public private(set) var probing = false
52 | public private(set) var session: NSURLSession?
53 | public private(set) var sid = ""
54 | public private(set) var socketPath = "/engine.io/"
55 | public private(set) var urlPolling = NSURL()
56 | public private(set) var urlWebSocket = NSURL()
57 | public private(set) var websocket = false
58 | public private(set) var ws: WebSocket?
59 |
60 | public weak var client: SocketEngineClient?
61 |
62 | private weak var sessionDelegate: NSURLSessionDelegate?
63 |
64 | private typealias Probe = (msg: String, type: SocketEnginePacketType, data: [NSData])
65 | private typealias ProbeWaitQueue = [Probe]
66 |
67 | private let logType = "SocketEngine"
68 | private let url: NSURL
69 |
70 | private var pingInterval: Double?
71 | private var pingTimeout = 0.0 {
72 | didSet {
73 | pongsMissedMax = Int(pingTimeout / (pingInterval ?? 25))
74 | }
75 | }
76 | private var pongsMissed = 0
77 | private var pongsMissedMax = 0
78 | private var probeWait = ProbeWaitQueue()
79 | private var secure = false
80 | private var selfSigned = false
81 | private var voipEnabled = false
82 |
83 | public init(client: SocketEngineClient, url: NSURL, options: Set) {
84 | self.client = client
85 | self.url = url
86 |
87 | for option in options {
88 | switch option {
89 | case let .ConnectParams(params):
90 | connectParams = params
91 | case let .SessionDelegate(delegate):
92 | sessionDelegate = delegate
93 | case let .ForcePolling(force):
94 | forcePolling = force
95 | case let .ForceWebsockets(force):
96 | forceWebsockets = force
97 | case let .Cookies(cookies):
98 | self.cookies = cookies
99 | case let .Path(path):
100 | socketPath = path
101 | case let .ExtraHeaders(headers):
102 | extraHeaders = headers
103 | case let .VoipEnabled(enable):
104 | voipEnabled = enable
105 | case let .Secure(secure):
106 | self.secure = secure
107 | case let .SelfSigned(selfSigned):
108 | self.selfSigned = selfSigned
109 | default:
110 | continue
111 | }
112 | }
113 |
114 | super.init()
115 |
116 | (urlPolling, urlWebSocket) = createURLs()
117 | }
118 |
119 | public convenience init(client: SocketEngineClient, url: NSURL, options: NSDictionary?) {
120 | self.init(client: client, url: url, options: options?.toSocketOptionsSet() ?? [])
121 | }
122 |
123 | @available(*, deprecated=5.3)
124 | public convenience init(client: SocketEngineClient, urlString: String, options: Set) {
125 | guard let url = NSURL(string: urlString) else { fatalError("Incorrect url") }
126 | self.init(client: client, url: url, options: options)
127 | }
128 |
129 | @available(*, deprecated=5.3)
130 | public convenience init(client: SocketEngineClient, urlString: String, options: NSDictionary?) {
131 | guard let url = NSURL(string: urlString) else { fatalError("Incorrect url") }
132 | self.init(client: client, url: url, options: options?.toSocketOptionsSet() ?? [])
133 | }
134 |
135 | deinit {
136 | DefaultSocketLogger.Logger.log("Engine is being released", type: logType)
137 | closed = true
138 | stopPolling()
139 | }
140 |
141 | private func checkAndHandleEngineError(msg: String) {
142 | guard let stringData = msg.dataUsingEncoding(NSUTF8StringEncoding,
143 | allowLossyConversion: false) else { return }
144 |
145 | do {
146 | if let dict = try NSJSONSerialization.JSONObjectWithData(stringData,
147 | options: NSJSONReadingOptions.MutableContainers) as? NSDictionary {
148 | guard let code = dict["code"] as? Int else { return }
149 | guard let error = dict["message"] as? String else { return }
150 |
151 | switch code {
152 | case 0: // Unknown transport
153 | didError(error)
154 | case 1: // Unknown sid.
155 | didError(error)
156 | case 2: // Bad handshake request
157 | didError(error)
158 | case 3: // Bad request
159 | didError(error)
160 | default:
161 | didError(error)
162 | }
163 | }
164 | } catch {
165 | didError("Got unknown error from server \(msg)")
166 | }
167 | }
168 |
169 | private func checkIfMessageIsBase64Binary(message: String) -> Bool {
170 | if message.hasPrefix("b4") {
171 | // binary in base64 string
172 | let noPrefix = message[message.startIndex.advancedBy(2).. (NSURL, NSURL) {
212 | if client == nil {
213 | return (NSURL(), NSURL())
214 | }
215 |
216 | let urlPolling = NSURLComponents(string: url.absoluteString)!
217 | let urlWebSocket = NSURLComponents(string: url.absoluteString)!
218 | var queryString = ""
219 |
220 | urlWebSocket.path = socketPath
221 | urlPolling.path = socketPath
222 | urlWebSocket.query = "transport=websocket"
223 | urlPolling.query = "transport=polling&b64=1"
224 |
225 | if secure {
226 | urlPolling.scheme = "https"
227 | urlWebSocket.scheme = "wss"
228 | } else {
229 | urlPolling.scheme = "http"
230 | urlWebSocket.scheme = "ws"
231 | }
232 |
233 | if connectParams != nil {
234 | for (key, value) in connectParams! {
235 | queryString += "&\(key)=\(value)"
236 | }
237 | }
238 |
239 | urlWebSocket.query = urlWebSocket.query! + queryString
240 | urlPolling.query = urlPolling.query! + queryString
241 |
242 | return (urlPolling.URL!, urlWebSocket.URL!)
243 | }
244 |
245 | private func createWebsocketAndConnect() {
246 | let component = NSURLComponents(URL: urlWebSocket, resolvingAgainstBaseURL: false)!
247 | component.query = component.query! + (sid == "" ? "" : "&sid=\(sid)")
248 |
249 | ws = WebSocket(url: component.URL!)
250 |
251 | if cookies != nil {
252 | let headers = NSHTTPCookie.requestHeaderFieldsWithCookies(cookies!)
253 | for (key, value) in headers {
254 | ws?.headers[key] = value
255 | }
256 | }
257 |
258 | if extraHeaders != nil {
259 | for (headerName, value) in extraHeaders! {
260 | ws?.headers[headerName] = value
261 | }
262 | }
263 |
264 | ws?.queue = handleQueue
265 | ws?.voipEnabled = voipEnabled
266 | ws?.delegate = self
267 | ws?.selfSignedSSL = selfSigned
268 |
269 | ws?.connect()
270 | }
271 |
272 | public func didError(error: String) {
273 | DefaultSocketLogger.Logger.error(error, type: logType)
274 | client?.engineDidError(error)
275 | close(error)
276 | }
277 |
278 | public func doFastUpgrade() {
279 | if waitingForPoll {
280 | DefaultSocketLogger.Logger.error("Outstanding poll when switched to WebSockets," +
281 | "we'll probably disconnect soon. You should report this.", type: logType)
282 | }
283 |
284 | sendWebSocketMessage("", withType: .Upgrade, withData: [])
285 | websocket = true
286 | polling = false
287 | fastUpgrade = false
288 | probing = false
289 | flushProbeWait()
290 | }
291 |
292 | private func flushProbeWait() {
293 | DefaultSocketLogger.Logger.log("Flushing probe wait", type: logType)
294 |
295 | dispatch_async(emitQueue) {
296 | for waiter in self.probeWait {
297 | self.write(waiter.msg, withType: waiter.type, withData: waiter.data)
298 | }
299 |
300 | self.probeWait.removeAll(keepCapacity: false)
301 |
302 | if self.postWait.count != 0 {
303 | self.flushWaitingForPostToWebSocket()
304 | }
305 | }
306 | }
307 |
308 | // We had packets waiting for send when we upgraded
309 | // Send them raw
310 | public func flushWaitingForPostToWebSocket() {
311 | guard let ws = self.ws else { return }
312 |
313 | for msg in postWait {
314 | ws.writeString(fixDoubleUTF8(msg))
315 | }
316 |
317 | postWait.removeAll(keepCapacity: true)
318 | }
319 |
320 | private func handleClose(reason: String) {
321 | client?.engineDidClose(reason)
322 | }
323 |
324 | private func handleMessage(message: String) {
325 | client?.parseEngineMessage(message)
326 | }
327 |
328 | private func handleNOOP() {
329 | doPoll()
330 | }
331 |
332 | private func handleOpen(openData: String) {
333 | let mesData = openData.dataUsingEncoding(NSUTF8StringEncoding, allowLossyConversion: false)!
334 | do {
335 | let json = try NSJSONSerialization.JSONObjectWithData(mesData,
336 | options: NSJSONReadingOptions.AllowFragments) as? NSDictionary
337 | if let sid = json?["sid"] as? String {
338 | let upgradeWs: Bool
339 |
340 | self.sid = sid
341 | connected = true
342 |
343 | if let upgrades = json?["upgrades"] as? [String] {
344 | upgradeWs = upgrades.contains("websocket")
345 | } else {
346 | upgradeWs = false
347 | }
348 |
349 | if let pingInterval = json?["pingInterval"] as? Double, pingTimeout = json?["pingTimeout"] as? Double {
350 | self.pingInterval = pingInterval / 1000.0
351 | self.pingTimeout = pingTimeout / 1000.0
352 | }
353 |
354 | if !forcePolling && !forceWebsockets && upgradeWs {
355 | createWebsocketAndConnect()
356 | }
357 |
358 |
359 | startPingTimer()
360 |
361 | if !forceWebsockets {
362 | doPoll()
363 | }
364 |
365 | client?.engineDidOpen?("Connect")
366 | }
367 | } catch {
368 | didError("Error parsing open packet")
369 | return
370 | }
371 | }
372 |
373 | private func handlePong(pongMessage: String) {
374 | pongsMissed = 0
375 |
376 | // We should upgrade
377 | if pongMessage == "3probe" {
378 | upgradeTransport()
379 | }
380 | }
381 |
382 | public func open() {
383 | if connected {
384 | DefaultSocketLogger.Logger.error("Engine tried opening while connected. This is probably a programming error. "
385 | + "Abandoning open attempt", type: logType)
386 | return
387 | }
388 |
389 | DefaultSocketLogger.Logger.log("Starting engine", type: logType)
390 | DefaultSocketLogger.Logger.log("Handshaking", type: logType)
391 |
392 | resetEngine()
393 |
394 | if forceWebsockets {
395 | polling = false
396 | websocket = true
397 | createWebsocketAndConnect()
398 | return
399 | }
400 |
401 | let reqPolling = NSMutableURLRequest(URL: urlPolling)
402 |
403 | if cookies != nil {
404 | let headers = NSHTTPCookie.requestHeaderFieldsWithCookies(cookies!)
405 | reqPolling.allHTTPHeaderFields = headers
406 | }
407 |
408 | if let extraHeaders = extraHeaders {
409 | for (headerName, value) in extraHeaders {
410 | reqPolling.setValue(value, forHTTPHeaderField: headerName)
411 | }
412 | }
413 |
414 | doLongPoll(reqPolling)
415 | }
416 |
417 | public func parseEngineData(data: NSData) {
418 | DefaultSocketLogger.Logger.log("Got binary data: %@", type: "SocketEngine", args: data)
419 | client?.parseEngineBinaryData(data.subdataWithRange(NSMakeRange(1, data.length - 1)))
420 | }
421 |
422 | public func parseEngineMessage(message: String, fromPolling: Bool) {
423 | DefaultSocketLogger.Logger.log("Got message: %@", type: logType, args: message)
424 |
425 | let reader = SocketStringReader(message: message)
426 | let fixedString: String
427 |
428 | guard let type = SocketEnginePacketType(rawValue: Int(reader.currentCharacter) ?? -1) else {
429 | if !checkIfMessageIsBase64Binary(message) {
430 | checkAndHandleEngineError(message)
431 | }
432 |
433 | return
434 | }
435 |
436 | if fromPolling && type != .Noop {
437 | fixedString = fixDoubleUTF8(message)
438 | } else {
439 | fixedString = message
440 | }
441 |
442 | switch type {
443 | case .Message:
444 | handleMessage(fixedString[fixedString.startIndex.successor().. pongsMissedMax {
477 | pingTimer?.invalidate()
478 | client?.engineDidClose("Ping timeout")
479 | return
480 | }
481 |
482 | pongsMissed += 1
483 | write("", withType: .Ping, withData: [])
484 | }
485 |
486 | // Starts the ping timer
487 | private func startPingTimer() {
488 | if let pingInterval = pingInterval {
489 | pingTimer?.invalidate()
490 | pingTimer = nil
491 |
492 | dispatch_async(dispatch_get_main_queue()) {
493 | self.pingTimer = NSTimer.scheduledTimerWithTimeInterval(pingInterval, target: self,
494 | selector: Selector("sendPing"), userInfo: nil, repeats: true)
495 | }
496 | }
497 | }
498 |
499 | private func upgradeTransport() {
500 | if ws?.isConnected ?? false {
501 | DefaultSocketLogger.Logger.log("Upgrading transport to WebSockets", type: logType)
502 |
503 | fastUpgrade = true
504 | sendPollMessage("", withType: .Noop, withData: [])
505 | // After this point, we should not send anymore polling messages
506 | }
507 | }
508 |
509 | /**
510 | Write a message, independent of transport.
511 | */
512 | public func write(msg: String, withType type: SocketEnginePacketType, withData data: [NSData]) {
513 | dispatch_async(emitQueue) {
514 | guard self.connected else { return }
515 |
516 | if self.websocket {
517 | DefaultSocketLogger.Logger.log("Writing ws: %@ has data: %@",
518 | type: self.logType, args: msg, data.count != 0)
519 | self.sendWebSocketMessage(msg, withType: type, withData: data)
520 | } else if !self.probing {
521 | DefaultSocketLogger.Logger.log("Writing poll: %@ has data: %@",
522 | type: self.logType, args: msg, data.count != 0)
523 | self.sendPollMessage(msg, withType: type, withData: data)
524 | } else {
525 | self.probeWait.append((msg, type, data))
526 | }
527 | }
528 | }
529 |
530 | // Delegate methods
531 | public func websocketDidConnect(socket: WebSocket) {
532 | if !forceWebsockets {
533 | probing = true
534 | probeWebSocket()
535 | } else {
536 | connected = true
537 | probing = false
538 | polling = false
539 | }
540 | }
541 |
542 | public func websocketDidDisconnect(socket: WebSocket, error: NSError?) {
543 | probing = false
544 |
545 | if closed {
546 | client?.engineDidClose("Disconnect")
547 | return
548 | }
549 |
550 | if websocket {
551 | pingTimer?.invalidate()
552 | connected = false
553 | websocket = false
554 |
555 | let reason = error?.localizedDescription ?? "Socket Disconnected"
556 |
557 | if error != nil {
558 | didError(reason)
559 | }
560 |
561 | client?.engineDidClose(reason)
562 | } else {
563 | flushProbeWait()
564 | }
565 | }
566 | }
567 |
--------------------------------------------------------------------------------
/SocketChat.xcodeproj/project.pbxproj:
--------------------------------------------------------------------------------
1 | // !$*UTF8*$!
2 | {
3 | archiveVersion = 1;
4 | classes = {
5 | };
6 | objectVersion = 46;
7 | objects = {
8 |
9 | /* Begin PBXBuildFile section */
10 | C075D71D1C5E0E81009F9044 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = C075D71C1C5E0E81009F9044 /* AppDelegate.swift */; };
11 | C075D7221C5E0E81009F9044 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = C075D7201C5E0E81009F9044 /* Main.storyboard */; };
12 | C075D7241C5E0E81009F9044 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = C075D7231C5E0E81009F9044 /* Assets.xcassets */; };
13 | C075D7271C5E0E81009F9044 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = C075D7251C5E0E81009F9044 /* LaunchScreen.storyboard */; };
14 | C075D7301C5E0EB1009F9044 /* UsersViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = C075D72F1C5E0EB1009F9044 /* UsersViewController.swift */; };
15 | C075D7321C5E0EBB009F9044 /* ChatViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = C075D7311C5E0EBB009F9044 /* ChatViewController.swift */; };
16 | C075D7351C5E0EDC009F9044 /* UserCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = C075D7341C5E0EDC009F9044 /* UserCell.xib */; };
17 | C075D7371C5E0EED009F9044 /* UserCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = C075D7361C5E0EED009F9044 /* UserCell.swift */; };
18 | C075D7391C5E0EFC009F9044 /* ChatCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = C075D7381C5E0EFC009F9044 /* ChatCell.xib */; };
19 | C075D73B1C5E0F10009F9044 /* ChatCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = C075D73A1C5E0F10009F9044 /* ChatCell.swift */; };
20 | C075D73D1C5E0F4E009F9044 /* BaseCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = C075D73C1C5E0F4E009F9044 /* BaseCell.swift */; };
21 | C075D7561C5E17DD009F9044 /* SocketAckEmitter.swift in Sources */ = {isa = PBXBuildFile; fileRef = C075D7401C5E17DD009F9044 /* SocketAckEmitter.swift */; };
22 | C075D7571C5E17DD009F9044 /* SocketAckManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = C075D7411C5E17DD009F9044 /* SocketAckManager.swift */; };
23 | C075D7581C5E17DD009F9044 /* SocketAnyEvent.swift in Sources */ = {isa = PBXBuildFile; fileRef = C075D7421C5E17DD009F9044 /* SocketAnyEvent.swift */; };
24 | C075D7591C5E17DD009F9044 /* SocketClientSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = C075D7431C5E17DD009F9044 /* SocketClientSpec.swift */; };
25 | C075D75A1C5E17DD009F9044 /* SocketEngine.swift in Sources */ = {isa = PBXBuildFile; fileRef = C075D7441C5E17DD009F9044 /* SocketEngine.swift */; };
26 | C075D75B1C5E17DD009F9044 /* SocketEngineClient.swift in Sources */ = {isa = PBXBuildFile; fileRef = C075D7451C5E17DD009F9044 /* SocketEngineClient.swift */; };
27 | C075D75C1C5E17DD009F9044 /* SocketEnginePacketType.swift in Sources */ = {isa = PBXBuildFile; fileRef = C075D7461C5E17DD009F9044 /* SocketEnginePacketType.swift */; };
28 | C075D75D1C5E17DD009F9044 /* SocketEnginePollable.swift in Sources */ = {isa = PBXBuildFile; fileRef = C075D7471C5E17DD009F9044 /* SocketEnginePollable.swift */; };
29 | C075D75E1C5E17DD009F9044 /* SocketEngineSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = C075D7481C5E17DD009F9044 /* SocketEngineSpec.swift */; };
30 | C075D75F1C5E17DD009F9044 /* SocketEngineWebsocket.swift in Sources */ = {isa = PBXBuildFile; fileRef = C075D7491C5E17DD009F9044 /* SocketEngineWebsocket.swift */; };
31 | C075D7601C5E17DD009F9044 /* SocketEventHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = C075D74A1C5E17DD009F9044 /* SocketEventHandler.swift */; };
32 | C075D7611C5E17DD009F9044 /* SocketFixUTF8.swift in Sources */ = {isa = PBXBuildFile; fileRef = C075D74B1C5E17DD009F9044 /* SocketFixUTF8.swift */; };
33 | C075D7621C5E17DD009F9044 /* SocketIOClient.swift in Sources */ = {isa = PBXBuildFile; fileRef = C075D74C1C5E17DD009F9044 /* SocketIOClient.swift */; };
34 | C075D7631C5E17DD009F9044 /* SocketIOClientOption.swift in Sources */ = {isa = PBXBuildFile; fileRef = C075D74D1C5E17DD009F9044 /* SocketIOClientOption.swift */; };
35 | C075D7641C5E17DD009F9044 /* SocketIOClientStatus.swift in Sources */ = {isa = PBXBuildFile; fileRef = C075D74E1C5E17DD009F9044 /* SocketIOClientStatus.swift */; };
36 | C075D7651C5E17DD009F9044 /* SocketLogger.swift in Sources */ = {isa = PBXBuildFile; fileRef = C075D74F1C5E17DD009F9044 /* SocketLogger.swift */; };
37 | C075D7661C5E17DD009F9044 /* SocketPacket.swift in Sources */ = {isa = PBXBuildFile; fileRef = C075D7501C5E17DD009F9044 /* SocketPacket.swift */; };
38 | C075D7671C5E17DD009F9044 /* SocketParsable.swift in Sources */ = {isa = PBXBuildFile; fileRef = C075D7511C5E17DD009F9044 /* SocketParsable.swift */; };
39 | C075D7681C5E17DD009F9044 /* SocketStringReader.swift in Sources */ = {isa = PBXBuildFile; fileRef = C075D7521C5E17DD009F9044 /* SocketStringReader.swift */; };
40 | C075D7691C5E17DD009F9044 /* SocketTypes.swift in Sources */ = {isa = PBXBuildFile; fileRef = C075D7531C5E17DD009F9044 /* SocketTypes.swift */; };
41 | C075D76A1C5E17DD009F9044 /* SwiftRegex.swift in Sources */ = {isa = PBXBuildFile; fileRef = C075D7541C5E17DD009F9044 /* SwiftRegex.swift */; };
42 | C075D76B1C5E17DD009F9044 /* WebSocket.swift in Sources */ = {isa = PBXBuildFile; fileRef = C075D7551C5E17DD009F9044 /* WebSocket.swift */; };
43 | C075D76E1C5E187A009F9044 /* SocketIOManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = C075D76D1C5E187A009F9044 /* SocketIOManager.swift */; };
44 | /* End PBXBuildFile section */
45 |
46 | /* Begin PBXFileReference section */
47 | C075D7191C5E0E81009F9044 /* SocketChat.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = SocketChat.app; sourceTree = BUILT_PRODUCTS_DIR; };
48 | C075D71C1C5E0E81009F9044 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; };
49 | C075D7211C5E0E81009F9044 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; };
50 | C075D7231C5E0E81009F9044 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; };
51 | C075D7261C5E0E81009F9044 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; };
52 | C075D7281C5E0E81009F9044 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; };
53 | C075D72F1C5E0EB1009F9044 /* UsersViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UsersViewController.swift; sourceTree = ""; };
54 | C075D7311C5E0EBB009F9044 /* ChatViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ChatViewController.swift; sourceTree = ""; };
55 | C075D7341C5E0EDC009F9044 /* UserCell.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = UserCell.xib; sourceTree = ""; };
56 | C075D7361C5E0EED009F9044 /* UserCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UserCell.swift; sourceTree = ""; };
57 | C075D7381C5E0EFC009F9044 /* ChatCell.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = ChatCell.xib; sourceTree = ""; };
58 | C075D73A1C5E0F10009F9044 /* ChatCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ChatCell.swift; sourceTree = ""; };
59 | C075D73C1C5E0F4E009F9044 /* BaseCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BaseCell.swift; sourceTree = ""; };
60 | C075D7401C5E17DD009F9044 /* SocketAckEmitter.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SocketAckEmitter.swift; sourceTree = ""; };
61 | C075D7411C5E17DD009F9044 /* SocketAckManager.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SocketAckManager.swift; sourceTree = ""; };
62 | C075D7421C5E17DD009F9044 /* SocketAnyEvent.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SocketAnyEvent.swift; sourceTree = ""; };
63 | C075D7431C5E17DD009F9044 /* SocketClientSpec.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SocketClientSpec.swift; sourceTree = ""; };
64 | C075D7441C5E17DD009F9044 /* SocketEngine.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SocketEngine.swift; sourceTree = ""; };
65 | C075D7451C5E17DD009F9044 /* SocketEngineClient.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SocketEngineClient.swift; sourceTree = ""; };
66 | C075D7461C5E17DD009F9044 /* SocketEnginePacketType.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SocketEnginePacketType.swift; sourceTree = ""; };
67 | C075D7471C5E17DD009F9044 /* SocketEnginePollable.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SocketEnginePollable.swift; sourceTree = ""; };
68 | C075D7481C5E17DD009F9044 /* SocketEngineSpec.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SocketEngineSpec.swift; sourceTree = ""; };
69 | C075D7491C5E17DD009F9044 /* SocketEngineWebsocket.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SocketEngineWebsocket.swift; sourceTree = ""; };
70 | C075D74A1C5E17DD009F9044 /* SocketEventHandler.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SocketEventHandler.swift; sourceTree = ""; };
71 | C075D74B1C5E17DD009F9044 /* SocketFixUTF8.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SocketFixUTF8.swift; sourceTree = ""; };
72 | C075D74C1C5E17DD009F9044 /* SocketIOClient.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SocketIOClient.swift; sourceTree = ""; };
73 | C075D74D1C5E17DD009F9044 /* SocketIOClientOption.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SocketIOClientOption.swift; sourceTree = ""; };
74 | C075D74E1C5E17DD009F9044 /* SocketIOClientStatus.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SocketIOClientStatus.swift; sourceTree = ""; };
75 | C075D74F1C5E17DD009F9044 /* SocketLogger.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SocketLogger.swift; sourceTree = ""; };
76 | C075D7501C5E17DD009F9044 /* SocketPacket.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SocketPacket.swift; sourceTree = ""; };
77 | C075D7511C5E17DD009F9044 /* SocketParsable.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SocketParsable.swift; sourceTree = ""; };
78 | C075D7521C5E17DD009F9044 /* SocketStringReader.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SocketStringReader.swift; sourceTree = ""; };
79 | C075D7531C5E17DD009F9044 /* SocketTypes.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SocketTypes.swift; sourceTree = ""; };
80 | C075D7541C5E17DD009F9044 /* SwiftRegex.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SwiftRegex.swift; sourceTree = ""; };
81 | C075D7551C5E17DD009F9044 /* WebSocket.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = WebSocket.swift; sourceTree = ""; };
82 | C075D76D1C5E187A009F9044 /* SocketIOManager.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SocketIOManager.swift; sourceTree = ""; };
83 | /* End PBXFileReference section */
84 |
85 | /* Begin PBXFrameworksBuildPhase section */
86 | C075D7161C5E0E81009F9044 /* Frameworks */ = {
87 | isa = PBXFrameworksBuildPhase;
88 | buildActionMask = 2147483647;
89 | files = (
90 | );
91 | runOnlyForDeploymentPostprocessing = 0;
92 | };
93 | /* End PBXFrameworksBuildPhase section */
94 |
95 | /* Begin PBXGroup section */
96 | C075D7101C5E0E81009F9044 = {
97 | isa = PBXGroup;
98 | children = (
99 | C075D73E1C5E17A2009F9044 /* SocketIO */,
100 | C075D71B1C5E0E81009F9044 /* SocketChat */,
101 | C075D71A1C5E0E81009F9044 /* Products */,
102 | );
103 | sourceTree = "";
104 | };
105 | C075D71A1C5E0E81009F9044 /* Products */ = {
106 | isa = PBXGroup;
107 | children = (
108 | C075D7191C5E0E81009F9044 /* SocketChat.app */,
109 | );
110 | name = Products;
111 | sourceTree = "";
112 | };
113 | C075D71B1C5E0E81009F9044 /* SocketChat */ = {
114 | isa = PBXGroup;
115 | children = (
116 | C075D76C1C5E186C009F9044 /* SocketIO */,
117 | C075D7331C5E0EC9009F9044 /* Custom Cells */,
118 | C075D72E1C5E0E96009F9044 /* View Controllers */,
119 | C075D71C1C5E0E81009F9044 /* AppDelegate.swift */,
120 | C075D7201C5E0E81009F9044 /* Main.storyboard */,
121 | C075D7231C5E0E81009F9044 /* Assets.xcassets */,
122 | C075D7251C5E0E81009F9044 /* LaunchScreen.storyboard */,
123 | C075D7281C5E0E81009F9044 /* Info.plist */,
124 | );
125 | path = SocketChat;
126 | sourceTree = "";
127 | };
128 | C075D72E1C5E0E96009F9044 /* View Controllers */ = {
129 | isa = PBXGroup;
130 | children = (
131 | C075D72F1C5E0EB1009F9044 /* UsersViewController.swift */,
132 | C075D7311C5E0EBB009F9044 /* ChatViewController.swift */,
133 | );
134 | name = "View Controllers";
135 | sourceTree = "";
136 | };
137 | C075D7331C5E0EC9009F9044 /* Custom Cells */ = {
138 | isa = PBXGroup;
139 | children = (
140 | C075D73C1C5E0F4E009F9044 /* BaseCell.swift */,
141 | C075D7361C5E0EED009F9044 /* UserCell.swift */,
142 | C075D7341C5E0EDC009F9044 /* UserCell.xib */,
143 | C075D73A1C5E0F10009F9044 /* ChatCell.swift */,
144 | C075D7381C5E0EFC009F9044 /* ChatCell.xib */,
145 | );
146 | name = "Custom Cells";
147 | sourceTree = "";
148 | };
149 | C075D73E1C5E17A2009F9044 /* SocketIO */ = {
150 | isa = PBXGroup;
151 | children = (
152 | C075D73F1C5E17DD009F9044 /* Source */,
153 | );
154 | name = SocketIO;
155 | sourceTree = "";
156 | };
157 | C075D73F1C5E17DD009F9044 /* Source */ = {
158 | isa = PBXGroup;
159 | children = (
160 | C075D7401C5E17DD009F9044 /* SocketAckEmitter.swift */,
161 | C075D7411C5E17DD009F9044 /* SocketAckManager.swift */,
162 | C075D7421C5E17DD009F9044 /* SocketAnyEvent.swift */,
163 | C075D7431C5E17DD009F9044 /* SocketClientSpec.swift */,
164 | C075D7441C5E17DD009F9044 /* SocketEngine.swift */,
165 | C075D7451C5E17DD009F9044 /* SocketEngineClient.swift */,
166 | C075D7461C5E17DD009F9044 /* SocketEnginePacketType.swift */,
167 | C075D7471C5E17DD009F9044 /* SocketEnginePollable.swift */,
168 | C075D7481C5E17DD009F9044 /* SocketEngineSpec.swift */,
169 | C075D7491C5E17DD009F9044 /* SocketEngineWebsocket.swift */,
170 | C075D74A1C5E17DD009F9044 /* SocketEventHandler.swift */,
171 | C075D74B1C5E17DD009F9044 /* SocketFixUTF8.swift */,
172 | C075D74C1C5E17DD009F9044 /* SocketIOClient.swift */,
173 | C075D74D1C5E17DD009F9044 /* SocketIOClientOption.swift */,
174 | C075D74E1C5E17DD009F9044 /* SocketIOClientStatus.swift */,
175 | C075D74F1C5E17DD009F9044 /* SocketLogger.swift */,
176 | C075D7501C5E17DD009F9044 /* SocketPacket.swift */,
177 | C075D7511C5E17DD009F9044 /* SocketParsable.swift */,
178 | C075D7521C5E17DD009F9044 /* SocketStringReader.swift */,
179 | C075D7531C5E17DD009F9044 /* SocketTypes.swift */,
180 | C075D7541C5E17DD009F9044 /* SwiftRegex.swift */,
181 | C075D7551C5E17DD009F9044 /* WebSocket.swift */,
182 | );
183 | path = Source;
184 | sourceTree = "";
185 | };
186 | C075D76C1C5E186C009F9044 /* SocketIO */ = {
187 | isa = PBXGroup;
188 | children = (
189 | C075D76D1C5E187A009F9044 /* SocketIOManager.swift */,
190 | );
191 | name = SocketIO;
192 | sourceTree = "";
193 | };
194 | /* End PBXGroup section */
195 |
196 | /* Begin PBXNativeTarget section */
197 | C075D7181C5E0E81009F9044 /* SocketChat */ = {
198 | isa = PBXNativeTarget;
199 | buildConfigurationList = C075D72B1C5E0E81009F9044 /* Build configuration list for PBXNativeTarget "SocketChat" */;
200 | buildPhases = (
201 | C075D7151C5E0E81009F9044 /* Sources */,
202 | C075D7161C5E0E81009F9044 /* Frameworks */,
203 | C075D7171C5E0E81009F9044 /* Resources */,
204 | );
205 | buildRules = (
206 | );
207 | dependencies = (
208 | );
209 | name = SocketChat;
210 | productName = SocketChat;
211 | productReference = C075D7191C5E0E81009F9044 /* SocketChat.app */;
212 | productType = "com.apple.product-type.application";
213 | };
214 | /* End PBXNativeTarget section */
215 |
216 | /* Begin PBXProject section */
217 | C075D7111C5E0E81009F9044 /* Project object */ = {
218 | isa = PBXProject;
219 | attributes = {
220 | LastSwiftUpdateCheck = 0720;
221 | LastUpgradeCheck = 0720;
222 | ORGANIZATIONNAME = AppCoda;
223 | TargetAttributes = {
224 | C075D7181C5E0E81009F9044 = {
225 | CreatedOnToolsVersion = 7.2;
226 | };
227 | };
228 | };
229 | buildConfigurationList = C075D7141C5E0E81009F9044 /* Build configuration list for PBXProject "SocketChat" */;
230 | compatibilityVersion = "Xcode 3.2";
231 | developmentRegion = English;
232 | hasScannedForEncodings = 0;
233 | knownRegions = (
234 | en,
235 | Base,
236 | );
237 | mainGroup = C075D7101C5E0E81009F9044;
238 | productRefGroup = C075D71A1C5E0E81009F9044 /* Products */;
239 | projectDirPath = "";
240 | projectRoot = "";
241 | targets = (
242 | C075D7181C5E0E81009F9044 /* SocketChat */,
243 | );
244 | };
245 | /* End PBXProject section */
246 |
247 | /* Begin PBXResourcesBuildPhase section */
248 | C075D7171C5E0E81009F9044 /* Resources */ = {
249 | isa = PBXResourcesBuildPhase;
250 | buildActionMask = 2147483647;
251 | files = (
252 | C075D7351C5E0EDC009F9044 /* UserCell.xib in Resources */,
253 | C075D7271C5E0E81009F9044 /* LaunchScreen.storyboard in Resources */,
254 | C075D7391C5E0EFC009F9044 /* ChatCell.xib in Resources */,
255 | C075D7241C5E0E81009F9044 /* Assets.xcassets in Resources */,
256 | C075D7221C5E0E81009F9044 /* Main.storyboard in Resources */,
257 | );
258 | runOnlyForDeploymentPostprocessing = 0;
259 | };
260 | /* End PBXResourcesBuildPhase section */
261 |
262 | /* Begin PBXSourcesBuildPhase section */
263 | C075D7151C5E0E81009F9044 /* Sources */ = {
264 | isa = PBXSourcesBuildPhase;
265 | buildActionMask = 2147483647;
266 | files = (
267 | C075D7371C5E0EED009F9044 /* UserCell.swift in Sources */,
268 | C075D75B1C5E17DD009F9044 /* SocketEngineClient.swift in Sources */,
269 | C075D7671C5E17DD009F9044 /* SocketParsable.swift in Sources */,
270 | C075D75A1C5E17DD009F9044 /* SocketEngine.swift in Sources */,
271 | C075D75E1C5E17DD009F9044 /* SocketEngineSpec.swift in Sources */,
272 | C075D7301C5E0EB1009F9044 /* UsersViewController.swift in Sources */,
273 | C075D7581C5E17DD009F9044 /* SocketAnyEvent.swift in Sources */,
274 | C075D7321C5E0EBB009F9044 /* ChatViewController.swift in Sources */,
275 | C075D7591C5E17DD009F9044 /* SocketClientSpec.swift in Sources */,
276 | C075D73B1C5E0F10009F9044 /* ChatCell.swift in Sources */,
277 | C075D7611C5E17DD009F9044 /* SocketFixUTF8.swift in Sources */,
278 | C075D76A1C5E17DD009F9044 /* SwiftRegex.swift in Sources */,
279 | C075D7571C5E17DD009F9044 /* SocketAckManager.swift in Sources */,
280 | C075D76B1C5E17DD009F9044 /* WebSocket.swift in Sources */,
281 | C075D7561C5E17DD009F9044 /* SocketAckEmitter.swift in Sources */,
282 | C075D75C1C5E17DD009F9044 /* SocketEnginePacketType.swift in Sources */,
283 | C075D7661C5E17DD009F9044 /* SocketPacket.swift in Sources */,
284 | C075D7601C5E17DD009F9044 /* SocketEventHandler.swift in Sources */,
285 | C075D7631C5E17DD009F9044 /* SocketIOClientOption.swift in Sources */,
286 | C075D76E1C5E187A009F9044 /* SocketIOManager.swift in Sources */,
287 | C075D75F1C5E17DD009F9044 /* SocketEngineWebsocket.swift in Sources */,
288 | C075D7641C5E17DD009F9044 /* SocketIOClientStatus.swift in Sources */,
289 | C075D75D1C5E17DD009F9044 /* SocketEnginePollable.swift in Sources */,
290 | C075D7621C5E17DD009F9044 /* SocketIOClient.swift in Sources */,
291 | C075D7681C5E17DD009F9044 /* SocketStringReader.swift in Sources */,
292 | C075D7691C5E17DD009F9044 /* SocketTypes.swift in Sources */,
293 | C075D73D1C5E0F4E009F9044 /* BaseCell.swift in Sources */,
294 | C075D7651C5E17DD009F9044 /* SocketLogger.swift in Sources */,
295 | C075D71D1C5E0E81009F9044 /* AppDelegate.swift in Sources */,
296 | );
297 | runOnlyForDeploymentPostprocessing = 0;
298 | };
299 | /* End PBXSourcesBuildPhase section */
300 |
301 | /* Begin PBXVariantGroup section */
302 | C075D7201C5E0E81009F9044 /* Main.storyboard */ = {
303 | isa = PBXVariantGroup;
304 | children = (
305 | C075D7211C5E0E81009F9044 /* Base */,
306 | );
307 | name = Main.storyboard;
308 | sourceTree = "";
309 | };
310 | C075D7251C5E0E81009F9044 /* LaunchScreen.storyboard */ = {
311 | isa = PBXVariantGroup;
312 | children = (
313 | C075D7261C5E0E81009F9044 /* Base */,
314 | );
315 | name = LaunchScreen.storyboard;
316 | sourceTree = "";
317 | };
318 | /* End PBXVariantGroup section */
319 |
320 | /* Begin XCBuildConfiguration section */
321 | C075D7291C5E0E81009F9044 /* Debug */ = {
322 | isa = XCBuildConfiguration;
323 | buildSettings = {
324 | ALWAYS_SEARCH_USER_PATHS = NO;
325 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
326 | CLANG_CXX_LIBRARY = "libc++";
327 | CLANG_ENABLE_MODULES = YES;
328 | CLANG_ENABLE_OBJC_ARC = YES;
329 | CLANG_WARN_BOOL_CONVERSION = YES;
330 | CLANG_WARN_CONSTANT_CONVERSION = YES;
331 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
332 | CLANG_WARN_EMPTY_BODY = YES;
333 | CLANG_WARN_ENUM_CONVERSION = YES;
334 | CLANG_WARN_INT_CONVERSION = YES;
335 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
336 | CLANG_WARN_UNREACHABLE_CODE = YES;
337 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
338 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
339 | COPY_PHASE_STRIP = NO;
340 | DEBUG_INFORMATION_FORMAT = dwarf;
341 | ENABLE_STRICT_OBJC_MSGSEND = YES;
342 | ENABLE_TESTABILITY = YES;
343 | GCC_C_LANGUAGE_STANDARD = gnu99;
344 | GCC_DYNAMIC_NO_PIC = NO;
345 | GCC_NO_COMMON_BLOCKS = YES;
346 | GCC_OPTIMIZATION_LEVEL = 0;
347 | GCC_PREPROCESSOR_DEFINITIONS = (
348 | "DEBUG=1",
349 | "$(inherited)",
350 | );
351 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
352 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
353 | GCC_WARN_UNDECLARED_SELECTOR = YES;
354 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
355 | GCC_WARN_UNUSED_FUNCTION = YES;
356 | GCC_WARN_UNUSED_VARIABLE = YES;
357 | IPHONEOS_DEPLOYMENT_TARGET = 9.2;
358 | MTL_ENABLE_DEBUG_INFO = YES;
359 | ONLY_ACTIVE_ARCH = YES;
360 | SDKROOT = iphoneos;
361 | SWIFT_OPTIMIZATION_LEVEL = "-Onone";
362 | TARGETED_DEVICE_FAMILY = "1,2";
363 | };
364 | name = Debug;
365 | };
366 | C075D72A1C5E0E81009F9044 /* Release */ = {
367 | isa = XCBuildConfiguration;
368 | buildSettings = {
369 | ALWAYS_SEARCH_USER_PATHS = NO;
370 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
371 | CLANG_CXX_LIBRARY = "libc++";
372 | CLANG_ENABLE_MODULES = YES;
373 | CLANG_ENABLE_OBJC_ARC = YES;
374 | CLANG_WARN_BOOL_CONVERSION = YES;
375 | CLANG_WARN_CONSTANT_CONVERSION = YES;
376 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
377 | CLANG_WARN_EMPTY_BODY = YES;
378 | CLANG_WARN_ENUM_CONVERSION = YES;
379 | CLANG_WARN_INT_CONVERSION = YES;
380 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
381 | CLANG_WARN_UNREACHABLE_CODE = YES;
382 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
383 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
384 | COPY_PHASE_STRIP = NO;
385 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
386 | ENABLE_NS_ASSERTIONS = NO;
387 | ENABLE_STRICT_OBJC_MSGSEND = YES;
388 | GCC_C_LANGUAGE_STANDARD = gnu99;
389 | GCC_NO_COMMON_BLOCKS = YES;
390 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
391 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
392 | GCC_WARN_UNDECLARED_SELECTOR = YES;
393 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
394 | GCC_WARN_UNUSED_FUNCTION = YES;
395 | GCC_WARN_UNUSED_VARIABLE = YES;
396 | IPHONEOS_DEPLOYMENT_TARGET = 9.2;
397 | MTL_ENABLE_DEBUG_INFO = NO;
398 | SDKROOT = iphoneos;
399 | TARGETED_DEVICE_FAMILY = "1,2";
400 | VALIDATE_PRODUCT = YES;
401 | };
402 | name = Release;
403 | };
404 | C075D72C1C5E0E81009F9044 /* Debug */ = {
405 | isa = XCBuildConfiguration;
406 | buildSettings = {
407 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
408 | INFOPLIST_FILE = SocketChat/Info.plist;
409 | IPHONEOS_DEPLOYMENT_TARGET = 8.4;
410 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
411 | PRODUCT_BUNDLE_IDENTIFIER = com.appcoda.SocketChat;
412 | PRODUCT_NAME = "$(TARGET_NAME)";
413 | };
414 | name = Debug;
415 | };
416 | C075D72D1C5E0E81009F9044 /* Release */ = {
417 | isa = XCBuildConfiguration;
418 | buildSettings = {
419 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
420 | INFOPLIST_FILE = SocketChat/Info.plist;
421 | IPHONEOS_DEPLOYMENT_TARGET = 8.4;
422 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
423 | PRODUCT_BUNDLE_IDENTIFIER = com.appcoda.SocketChat;
424 | PRODUCT_NAME = "$(TARGET_NAME)";
425 | };
426 | name = Release;
427 | };
428 | /* End XCBuildConfiguration section */
429 |
430 | /* Begin XCConfigurationList section */
431 | C075D7141C5E0E81009F9044 /* Build configuration list for PBXProject "SocketChat" */ = {
432 | isa = XCConfigurationList;
433 | buildConfigurations = (
434 | C075D7291C5E0E81009F9044 /* Debug */,
435 | C075D72A1C5E0E81009F9044 /* Release */,
436 | );
437 | defaultConfigurationIsVisible = 0;
438 | defaultConfigurationName = Release;
439 | };
440 | C075D72B1C5E0E81009F9044 /* Build configuration list for PBXNativeTarget "SocketChat" */ = {
441 | isa = XCConfigurationList;
442 | buildConfigurations = (
443 | C075D72C1C5E0E81009F9044 /* Debug */,
444 | C075D72D1C5E0E81009F9044 /* Release */,
445 | );
446 | defaultConfigurationIsVisible = 0;
447 | defaultConfigurationName = Release;
448 | };
449 | /* End XCConfigurationList section */
450 | };
451 | rootObject = C075D7111C5E0E81009F9044 /* Project object */;
452 | }
453 |
--------------------------------------------------------------------------------