Compare commits

...

17 Commits

Author SHA1 Message Date
Peter Zignego d2037f4cc5 Merge pull request #23 from pvzig/feature/client-improvements
Feature/client-improvements
2016-03-22 21:44:20 -04:00
Peter Zignego b87232dfb5 Readme updates 2016-03-22 21:38:17 -04:00
Peter Zignego 9e5678739f Update readme 2016-03-22 21:34:45 -04:00
Peter Zignego 5cc8582d65 Code cleanup 2016-03-22 21:29:16 -04:00
Peter Zignego aa078934c0 File comment web api errors 2016-03-22 20:56:10 -04:00
Peter Zignego 5c157caea3 Client improvements - disconnect, ping-pong, timeout, reconnect 2016-03-22 20:50:29 -04:00
Peter Zignego b567113b5f Update Starscream dependency version 2016-03-22 20:44:58 -04:00
Peter Zignego 5b08fb6031 Merge pull request #21 from pvzig/feature/file-comment
Add support for file comment web API calls
2016-03-19 14:21:52 -04:00
Peter Zignego b48f33fb72 Add support for file comment web API calls 2016-03-19 14:12:04 -04:00
Peter Zignego deccb727a1 Merge pull request #20 from pvzig/feature/reactions-itemUser
Add support for the item user reaction property
2016-03-15 23:48:55 -04:00
Peter Zignego 8f1df8d138 Add support for the itemUser reaction property 2016-03-15 23:48:08 -04:00
Peter Zignego 0048710e24 Merge pull request #19 from pvzig/feature/dnd-read
DND Read Scope Web API implementation
2016-03-15 23:38:08 -04:00
Peter Zignego f6da0ddd32 DND read scope fixes 2016-03-15 23:37:33 -04:00
Peter Zignego 513485e704 DND Read Scope Web API implementation 2016-03-15 23:28:38 -04:00
Peter Zignego fb3719c29d Merge pull request #18 from pvzig/feature/rtm-team-profile
Add support for team profile events
2016-03-15 22:48:43 -04:00
Peter Zignego 76fdc55f9e Team profile events 2016-03-15 22:45:45 -04:00
Peter Zignego 687b57fc1f Version bump 2016-03-01 23:08:24 -05:00
16 changed files with 344 additions and 51 deletions
+2 -2
View File
@@ -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
+10 -2
View File
@@ -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
@@ -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
@@ -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
+3 -2
View File
@@ -1,6 +1,6 @@
Pod::Spec.new do |s|
s.name = "SlackKit"
s.version = "0.9.8"
s.version = "0.9.9"
s.summary = "a Slack client library for iOS and OS X written in Swift"
s.homepage = "https://github.com/pvzig/SlackKit"
s.license = 'MIT'
@@ -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
+1 -1
View File
@@ -15,7 +15,7 @@
<key>CFBundlePackageType</key>
<string>FMWK</string>
<key>CFBundleShortVersionString</key>
<string>0.9.8</string>
<string>0.9.9</string>
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleVersion</key>
+2 -6
View File
@@ -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]()
@@ -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?) {
+81 -10
View File
@@ -50,9 +50,10 @@ public class Client: WebSocketDelegate {
public var reactionEventsDelegate: ReactionEventsDelegate?
public var teamEventsDelegate: TeamEventsDelegate?
public var subteamEventsDelegate: SubteamEventsDelegate?
public var teamProfileEventsDelegate: TeamProfileEventsDelegate?
internal var token = "SLACK_AUTH_TOKEN"
public func setAuthToken(token: String) {
self.token = token
}
@@ -62,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
@@ -84,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()
@@ -120,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])
@@ -206,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)
}
}
@@ -222,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 _ {
+8 -16
View File
@@ -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
}
@@ -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
}
}
@@ -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
}
}
+9
View File
@@ -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"
@@ -78,10 +79,14 @@ internal enum EventType: String {
case TeamRename = "team_rename"
case TeamDomainChange = "team_domain_change"
case EmailDomainChange = "email_domain_change"
case TeamProfileChange = "team_profile_change"
case TeamProfileDelete = "team_profile_delete"
case TeamProfileReorder = "team_profile_reorder"
case BotAdded = "bot_added"
case BotChanged = "bot_changed"
case AccountsChanged = "accounts_changed"
case TeamMigrationStarted = "team_migration_started"
case ReconnectURL = "reconnect_url"
case SubteamCreated = "subteam_created"
case SubteamUpdated = "subteam_updated"
case SubteamSelfAdded = "subteam_self_added"
@@ -148,10 +153,12 @@ internal struct Event {
let file: File?
let message: Message?
let nestedMessage: Message?
let itemUser: String?
let item: Item?
let dndStatus: DoNotDisturbStatus?
let subteam: UserGroup?
let subteamID: String?
var profile: CustomProfile?
init(event:[String: AnyObject]) {
if let eventType = event["type"] as? String {
@@ -183,11 +190,13 @@ internal struct Event {
bot = Bot(bot: event["bot"] as? [String: AnyObject])
edited = Edited(edited:event["edited"] as? [String: AnyObject])
dndStatus = DoNotDisturbStatus(status: event["dnd_status"] as? [String: AnyObject])
itemUser = event["item_user"] as? String
item = Item(item: event["item"] as? [String: AnyObject])
subteam = UserGroup(userGroup: event["subteam"] as? [String: AnyObject])
subteamID = event["subteam_id"] as? String
message = Message(message: event)
nestedMessage = Message(message: event["message"] as? [String: AnyObject])
profile = CustomProfile(profile: event["profile"] as? [String: AnyObject])
// Comment, Channel, User, and File can come across as Strings or Dictionaries
if (Comment(comment: event["comment"] as? [String: AnyObject])?.id == nil) {
+8 -2
View File
@@ -80,8 +80,8 @@ public protocol StarEventsDelegate {
}
public protocol ReactionEventsDelegate {
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?)
}
public protocol TeamEventsDelegate {
@@ -99,3 +99,9 @@ public protocol SubteamEventsDelegate {
func subteamSelfAdded(subteamID: String)
func subteamSelfRemoved(subteamID: String)
}
public protocol TeamProfileEventsDelegate {
func teamProfileChanged(profile: CustomProfile?)
func teamProfileDeleted(profile: CustomProfile?)
func teamProfileReordered(profile: CustomProfile?)
}
+17 -3
View File
@@ -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:
@@ -117,7 +119,9 @@ internal class EventDispatcher {
case .EmojiChanged:
handler.emojiChanged(event)
case .CommandsChanged:
// Not implemented per Slack documentation.
// This functionality is only used by our web client.
// The other APIs required to support slash command metadata are currently unstable.
// Until they are released other clients should ignore this event.
break
case .TeamPlanChange:
handler.teamPlanChange(event)
@@ -129,15 +133,25 @@ internal class EventDispatcher {
handler.teamDomainChange(event)
case .EmailDomainChange:
handler.emailDomainChange(event)
case .TeamProfileChange:
handler.teamProfileChange(event)
case .TeamProfileDelete:
handler.teamProfileDeleted(event)
case .TeamProfileReorder:
handler.teamProfileReordered(event)
case .BotAdded:
handler.bot(event)
case .BotChanged:
handler.bot(event)
case .AccountsChanged:
// Not implemented per Slack documentation.
// The accounts_changed event is used by our web client to maintain a list of logged-in accounts.
// 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
case .SubteamCreated, .SubteamUpdated:
handler.subteam(event)
case .SubteamSelfAdded:
+50 -5
View File
@@ -38,8 +38,12 @@ internal class EventHandler {
}
}
//MARK: - Messages
//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 {
message.ts = event.ts
@@ -148,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)
@@ -397,7 +401,7 @@ internal class EventHandler {
}
if let delegate = client.reactionEventsDelegate {
delegate.reactionAdded(event.reaction, item: event.item)
delegate.reactionAdded(event.reaction, item: event.item, itemUser: event.itemUser)
}
}
}
@@ -440,7 +444,7 @@ internal class EventHandler {
}
if let delegate = client.reactionEventsDelegate {
delegate.reactionAdded(event.reaction, item: event.item)
delegate.reactionAdded(event.reaction, item: event.item, itemUser: event.itemUser)
}
}
}
@@ -592,6 +596,47 @@ internal class EventHandler {
}
}
//MARK: - Team Profiles
func teamProfileChange(event: Event) {
for user in client.users {
if let fields = event.profile?.fields {
for key in fields.keys {
client.users[user.0]?.profile?.customProfile?.fields[key]?.updateProfileField(fields[key])
}
}
}
if let delegate = client.teamProfileEventsDelegate {
delegate.teamProfileChanged(event.profile)
}
}
func teamProfileDeleted(event: Event) {
for user in client.users {
if let id = event.profile?.fields.first?.0 {
client.users[user.0]?.profile?.customProfile?.fields[id] = nil
}
}
if let delegate = client.teamProfileEventsDelegate {
delegate.teamProfileDeleted(event.profile)
}
}
func teamProfileReordered(event: Event) {
for user in client.users {
if let keys = event.profile?.fields.keys {
for key in keys {
client.users[user.0]?.profile?.customProfile?.fields[key]?.ordering = event.profile?.fields[key]?.ordering
}
}
}
if let delegate = client.teamProfileEventsDelegate {
delegate.teamProfileReordered(event.profile)
}
}
//MARK: - Authenticated User
func manualPresenceChange(event: Event) {
client.authenticatedUser?.presence = event.presence
+2 -2
View File
@@ -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
@@ -105,7 +105,7 @@ public struct File {
}
init?(id:String?) {
internal init?(id:String?) {
self.id = id
created = nil
name = nil
+69
View File
@@ -35,7 +35,12 @@ internal enum SlackAPIEndpoint: String {
case ChatDelete = "chat.delete"
case ChatPostMessage = "chat.postMessage"
case ChatUpdate = "chat.update"
case DNDInfo = "dnd.info"
case DNDTeamInfo = "dnd.teamInfo"
case EmojiList = "emoji.list"
case FilesCommentsAdd = "files.comments.add"
case FilesCommentsEdit = "files.comments.edit"
case FilesCommentsDelete = "files.comments.delete"
case FilesDelete = "files.delete"
case FilesUpload = "files.upload"
case GroupsClose = "groups.close"
@@ -209,6 +214,27 @@ public class SlackWebAPI {
}
}
//MARK: - Do Not Disturb
public func dndInfo(user: String? = nil, success: ((status: DoNotDisturbStatus?)->Void)?, failure: FailureClosure?) {
let parameters: [String: AnyObject?] = ["user": user]
client.api.request(.DNDInfo, token: client.token, parameters: filterNilParameters(parameters), successClosure: {
(response) -> Void in
success?(status: DoNotDisturbStatus(status: response))
}) {(error) -> Void in
failure?(error: error)
}
}
public func dndTeamInfo(users: [String]? = nil, success: ((statuses: [String: DoNotDisturbStatus]?)->Void)?, failure: FailureClosure?) {
let parameters: [String: AnyObject?] = ["users":users?.joinWithSeparator(",")]
client.api.request(.DNDTeamInfo, token: client.token, parameters: filterNilParameters(parameters), successClosure: {
(response) -> Void in
success?(statuses: self.enumerateDNDStauses(response["users"] as? [String: AnyObject]))
}) {(error) -> Void in
failure?(error: error)
}
}
//MARK: - Emoji
public func emojiList(success: ((emojiList: [String: AnyObject]?)->Void)?, failure: FailureClosure?) {
client.api.request(.EmojiList, token: client.token, parameters: nil, successClosure: {
@@ -240,6 +266,37 @@ public class SlackWebAPI {
}
}
//MARK: - File Comments
public func addFileComment(fileID: String, comment: String, success: ((comment: Comment?)->Void)?, failure: FailureClosure?) {
let parameters: [String: AnyObject] = ["file":fileID, "comment":comment.slackFormatEscaping()]
client.api.request(.FilesCommentsAdd, token: client.token, parameters: parameters, successClosure: {
(response) -> Void in
success?(comment: Comment(comment: response["comment"] as? [String: AnyObject]))
}) {(error) -> Void in
failure?(error: error)
}
}
public func editFileComment(fileID: String, commentID: String, comment: String, success: ((comment: Comment?)->Void)?, failure: FailureClosure?) {
let parameters: [String: AnyObject] = ["file":fileID, "id":commentID, "comment":comment.slackFormatEscaping()]
client.api.request(.FilesCommentsEdit, token: client.token, parameters: parameters, successClosure: {
(response) -> Void in
success?(comment: Comment(comment: response["comment"] as? [String: AnyObject]))
}) {(error) -> Void in
failure?(error: error)
}
}
public func deleteFileComment(fileID: String, commentID: String, success: ((deleted: Bool?)->Void)?, failure: FailureClosure?) {
let parameters: [String: AnyObject] = ["file":fileID, "id": commentID]
client.api.request(.FilesCommentsDelete, token: client.token, parameters: parameters, successClosure: {
(response) -> Void in
success?(deleted: true)
}) {(error) -> Void in
failure?(error: error)
}
}
//MARK: - Groups
public func closeGroup(groupID: String, success: ((closed: Bool)->Void)?, failure: FailureClosure?) {
close(.GroupsClose, channelID: groupID, success: {
@@ -634,6 +691,7 @@ public class SlackWebAPI {
return finalParameters
}
//MARK: - Encode Attachments
private func encodeAttachments(attachments: [Attachment?]?) -> NSString? {
if let attachments = attachments {
var attachmentArray: [[String: AnyObject]] = []
@@ -653,4 +711,15 @@ public class SlackWebAPI {
return nil
}
//MARK: - Enumerate Do Not Distrub Status
private func enumerateDNDStauses(statuses: [String: AnyObject]?) -> [String: DoNotDisturbStatus] {
var retVal = [String: DoNotDisturbStatus]()
if let keys = statuses?.keys {
for key in keys {
retVal[key] = DoNotDisturbStatus(status: statuses?[key] as? [String: AnyObject])
}
}
return retVal
}
}
@@ -34,6 +34,7 @@ public enum SlackError: ErrorType {
case BadRedirectURI
case BadTimeStamp
case CantArchiveGeneral
case CantDelete
case CantDeleteFile
case CantDeleteMessage
case CantInvite
@@ -75,6 +76,7 @@ public enum SlackError: ErrorType {
case MissingPostType
case NameTaken
case NoChannel
case NoComment
case NoItemSpecified
case NoReaction
case NoText
@@ -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":
@@ -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":
+73
View File
@@ -198,3 +198,76 @@ public struct DoNotDisturbStatus {
}
}
// MARK - Custom Team Profile
public struct CustomProfile {
internal(set) public var fields = [String: CustomProfileField]()
internal init?(profile: [String: AnyObject]?) {
if let eventFields = profile?["fields"] as? [AnyObject] {
for field in eventFields {
if let cpf = CustomProfileField(field: field as? [String: AnyObject]), id = cpf.id {
fields[id] = cpf
} else {
if let cpf = CustomProfileField(id: field as? String), id = cpf.id {
fields[id] = cpf
}
}
}
}
}
internal init?(customFields: [String: AnyObject]?) {
if let customFields = customFields {
for key in customFields.keys {
if let cpf = CustomProfileField(field: customFields[key] as? [String: AnyObject]) {
self.fields[key] = cpf
}
}
}
}
}
public struct CustomProfileField {
internal(set) public var id: String?
internal(set) public var alt: String?
internal(set) public var value: String?
internal(set) public var hidden: Bool?
internal(set) public var hint: String?
internal(set) public var label: String?
internal(set) public var options: String?
internal(set) public var ordering: Int?
internal(set) public var possibleValues: [String]?
internal(set) public var type: String?
internal init?(field: [String: AnyObject]?) {
id = field?["id"] as? String
alt = field?["alt"] as? String
value = field?["value"] as? String
hidden = field?["is_hidden"] as? Bool
hint = field?["hint"] as? String
label = field?["label"] as? String
options = field?["options"] as? String
ordering = field?["ordering"] as? Int
possibleValues = field?["possible_values"] as? [String]
type = field?["type"] as? String
}
internal init?(id: String?) {
self.id = id
}
internal mutating func updateProfileField(profile: CustomProfileField?) {
id = profile?.id != nil ? profile?.id : id
alt = profile?.alt != nil ? profile?.alt : alt
value = profile?.value != nil ? profile?.value : value
hidden = profile?.hidden != nil ? profile?.hidden : hidden
hint = profile?.hint != nil ? profile?.hint : hint
label = profile?.label != nil ? profile?.label : label
options = profile?.options != nil ? profile?.options : options
ordering = profile?.ordering != nil ? profile?.ordering : ordering
possibleValues = profile?.possibleValues != nil ? profile?.possibleValues : possibleValues
type = profile?.type != nil ? profile?.type : type
}
}
+3
View File
@@ -35,6 +35,7 @@ public struct User {
internal(set) public var image48: String?
internal(set) public var image72: String?
internal(set) public var image192: String?
internal(set) public var customProfile: CustomProfile?
internal init?(profile: [String: AnyObject]?) {
firstName = profile?["first_name"] as? String
@@ -48,9 +49,11 @@ public struct User {
image48 = profile?["image_48"] as? String
image72 = profile?["image_72"] as? String
image192 = profile?["image_192"] as? String
customProfile = CustomProfile(customFields: profile?["fields"] as? [String: AnyObject])
}
}
public let id: String?
internal(set) public var name: String?
internal(set) public var deleted: Bool?