Skip to content
This repository was archived by the owner on Dec 2, 2022. It is now read-only.

Commit 5c157ca

Browse files
committed
Client improvements - disconnect, ping-pong, timeout, reconnect
1 parent b567113 commit 5c157ca

File tree

6 files changed

+92
-19
lines changed

6 files changed

+92
-19
lines changed

SlackKit/Sources/Channel.swift

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ public struct Channel {
4343
internal(set) public var unread: Int?
4444
internal(set) public var unreadCountDisplay: Int?
4545
internal(set) public var hasPins: Bool?
46-
internal(set) public var members = [String]()
46+
internal(set) public var members: [String]?
4747
// Client use
4848
internal(set) public var pinnedItems = [Item]()
4949
internal(set) public var usersTyping = [String]()
@@ -70,11 +70,7 @@ public struct Channel {
7070
unread = channel?["unread_count"] as? Int
7171
unreadCountDisplay = channel?["unread_count_display"] as? Int
7272
hasPins = channel?["has_pins"] as? Bool
73-
74-
if let members = channel?["members"] as? [String] {
75-
self.members = members
76-
}
77-
73+
members = channel?["members"] as? [String]
7874
}
7975

8076
internal init?(id:String?) {

SlackKit/Sources/Client.swift

Lines changed: 77 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,7 @@ public class Client: WebSocketDelegate {
5353
public var teamProfileEventsDelegate: TeamProfileEventsDelegate?
5454

5555
internal var token = "SLACK_AUTH_TOKEN"
56-
56+
5757
public func setAuthToken(token: String) {
5858
self.token = token
5959
}
@@ -63,15 +63,25 @@ public class Client: WebSocketDelegate {
6363
}
6464

6565
internal var webSocket: WebSocket?
66+
internal let api = NetworkInterface()
6667
private var dispatcher: EventDispatcher?
6768

68-
internal let api = NetworkInterface()
69+
private let pingPongQueue = dispatch_queue_create("com.launchsoft.SlackKit", DISPATCH_QUEUE_SERIAL)
70+
internal var ping: Double?
71+
internal var pong: Double?
72+
73+
internal var pingInterval: NSTimeInterval?
74+
internal var timeout: NSTimeInterval?
75+
internal var reconnect: Bool?
6976

7077
required public init(apiToken: String) {
7178
self.token = apiToken
7279
}
7380

74-
public func connect() {
81+
public func connect(pingInterval pingInterval: NSTimeInterval? = nil, timeout: NSTimeInterval? = nil, reconnect: Bool? = nil) {
82+
self.pingInterval = pingInterval
83+
self.timeout = timeout
84+
self.reconnect = reconnect
7585
dispatcher = EventDispatcher(client: self)
7686
webAPI.rtmStart(success: {
7787
(response) -> Void in
@@ -85,6 +95,10 @@ public class Client: WebSocketDelegate {
8595
}, failure:nil)
8696
}
8797

98+
public func disconnect() {
99+
webSocket?.disconnect()
100+
}
101+
88102
//MARK: - Message send
89103
public func sendMessage(message: String, channelID: String) {
90104
if (connected) {
@@ -97,7 +111,7 @@ public class Client: WebSocketDelegate {
97111

98112
private func formatMessageToSlackJsonString(message: (msg: String, channel: String)) -> NSData? {
99113
let json: [String: AnyObject] = [
100-
"id": NSDate().timeIntervalSince1970,
114+
"id": NSDate().slackTimestamp(),
101115
"type": "message",
102116
"channel": message.channel,
103117
"text": message.msg.slackFormatEscaping()
@@ -121,6 +135,52 @@ public class Client: WebSocketDelegate {
121135
sentMessages[ts!.stringValue] = Message(message: message)
122136
}
123137

138+
//MARK: - RTM Ping
139+
private func pingRTMServerAtInterval(interval: NSTimeInterval) {
140+
let delay = dispatch_time(DISPATCH_TIME_NOW, Int64(interval * Double(NSEC_PER_SEC)))
141+
dispatch_after(delay, pingPongQueue, {
142+
if self.connected && self.timeoutCheck() {
143+
self.sendRTMPing()
144+
self.pingRTMServerAtInterval(interval)
145+
} else {
146+
self.disconnect()
147+
}
148+
})
149+
}
150+
151+
private func sendRTMPing() {
152+
if connected {
153+
let json: [String: AnyObject] = [
154+
"id": NSDate().slackTimestamp(),
155+
"type": "ping",
156+
]
157+
do {
158+
let data = try NSJSONSerialization.dataWithJSONObject(json, options: NSJSONWritingOptions.PrettyPrinted)
159+
let string = NSString(data: data, encoding: NSUTF8StringEncoding)
160+
if let writePing = string as? String {
161+
ping = json["id"] as? Double
162+
webSocket?.writeString(writePing)
163+
}
164+
}
165+
catch _ {
166+
167+
}
168+
}
169+
}
170+
171+
private func timeoutCheck() -> Bool {
172+
if let pong = pong, ping = ping, timeout = timeout {
173+
if pong - ping < timeout {
174+
return true
175+
} else {
176+
return false
177+
}
178+
// Ping-pong or timeout not configured
179+
} else {
180+
return true
181+
}
182+
}
183+
124184
//MARK: - Client setup
125185
internal func initialSetup(json: [String: AnyObject]) {
126186
team = Team(team: json["team"] as? [String: AnyObject])
@@ -207,14 +267,21 @@ public class Client: WebSocketDelegate {
207267
}
208268

209269
// MARK: - WebSocketDelegate
210-
public func websocketDidConnect(socket: WebSocket) {}
270+
public func websocketDidConnect(socket: WebSocket) {
271+
if let pingInterval = pingInterval {
272+
pingRTMServerAtInterval(pingInterval)
273+
}
274+
}
211275

212276
public func websocketDidDisconnect(socket: WebSocket, error: NSError?) {
213277
connected = false
214278
authenticated = false
215279
webSocket = nil
216-
if let delegate = slackEventsDelegate {
217-
delegate.clientDisconnected()
280+
dispatcher = nil
281+
authenticatedUser = nil
282+
slackEventsDelegate?.clientDisconnected()
283+
if reconnect == true {
284+
connect(pingInterval: pingInterval, timeout: timeout, reconnect: reconnect)
218285
}
219286
}
220287

@@ -223,7 +290,9 @@ public class Client: WebSocketDelegate {
223290
return
224291
}
225292
do {
226-
try dispatcher?.dispatch(NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions.AllowFragments) as! [String: AnyObject])
293+
if let json = try NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions.AllowFragments) as? [String: AnyObject] {
294+
dispatcher?.dispatch(json)
295+
}
227296
}
228297
catch _ {
229298

SlackKit/Sources/ClientExtensions.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -78,8 +78,8 @@ internal extension String {
7878

7979
public extension NSDate {
8080

81-
func slackTimestamp() -> String {
82-
return NSNumber(double: timeIntervalSince1970).stringValue
81+
func slackTimestamp() -> Double {
82+
return NSNumber(double: timeIntervalSince1970).doubleValue
8383
}
8484

8585
}

SlackKit/Sources/Event.swift

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,7 @@ internal enum EventType: String {
6262
case FileCommentDeleted = "file_comment_deleted"
6363
case PinAdded = "pin_added"
6464
case PinRemoved = "pin_removed"
65+
case Pong = "pong"
6566
case PresenceChange = "presence_change"
6667
case ManualPresenceChange = "manual_presence_change"
6768
case PrefChange = "pref_change"

SlackKit/Sources/EventDispatcher.swift

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,8 @@ internal class EventDispatcher {
9696
handler.pinAdded(event)
9797
case .PinRemoved:
9898
handler.pinRemoved(event)
99+
case .Pong:
100+
handler.pong(event)
99101
case .PresenceChange:
100102
handler.presenceChange(event)
101103
case .ManualPresenceChange:
@@ -146,7 +148,7 @@ internal class EventDispatcher {
146148
// Other clients should ignore this event.
147149
break
148150
case .TeamMigrationStarted:
149-
client.connect()
151+
client.connect(client.pingInterval, timeout: client.timeout, reconnect: client.reconnect)
150152
case .ReconnectURL:
151153
// The reconnect_url event is currently unsupported and experimental.
152154
break

SlackKit/Sources/EventHandler.swift

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,11 @@ internal class EventHandler {
3838
}
3939
}
4040

41+
//MARK: - Pong
42+
func pong(event: Event) {
43+
client.pong = event.replyTo
44+
}
45+
4146
//MARK: - Messages
4247
func messageSent(event: Event) {
4348
if let reply = event.replyTo, message = client.sentMessages[NSNumber(double: reply).stringValue], channel = message.channel, ts = message.ts {
@@ -147,8 +152,8 @@ internal class EventHandler {
147152

148153
func channelLeft(event: Event) {
149154
if let channel = event.channel, id = channel.id, userID = client.authenticatedUser?.id {
150-
if let index = client.channels[id]?.members.indexOf(userID) {
151-
client.channels[id]?.members.removeAtIndex(index)
155+
if let index = client.channels[id]?.members?.indexOf(userID) {
156+
client.channels[id]?.members?.removeAtIndex(index)
152157

153158
if let delegate = client.channelEventsDelegate {
154159
delegate.channelLeft(channel)

0 commit comments

Comments
 (0)