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

Commit

Permalink
Merge pull request #23 from pvzig/feature/client-improvements
Browse files Browse the repository at this point in the history
Feature/client-improvements
  • Loading branch information
pvzig committed Mar 23, 2016
2 parents 5b08fb6 + b87232d commit d2037f4
Show file tree
Hide file tree
Showing 11 changed files with 123 additions and 42 deletions.
4 changes: 2 additions & 2 deletions Podfile.lock
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
PODS:
- Starscream (1.0.2)
- Starscream (1.1.2)

DEPENDENCIES:
- Starscream

SPEC CHECKSUMS:
Starscream: 40e2c4c1c770d811f24116b8b7dbeb4542c56767
Starscream: 58a12fd35a3cb6aaa105716c2d42765f7c1c732f

COCOAPODS: 0.39.0
12 changes: 10 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,11 @@ If you want to receive messages from the Slack RTM API, connect to it.
client.connect()
```

You can also set options for a ping/pong interval, timeout interval, and automatic reconnection:
```swift
client.connect(pingInterval: 2, timeout: 10, reconnect: false)
```

Once connected, the client will begin to consume any messages sent by the Slack RTM API.

####Web API Methods
Expand All @@ -65,6 +70,9 @@ SlackKit currently supports the a subset of the Slack Web APIs that are availabl
- chat.postMessage
- chat.update
- emoji.list
- files.comments.add
- files.comments.edit
- files.comments.delete
- files.delete
- files.upload
- groups.close
Expand Down Expand Up @@ -187,8 +195,8 @@ func itemStarred(item: Item, star: Bool)

#####ReactionEventsDelegate
```swift
func reactionAdded(reaction: String?, item: Item?)
func reactionRemoved(reaction: String?, item: Item?)
func reactionAdded(reaction: String?, item: Item?, itemUser: String?)
func reactionRemoved(reaction: String?, item: Item?, itemUser: String?)
```

#####TeamEventsDelegate
Expand Down
3 changes: 2 additions & 1 deletion SlackKit.podspec
Original file line number Diff line number Diff line change
Expand Up @@ -12,5 +12,6 @@ Pod::Spec.new do |s|
s.requires_arc = true
s.source_files = 'SlackKit/Sources/*.swift'
s.frameworks = 'Foundation'
s.dependency 'Starscream', '~> 1.0.2'
s.dependency 'Starscream', '~> 1.1.2'
end

8 changes: 2 additions & 6 deletions SlackKit/Sources/Channel.swift
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ public struct Channel {
internal(set) public var unread: Int?
internal(set) public var unreadCountDisplay: Int?
internal(set) public var hasPins: Bool?
internal(set) public var members = [String]()
internal(set) public var members: [String]?
// Client use
internal(set) public var pinnedItems = [Item]()
internal(set) public var usersTyping = [String]()
Expand All @@ -70,11 +70,7 @@ public struct Channel {
unread = channel?["unread_count"] as? Int
unreadCountDisplay = channel?["unread_count_display"] as? Int
hasPins = channel?["has_pins"] as? Bool

if let members = channel?["members"] as? [String] {
self.members = members
}

members = channel?["members"] as? [String]
}

internal init?(id:String?) {
Expand Down
90 changes: 80 additions & 10 deletions SlackKit/Sources/Client.swift
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ public class Client: WebSocketDelegate {
public var teamProfileEventsDelegate: TeamProfileEventsDelegate?

internal var token = "SLACK_AUTH_TOKEN"

public func setAuthToken(token: String) {
self.token = token
}
Expand All @@ -63,15 +63,25 @@ public class Client: WebSocketDelegate {
}

internal var webSocket: WebSocket?
internal let api = NetworkInterface()
private var dispatcher: EventDispatcher?

internal let api = NetworkInterface()
private let pingPongQueue = dispatch_queue_create("com.launchsoft.SlackKit", DISPATCH_QUEUE_SERIAL)
internal var ping: Double?
internal var pong: Double?

internal var pingInterval: NSTimeInterval?
internal var timeout: NSTimeInterval?
internal var reconnect: Bool?

required public init(apiToken: String) {
self.token = apiToken
}

public func connect() {
public func connect(pingInterval pingInterval: NSTimeInterval? = nil, timeout: NSTimeInterval? = nil, reconnect: Bool? = nil) {
self.pingInterval = pingInterval
self.timeout = timeout
self.reconnect = reconnect
dispatcher = EventDispatcher(client: self)
webAPI.rtmStart(success: {
(response) -> Void in
Expand All @@ -85,19 +95,24 @@ public class Client: WebSocketDelegate {
}, failure:nil)
}

public func disconnect() {
webSocket?.disconnect()
}

//MARK: - Message send
public func sendMessage(message: String, channelID: String) {
if (connected) {
if let data = formatMessageToSlackJsonString(msg: message, channel: channelID) {
let string = NSString(data: data, encoding: NSUTF8StringEncoding)
webSocket?.writeString(string as! String)
if let string = NSString(data: data, encoding: NSUTF8StringEncoding) as? String {
webSocket?.writeString(string)
}
}
}
}

private func formatMessageToSlackJsonString(message: (msg: String, channel: String)) -> NSData? {
let json: [String: AnyObject] = [
"id": NSDate().timeIntervalSince1970,
"id": NSDate().slackTimestamp(),
"type": "message",
"channel": message.channel,
"text": message.msg.slackFormatEscaping()
Expand All @@ -121,6 +136,52 @@ public class Client: WebSocketDelegate {
sentMessages[ts!.stringValue] = Message(message: message)
}

//MARK: - RTM Ping
private func pingRTMServerAtInterval(interval: NSTimeInterval) {
let delay = dispatch_time(DISPATCH_TIME_NOW, Int64(interval * Double(NSEC_PER_SEC)))
dispatch_after(delay, pingPongQueue, {
if self.connected && self.timeoutCheck() {
self.sendRTMPing()
self.pingRTMServerAtInterval(interval)
} else {
self.disconnect()
}
})
}

private func sendRTMPing() {
if connected {
let json: [String: AnyObject] = [
"id": NSDate().slackTimestamp(),
"type": "ping",
]
do {
let data = try NSJSONSerialization.dataWithJSONObject(json, options: NSJSONWritingOptions.PrettyPrinted)
let string = NSString(data: data, encoding: NSUTF8StringEncoding)
if let writePing = string as? String {
ping = json["id"] as? Double
webSocket?.writeString(writePing)
}
}
catch _ {

}
}
}

private func timeoutCheck() -> Bool {
if let pong = pong, ping = ping, timeout = timeout {
if pong - ping < timeout {
return true
} else {
return false
}
// Ping-pong or timeout not configured
} else {
return true
}
}

//MARK: - Client setup
internal func initialSetup(json: [String: AnyObject]) {
team = Team(team: json["team"] as? [String: AnyObject])
Expand Down Expand Up @@ -207,14 +268,21 @@ public class Client: WebSocketDelegate {
}

// MARK: - WebSocketDelegate
public func websocketDidConnect(socket: WebSocket) {}
public func websocketDidConnect(socket: WebSocket) {
if let pingInterval = pingInterval {
pingRTMServerAtInterval(pingInterval)
}
}

public func websocketDidDisconnect(socket: WebSocket, error: NSError?) {
connected = false
authenticated = false
webSocket = nil
if let delegate = slackEventsDelegate {
delegate.clientDisconnected()
dispatcher = nil
authenticatedUser = nil
slackEventsDelegate?.clientDisconnected()
if reconnect == true {
connect(pingInterval: pingInterval, timeout: timeout, reconnect: reconnect)
}
}

Expand All @@ -223,7 +291,9 @@ public class Client: WebSocketDelegate {
return
}
do {
try dispatcher?.dispatch(NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions.AllowFragments) as! [String: AnyObject])
if let json = try NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions.AllowFragments) as? [String: AnyObject] {
dispatcher?.dispatch(json)
}
}
catch _ {

Expand Down
24 changes: 8 additions & 16 deletions SlackKit/Sources/ClientExtensions.swift
Original file line number Diff line number Diff line change
Expand Up @@ -26,20 +26,11 @@ import Foundation
extension Client {

//MARK: - User & Channel
public func getChannelOrUserIdByName(name: String) -> String? {
if (name[name.startIndex] == "@") {
return getUserIdByName(name)
} else if (name[name.startIndex] == "C") {
return getChannelIDByName(name)
}
return nil
}

public func getChannelIDByName(name: String) -> String? {
return channels.filter{$0.1.name == stripString(name)}.first?.0
}

public func getUserIdByName(name: String) -> String? {
public func getUserIDByName(name: String) -> String? {
return users.filter{$0.1.name == stripString(name)}.first?.0
}

Expand All @@ -54,13 +45,14 @@ extension Client {
}

//MARK: - Utilities
internal func stripString(var string: String) -> String {
internal func stripString(string: String) -> String? {
var strippedString: String?
if string[string.startIndex] == "@" {
string = string.substringFromIndex(string.startIndex.advancedBy(1))
strippedString = string.substringFromIndex(string.startIndex.advancedBy(1))
} else if string[string.startIndex] == "#" {
string = string.substringFromIndex(string.startIndex.advancedBy(1))
strippedString = string.substringFromIndex(string.startIndex.advancedBy(1))
}
return string
return strippedString
}

}
Expand All @@ -78,8 +70,8 @@ internal extension String {

public extension NSDate {

func slackTimestamp() -> String {
return NSNumber(double: timeIntervalSince1970).stringValue
func slackTimestamp() -> Double {
return NSNumber(double: timeIntervalSince1970).doubleValue
}

}
1 change: 1 addition & 0 deletions SlackKit/Sources/Event.swift
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@ internal enum EventType: String {
case FileCommentDeleted = "file_comment_deleted"
case PinAdded = "pin_added"
case PinRemoved = "pin_removed"
case Pong = "pong"
case PresenceChange = "presence_change"
case ManualPresenceChange = "manual_presence_change"
case PrefChange = "pref_change"
Expand Down
4 changes: 3 additions & 1 deletion SlackKit/Sources/EventDispatcher.swift
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,8 @@ internal class EventDispatcher {
handler.pinAdded(event)
case .PinRemoved:
handler.pinRemoved(event)
case .Pong:
handler.pong(event)
case .PresenceChange:
handler.presenceChange(event)
case .ManualPresenceChange:
Expand Down Expand Up @@ -146,7 +148,7 @@ internal class EventDispatcher {
// Other clients should ignore this event.
break
case .TeamMigrationStarted:
client.connect()
client.connect(pingInterval: client.pingInterval, timeout: client.timeout, reconnect: client.reconnect)
case .ReconnectURL:
// The reconnect_url event is currently unsupported and experimental.
break
Expand Down
9 changes: 7 additions & 2 deletions SlackKit/Sources/EventHandler.swift
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,11 @@ internal class EventHandler {
}
}

//MARK: - Pong
func pong(event: Event) {
client.pong = event.replyTo
}

//MARK: - Messages
func messageSent(event: Event) {
if let reply = event.replyTo, message = client.sentMessages[NSNumber(double: reply).stringValue], channel = message.channel, ts = message.ts {
Expand Down Expand Up @@ -147,8 +152,8 @@ internal class EventHandler {

func channelLeft(event: Event) {
if let channel = event.channel, id = channel.id, userID = client.authenticatedUser?.id {
if let index = client.channels[id]?.members.indexOf(userID) {
client.channels[id]?.members.removeAtIndex(index)
if let index = client.channels[id]?.members?.indexOf(userID) {
client.channels[id]?.members?.removeAtIndex(index)

if let delegate = client.channelEventsDelegate {
delegate.channelLeft(channel)
Expand Down
4 changes: 2 additions & 2 deletions SlackKit/Sources/File.swift
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ public struct File {
internal(set) public var comments = [String: Comment]()
internal(set) public var reactions = [String: Reaction]()

init?(file:[String: AnyObject]?) {
public init?(file:[String: AnyObject]?) {
id = file?["id"] as? String
created = file?["created"] as? Int
name = file?["name"] as? String
Expand Down Expand Up @@ -105,7 +105,7 @@ public struct File {

}

init?(id:String?) {
internal init?(id:String?) {
self.id = id
created = nil
name = nil
Expand Down
6 changes: 6 additions & 0 deletions SlackKit/Sources/SlackWebAPIErrorDispatcher.swift
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ public enum SlackError: ErrorType {
case BadRedirectURI
case BadTimeStamp
case CantArchiveGeneral
case CantDelete
case CantDeleteFile
case CantDeleteMessage
case CantInvite
Expand Down Expand Up @@ -75,6 +76,7 @@ public enum SlackError: ErrorType {
case MissingPostType
case NameTaken
case NoChannel
case NoComment
case NoItemSpecified
case NoReaction
case NoText
Expand Down Expand Up @@ -131,6 +133,8 @@ internal struct ErrorDispatcher {
return .BadRedirectURI
case "bad_timestamp":
return .BadTimeStamp
case "cant_delete":
return .CantDelete
case "cant_delete_file":
return .CantDeleteFile
case "cant_delete_message":
Expand Down Expand Up @@ -213,6 +217,8 @@ internal struct ErrorDispatcher {
return .NameTaken
case "no_channel":
return .NoChannel
case "no_comment":
return .NoComment
case "no_reaction":
return .NoReaction
case "no_item_specified":
Expand Down

0 comments on commit d2037f4

Please sign in to comment.