Compare commits

...

60 Commits

Author SHA1 Message Date
Peter Zignego c6258c57a0 Merge pull request #43 from pvzig/spm-fix
SPM fix
2016-07-04 14:32:31 -04:00
Peter Zignego 45e075aca2 Update readme 2016-07-04 14:30:52 -04:00
Peter Zignego 5ebe40a389 Remove example 2016-07-04 13:15:32 -04:00
Peter Zignego 0a5a8f83f7 Remove example target 2016-07-04 13:14:03 -04:00
Peter Zignego 8f4a287ad0 Fix Package.swift 2016-07-04 13:13:28 -04:00
Peter Zignego e1bf5a160f Merge pull request #41 from hamin/make-some-channel-setters-public
Make the following Chanel setters public 'unreadCountDisplay', 'unrea…
2016-06-19 22:00:07 -04:00
Haris Amin c66a5fe0db Make the following Chanel setters public 'unreadCountDisplay', 'unread', and 'lastRead' 2016-06-19 17:48:31 -04:00
Peter Zignego f03908bcc7 Merge pull request #38 from natestedman/master
Remove curly quotes from Carthage instructions
2016-06-04 11:13:55 -04:00
Nate Stedman 591b0d9d55 Remove curly quotes from Carthage instructions. 2016-06-04 08:55:23 -04:00
Peter Zignego 6e83fb93d8 Merge pull request #36 from pvzig/feature/additional-footer-fields
1.1.1
2016-06-01 17:22:02 -04:00
Peter Zignego c1d202f433 Increment version 2016-06-01 17:21:23 -04:00
Peter Zignego c7db8ac578 Add new Attachment footer fields
https://api.slack.com/docs/attachments
2016-06-01 17:17:27 -04:00
Peter Zignego d2e430e5bf Merge pull request #35 from pvzig/feature/model-object-improvements
1.1.0
2016-05-22 22:33:12 -04:00
Peter Zignego ebe169bf2a Update podspec 2016-05-22 22:30:29 -04:00
Peter Zignego ca36653dc4 Update readme 2016-05-22 22:30:21 -04:00
Peter Zignego 94895edfac Bump version 2016-05-22 22:27:34 -04:00
Peter Zignego 3ea13ac6c4 Multiple target naming clean up 2016-05-22 22:27:25 -04:00
Peter Zignego e94adfc019 Make timestamp non-optional 2016-05-22 21:59:51 -04:00
Peter Zignego 1291323c5a Remove unused variable 2016-05-22 21:30:41 -04:00
Peter Zignego 4ceb452e6b File reordering 2016-05-22 21:30:27 -04:00
Peter Zignego 457504e786 Code quality 2016-05-22 18:38:00 -04:00
Peter Zignego 6d9a939575 Files only come as an ID via RTM (API change)
https://medium.com/slack-developer-blog/changes-to-file-events-in-the-real-time-messaging-api-5fa75c8c4d99#.1f3k421tz
2016-05-22 18:12:46 -04:00
Peter Zignego efc3847a20 Merge remote-tracking branch 'michallaskowski/remove-unneded-optionals' into feature/model-object-improvements
# Conflicts:
#	SlackKit/Sources/Message.swift
#	SlackKit/Sources/SlackWebAPI.swift
2016-05-21 15:28:28 -04:00
Peter Zignego 3ed139b503 Merge pull request #33 from hamin/fix-message-reactions-parsing
Fixes parsing of Reaction from Message response. It now properly adds…
2016-05-21 12:26:51 -04:00
Peter Zignego a4d9083f72 Merge pull request #34 from muratayusuke/feature/handle_http_error
Hanlde HTTP error
2016-05-21 12:25:18 -04:00
muratayusuke 67b2fa95b3 Hanlde HTTP error 2016-05-21 15:06:57 +09:00
Haris Amin e031f85447 Fixes parsing of Reaction from Message response. It now properly adds users to a parsed Reaction 2016-05-21 01:16:34 -04:00
Peter Zignego 0b1dc6068e Merge remote-tracking branch 'michallaskowski/slack-web-api-without-client' 2016-05-17 20:21:20 -04:00
Peter Zignego b027b5b779 Readme styling 2016-05-15 21:16:09 -04:00
Peter Zignego e507c85ca2 Update readme with note about slow carthage build times 2016-05-15 21:14:43 -04:00
michallaskowski 145dfccfae Remove unneeded Array extension 2016-05-15 23:16:55 +02:00
michallaskowski b4ca2bcc07 Delete optionals from EventDelegate where possible, fix reactionRemoved 2016-05-15 23:16:55 +02:00
michallaskowski 85b2d920ad Gardening Client.swift 2016-05-15 23:16:55 +02:00
michallaskowski 6415a113ed Remove failable initializers 2016-05-15 23:16:55 +02:00
Peter Zignego 3a2324b279 Fix readme 2016-05-15 16:29:40 -04:00
Peter Zignego d647a6b0c9 Update podspec 2016-05-15 16:27:47 -04:00
Peter Zignego 4230d7841c Merge pull request #32 from pvzig/feature/carthage-support
Feature/carthage support
2016-05-15 16:22:18 -04:00
Peter Zignego 8995a54d15 Fix project file and schemes after merge 2016-05-15 16:16:26 -04:00
Peter Zignego ba43a123ea Merge branch 'master' into feature/carthage-support
# Conflicts:
#	SlackKit.podspec
#	SlackKit.xcodeproj/project.pbxproj
#	SlackKit.xcodeproj/xcshareddata/xcschemes/SlackKit.xcscheme
2016-05-15 15:57:27 -04:00
Peter Zignego d00419dc18 Framework build details 2016-05-15 15:41:42 -04:00
Peter Zignego 15f3a30ba9 Framework build details 2016-05-15 15:39:47 -04:00
Peter Zignego befa53e3a9 Update ignore 2016-05-15 15:28:43 -04:00
Peter Zignego 35dfa840e8 Revert "Use submodule to handle core dependency"
This reverts commit edf56e1678.
2016-05-15 15:25:36 -04:00
Peter Zignego edf56e1678 Use submodule to handle core dependency 2016-05-15 15:21:47 -04:00
Peter Zignego 3eab323564 Ignore Cartfile.resolved 2016-05-15 15:04:34 -04:00
Peter Zignego 0b2ee1d10e Clean up project file 2016-05-15 14:51:15 -04:00
michallaskowski 5444948940 Do not hold Client instance in SlackWebAPI 2016-05-15 20:40:05 +02:00
Peter Zignego 13335b5de1 Add additional shared schemes 2016-05-15 14:09:21 -04:00
Peter Zignego c8d8e18db6 Move AttachmentColor enum 2016-05-14 20:11:14 -04:00
Peter Zignego c11321c116 File name comment fixes 2016-05-14 20:08:54 -04:00
Michal Laskowski 0041a118a7 Fix circular dependencies (#30)
* Remove EventDispatcher, EventHandler classes; extend Client

* Make delegates weak

* Remove unneded 'if let delegate = '

* Move files around
2016-05-14 19:51:08 -04:00
Michal Laskowski 6cb5280cf5 Add tvOS target to podspec 2016-05-11 10:12:15 -05:00
Peter Zignego 9cad043957 Add shared scheme 2016-05-06 15:02:39 -04:00
Peter Zignego fa15ed8751 Merge pull request #28 from hamin/add-larger-thumbs-to-file
Adding support for larger size File thumbs
2016-05-04 12:02:42 -04:00
Haris Amin 0c9c146d58 Adding support for larger size File thumbs 2016-05-04 01:58:43 -04:00
Peter Zignego 109a20fac2 Readme update 2016-04-27 23:18:19 -04:00
Peter Zignego a6cf1e3cb6 Revert "Delete and add Cartfile.resolved to ignore"
This reverts commit d3c16bed5f.
2016-04-27 23:17:41 -04:00
Peter Zignego d3c16bed5f Delete and add Cartfile.resolved to ignore 2016-04-27 23:07:55 -04:00
Peter Zignego 797d95ab75 Update readme 2016-04-27 22:54:30 -04:00
Nate Stedman efb7bc9458 Use Carthage for resolving dependencies. 2016-04-27 22:34:28 -04:00
41 changed files with 1827 additions and 2375 deletions
+5 -3
View File
@@ -16,8 +16,10 @@ DerivedData
*.hmap
*.ipa
*.xcuserstate
.build
# SwiftPM
Packages/
.build
# CocoaPods
#
@@ -26,10 +28,10 @@ Packages/
# http://guides.cocoapods.org/using/using-cocoapods.html#should-i-ignore-the-pods-directory-in-source-control
#
Pods/
SlackKit.xcworkspace
# Carthage
#
# Add this line if you want to avoid checking in source code from Carthage dependencies.
# Carthage/Checkouts
Carthage/Checkouts
Carthage/Build
+1
View File
@@ -0,0 +1 @@
github "daltoniam/Starscream" ~> 1.0
+1
View File
@@ -0,0 +1 @@
github "daltoniam/Starscream" "1.1.3"
@@ -1,58 +0,0 @@
{
"images" : [
{
"idiom" : "mac",
"size" : "16x16",
"scale" : "1x"
},
{
"idiom" : "mac",
"size" : "16x16",
"scale" : "2x"
},
{
"idiom" : "mac",
"size" : "32x32",
"scale" : "1x"
},
{
"idiom" : "mac",
"size" : "32x32",
"scale" : "2x"
},
{
"idiom" : "mac",
"size" : "128x128",
"scale" : "1x"
},
{
"idiom" : "mac",
"size" : "128x128",
"scale" : "2x"
},
{
"idiom" : "mac",
"size" : "256x256",
"scale" : "1x"
},
{
"idiom" : "mac",
"size" : "256x256",
"scale" : "2x"
},
{
"idiom" : "mac",
"size" : "512x512",
"scale" : "1x"
},
{
"idiom" : "mac",
"size" : "512x512",
"scale" : "2x"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}
-680
View File
@@ -1,680 +0,0 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="6233" systemVersion="14A329f" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES" customObjectInstantitationMethod="direct">
<dependencies>
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="6233"/>
</dependencies>
<objects>
<customObject id="-2" userLabel="File's Owner" customClass="NSApplication">
<connections>
<outlet property="delegate" destination="Voe-Tx-rLC" id="GzC-gU-4Uq"/>
</connections>
</customObject>
<customObject id="-1" userLabel="First Responder" customClass="FirstResponder"/>
<customObject id="-3" userLabel="Application" customClass="NSObject"/>
<customObject id="Voe-Tx-rLC" customClass="AppDelegate" customModuleProvider="target">
<connections>
<outlet property="window" destination="QvC-M9-y7g" id="gIp-Ho-8D9"/>
</connections>
</customObject>
<customObject id="YLy-65-1bz" customClass="NSFontManager"/>
<menu title="Main Menu" systemMenu="main" id="AYu-sK-qS6">
<items>
<menuItem title="OSX-Sample" id="1Xt-HY-uBw">
<modifierMask key="keyEquivalentModifierMask"/>
<menu key="submenu" title="OSX-Sample" systemMenu="apple" id="uQy-DD-JDr">
<items>
<menuItem title="About OSX-Sample" id="5kV-Vb-QxS">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="orderFrontStandardAboutPanel:" target="-1" id="Exp-CZ-Vem"/>
</connections>
</menuItem>
<menuItem isSeparatorItem="YES" id="VOq-y0-SEH"/>
<menuItem title="Preferences…" keyEquivalent="," id="BOF-NM-1cW"/>
<menuItem isSeparatorItem="YES" id="wFC-TO-SCJ"/>
<menuItem title="Services" id="NMo-om-nkz">
<modifierMask key="keyEquivalentModifierMask"/>
<menu key="submenu" title="Services" systemMenu="services" id="hz9-B4-Xy5"/>
</menuItem>
<menuItem isSeparatorItem="YES" id="4je-JR-u6R"/>
<menuItem title="Hide OSX-Sample" keyEquivalent="h" id="Olw-nP-bQN">
<connections>
<action selector="hide:" target="-1" id="PnN-Uc-m68"/>
</connections>
</menuItem>
<menuItem title="Hide Others" keyEquivalent="h" id="Vdr-fp-XzO">
<modifierMask key="keyEquivalentModifierMask" option="YES" command="YES"/>
<connections>
<action selector="hideOtherApplications:" target="-1" id="VT4-aY-XCT"/>
</connections>
</menuItem>
<menuItem title="Show All" id="Kd2-mp-pUS">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="unhideAllApplications:" target="-1" id="Dhg-Le-xox"/>
</connections>
</menuItem>
<menuItem isSeparatorItem="YES" id="kCx-OE-vgT"/>
<menuItem title="Quit OSX-Sample" keyEquivalent="q" id="4sb-4s-VLi">
<connections>
<action selector="terminate:" target="-1" id="Te7-pn-YzF"/>
</connections>
</menuItem>
</items>
</menu>
</menuItem>
<menuItem title="File" id="dMs-cI-mzQ">
<modifierMask key="keyEquivalentModifierMask"/>
<menu key="submenu" title="File" id="bib-Uj-vzu">
<items>
<menuItem title="New" keyEquivalent="n" id="Was-JA-tGl">
<connections>
<action selector="newDocument:" target="-1" id="4Si-XN-c54"/>
</connections>
</menuItem>
<menuItem title="Open…" keyEquivalent="o" id="IAo-SY-fd9">
<connections>
<action selector="openDocument:" target="-1" id="bVn-NM-KNZ"/>
</connections>
</menuItem>
<menuItem title="Open Recent" id="tXI-mr-wws">
<modifierMask key="keyEquivalentModifierMask"/>
<menu key="submenu" title="Open Recent" systemMenu="recentDocuments" id="oas-Oc-fiZ">
<items>
<menuItem title="Clear Menu" id="vNY-rz-j42">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="clearRecentDocuments:" target="-1" id="Daa-9d-B3U"/>
</connections>
</menuItem>
</items>
</menu>
</menuItem>
<menuItem isSeparatorItem="YES" id="m54-Is-iLE"/>
<menuItem title="Close" keyEquivalent="w" id="DVo-aG-piG">
<connections>
<action selector="performClose:" target="-1" id="HmO-Ls-i7Q"/>
</connections>
</menuItem>
<menuItem title="Save…" keyEquivalent="s" id="pxx-59-PXV">
<connections>
<action selector="saveDocument:" target="-1" id="teZ-XB-qJY"/>
</connections>
</menuItem>
<menuItem title="Save As…" keyEquivalent="S" id="Bw7-FT-i3A">
<connections>
<action selector="saveDocumentAs:" target="-1" id="mDf-zr-I0C"/>
</connections>
</menuItem>
<menuItem title="Revert to Saved" id="KaW-ft-85H">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="revertDocumentToSaved:" target="-1" id="iJ3-Pv-kwq"/>
</connections>
</menuItem>
<menuItem isSeparatorItem="YES" id="aJh-i4-bef"/>
<menuItem title="Page Setup…" keyEquivalent="P" id="qIS-W8-SiK">
<modifierMask key="keyEquivalentModifierMask" shift="YES" command="YES"/>
<connections>
<action selector="runPageLayout:" target="-1" id="Din-rz-gC5"/>
</connections>
</menuItem>
<menuItem title="Print…" keyEquivalent="p" id="aTl-1u-JFS">
<connections>
<action selector="print:" target="-1" id="qaZ-4w-aoO"/>
</connections>
</menuItem>
</items>
</menu>
</menuItem>
<menuItem title="Edit" id="5QF-Oa-p0T">
<modifierMask key="keyEquivalentModifierMask"/>
<menu key="submenu" title="Edit" id="W48-6f-4Dl">
<items>
<menuItem title="Undo" keyEquivalent="z" id="dRJ-4n-Yzg">
<connections>
<action selector="undo:" target="-1" id="M6e-cu-g7V"/>
</connections>
</menuItem>
<menuItem title="Redo" keyEquivalent="Z" id="6dh-zS-Vam">
<connections>
<action selector="redo:" target="-1" id="oIA-Rs-6OD"/>
</connections>
</menuItem>
<menuItem isSeparatorItem="YES" id="WRV-NI-Exz"/>
<menuItem title="Cut" keyEquivalent="x" id="uRl-iY-unG">
<connections>
<action selector="cut:" target="-1" id="YJe-68-I9s"/>
</connections>
</menuItem>
<menuItem title="Copy" keyEquivalent="c" id="x3v-GG-iWU">
<connections>
<action selector="copy:" target="-1" id="G1f-GL-Joy"/>
</connections>
</menuItem>
<menuItem title="Paste" keyEquivalent="v" id="gVA-U4-sdL">
<connections>
<action selector="paste:" target="-1" id="UvS-8e-Qdg"/>
</connections>
</menuItem>
<menuItem title="Paste and Match Style" keyEquivalent="V" id="WeT-3V-zwk">
<modifierMask key="keyEquivalentModifierMask" option="YES" command="YES"/>
<connections>
<action selector="pasteAsPlainText:" target="-1" id="cEh-KX-wJQ"/>
</connections>
</menuItem>
<menuItem title="Delete" id="pa3-QI-u2k">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="delete:" target="-1" id="0Mk-Ml-PaM"/>
</connections>
</menuItem>
<menuItem title="Select All" keyEquivalent="a" id="Ruw-6m-B2m">
<connections>
<action selector="selectAll:" target="-1" id="VNm-Mi-diN"/>
</connections>
</menuItem>
<menuItem isSeparatorItem="YES" id="uyl-h8-XO2"/>
<menuItem title="Find" id="4EN-yA-p0u">
<modifierMask key="keyEquivalentModifierMask"/>
<menu key="submenu" title="Find" id="1b7-l0-nxx">
<items>
<menuItem title="Find…" tag="1" keyEquivalent="f" id="Xz5-n4-O0W">
<connections>
<action selector="performFindPanelAction:" target="-1" id="cD7-Qs-BN4"/>
</connections>
</menuItem>
<menuItem title="Find and Replace…" tag="12" keyEquivalent="f" id="YEy-JH-Tfz">
<modifierMask key="keyEquivalentModifierMask" option="YES" command="YES"/>
<connections>
<action selector="performFindPanelAction:" target="-1" id="WD3-Gg-5AJ"/>
</connections>
</menuItem>
<menuItem title="Find Next" tag="2" keyEquivalent="g" id="q09-fT-Sye">
<connections>
<action selector="performFindPanelAction:" target="-1" id="NDo-RZ-v9R"/>
</connections>
</menuItem>
<menuItem title="Find Previous" tag="3" keyEquivalent="G" id="OwM-mh-QMV">
<connections>
<action selector="performFindPanelAction:" target="-1" id="HOh-sY-3ay"/>
</connections>
</menuItem>
<menuItem title="Use Selection for Find" tag="7" keyEquivalent="e" id="buJ-ug-pKt">
<connections>
<action selector="performFindPanelAction:" target="-1" id="U76-nv-p5D"/>
</connections>
</menuItem>
<menuItem title="Jump to Selection" keyEquivalent="j" id="S0p-oC-mLd">
<connections>
<action selector="centerSelectionInVisibleArea:" target="-1" id="IOG-6D-g5B"/>
</connections>
</menuItem>
</items>
</menu>
</menuItem>
<menuItem title="Spelling and Grammar" id="Dv1-io-Yv7">
<modifierMask key="keyEquivalentModifierMask"/>
<menu key="submenu" title="Spelling" id="3IN-sU-3Bg">
<items>
<menuItem title="Show Spelling and Grammar" keyEquivalent=":" id="HFo-cy-zxI">
<connections>
<action selector="showGuessPanel:" target="-1" id="vFj-Ks-hy3"/>
</connections>
</menuItem>
<menuItem title="Check Document Now" keyEquivalent=";" id="hz2-CU-CR7">
<connections>
<action selector="checkSpelling:" target="-1" id="fz7-VC-reM"/>
</connections>
</menuItem>
<menuItem isSeparatorItem="YES" id="bNw-od-mp5"/>
<menuItem title="Check Spelling While Typing" id="rbD-Rh-wIN">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="toggleContinuousSpellChecking:" target="-1" id="7w6-Qz-0kB"/>
</connections>
</menuItem>
<menuItem title="Check Grammar With Spelling" id="mK6-2p-4JG">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="toggleGrammarChecking:" target="-1" id="muD-Qn-j4w"/>
</connections>
</menuItem>
<menuItem title="Correct Spelling Automatically" id="78Y-hA-62v">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="toggleAutomaticSpellingCorrection:" target="-1" id="2lM-Qi-WAP"/>
</connections>
</menuItem>
</items>
</menu>
</menuItem>
<menuItem title="Substitutions" id="9ic-FL-obx">
<modifierMask key="keyEquivalentModifierMask"/>
<menu key="submenu" title="Substitutions" id="FeM-D8-WVr">
<items>
<menuItem title="Show Substitutions" id="z6F-FW-3nz">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="orderFrontSubstitutionsPanel:" target="-1" id="oku-mr-iSq"/>
</connections>
</menuItem>
<menuItem isSeparatorItem="YES" id="gPx-C9-uUO"/>
<menuItem title="Smart Copy/Paste" id="9yt-4B-nSM">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="toggleSmartInsertDelete:" target="-1" id="3IJ-Se-DZD"/>
</connections>
</menuItem>
<menuItem title="Smart Quotes" id="hQb-2v-fYv">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="toggleAutomaticQuoteSubstitution:" target="-1" id="ptq-xd-QOA"/>
</connections>
</menuItem>
<menuItem title="Smart Dashes" id="rgM-f4-ycn">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="toggleAutomaticDashSubstitution:" target="-1" id="oCt-pO-9gS"/>
</connections>
</menuItem>
<menuItem title="Smart Links" id="cwL-P1-jid">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="toggleAutomaticLinkDetection:" target="-1" id="Gip-E3-Fov"/>
</connections>
</menuItem>
<menuItem title="Data Detectors" id="tRr-pd-1PS">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="toggleAutomaticDataDetection:" target="-1" id="R1I-Nq-Kbl"/>
</connections>
</menuItem>
<menuItem title="Text Replacement" id="HFQ-gK-NFA">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="toggleAutomaticTextReplacement:" target="-1" id="DvP-Fe-Py6"/>
</connections>
</menuItem>
</items>
</menu>
</menuItem>
<menuItem title="Transformations" id="2oI-Rn-ZJC">
<modifierMask key="keyEquivalentModifierMask"/>
<menu key="submenu" title="Transformations" id="c8a-y6-VQd">
<items>
<menuItem title="Make Upper Case" id="vmV-6d-7jI">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="uppercaseWord:" target="-1" id="sPh-Tk-edu"/>
</connections>
</menuItem>
<menuItem title="Make Lower Case" id="d9M-CD-aMd">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="lowercaseWord:" target="-1" id="iUZ-b5-hil"/>
</connections>
</menuItem>
<menuItem title="Capitalize" id="UEZ-Bs-lqG">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="capitalizeWord:" target="-1" id="26H-TL-nsh"/>
</connections>
</menuItem>
</items>
</menu>
</menuItem>
<menuItem title="Speech" id="xrE-MZ-jX0">
<modifierMask key="keyEquivalentModifierMask"/>
<menu key="submenu" title="Speech" id="3rS-ZA-NoH">
<items>
<menuItem title="Start Speaking" id="Ynk-f8-cLZ">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="startSpeaking:" target="-1" id="654-Ng-kyl"/>
</connections>
</menuItem>
<menuItem title="Stop Speaking" id="Oyz-dy-DGm">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="stopSpeaking:" target="-1" id="dX8-6p-jy9"/>
</connections>
</menuItem>
</items>
</menu>
</menuItem>
</items>
</menu>
</menuItem>
<menuItem title="Format" id="jxT-CU-nIS">
<modifierMask key="keyEquivalentModifierMask"/>
<menu key="submenu" title="Format" id="GEO-Iw-cKr">
<items>
<menuItem title="Font" id="Gi5-1S-RQB">
<modifierMask key="keyEquivalentModifierMask"/>
<menu key="submenu" title="Font" systemMenu="font" id="aXa-aM-Jaq">
<items>
<menuItem title="Show Fonts" keyEquivalent="t" id="Q5e-8K-NDq">
<connections>
<action selector="orderFrontFontPanel:" target="YLy-65-1bz" id="WHr-nq-2xA"/>
</connections>
</menuItem>
<menuItem title="Bold" tag="2" keyEquivalent="b" id="GB9-OM-e27">
<connections>
<action selector="addFontTrait:" target="YLy-65-1bz" id="hqk-hr-sYV"/>
</connections>
</menuItem>
<menuItem title="Italic" tag="1" keyEquivalent="i" id="Vjx-xi-njq">
<connections>
<action selector="addFontTrait:" target="YLy-65-1bz" id="IHV-OB-c03"/>
</connections>
</menuItem>
<menuItem title="Underline" keyEquivalent="u" id="WRG-CD-K1S">
<connections>
<action selector="underline:" target="-1" id="FYS-2b-JAY"/>
</connections>
</menuItem>
<menuItem isSeparatorItem="YES" id="5gT-KC-WSO"/>
<menuItem title="Bigger" tag="3" keyEquivalent="+" id="Ptp-SP-VEL">
<connections>
<action selector="modifyFont:" target="YLy-65-1bz" id="Uc7-di-UnL"/>
</connections>
</menuItem>
<menuItem title="Smaller" tag="4" keyEquivalent="-" id="i1d-Er-qST">
<connections>
<action selector="modifyFont:" target="YLy-65-1bz" id="HcX-Lf-eNd"/>
</connections>
</menuItem>
<menuItem isSeparatorItem="YES" id="kx3-Dk-x3B"/>
<menuItem title="Kern" id="jBQ-r6-VK2">
<modifierMask key="keyEquivalentModifierMask"/>
<menu key="submenu" title="Kern" id="tlD-Oa-oAM">
<items>
<menuItem title="Use Default" id="GUa-eO-cwY">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="useStandardKerning:" target="-1" id="6dk-9l-Ckg"/>
</connections>
</menuItem>
<menuItem title="Use None" id="cDB-IK-hbR">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="turnOffKerning:" target="-1" id="U8a-gz-Maa"/>
</connections>
</menuItem>
<menuItem title="Tighten" id="46P-cB-AYj">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="tightenKerning:" target="-1" id="hr7-Nz-8ro"/>
</connections>
</menuItem>
<menuItem title="Loosen" id="ogc-rX-tC1">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="loosenKerning:" target="-1" id="8i4-f9-FKE"/>
</connections>
</menuItem>
</items>
</menu>
</menuItem>
<menuItem title="Ligatures" id="o6e-r0-MWq">
<modifierMask key="keyEquivalentModifierMask"/>
<menu key="submenu" title="Ligatures" id="w0m-vy-SC9">
<items>
<menuItem title="Use Default" id="agt-UL-0e3">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="useStandardLigatures:" target="-1" id="7uR-wd-Dx6"/>
</connections>
</menuItem>
<menuItem title="Use None" id="J7y-lM-qPV">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="turnOffLigatures:" target="-1" id="iX2-gA-Ilz"/>
</connections>
</menuItem>
<menuItem title="Use All" id="xQD-1f-W4t">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="useAllLigatures:" target="-1" id="KcB-kA-TuK"/>
</connections>
</menuItem>
</items>
</menu>
</menuItem>
<menuItem title="Baseline" id="OaQ-X3-Vso">
<modifierMask key="keyEquivalentModifierMask"/>
<menu key="submenu" title="Baseline" id="ijk-EB-dga">
<items>
<menuItem title="Use Default" id="3Om-Ey-2VK">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="unscript:" target="-1" id="0vZ-95-Ywn"/>
</connections>
</menuItem>
<menuItem title="Superscript" id="Rqc-34-cIF">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="superscript:" target="-1" id="3qV-fo-wpU"/>
</connections>
</menuItem>
<menuItem title="Subscript" id="I0S-gh-46l">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="subscript:" target="-1" id="Q6W-4W-IGz"/>
</connections>
</menuItem>
<menuItem title="Raise" id="2h7-ER-AoG">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="raiseBaseline:" target="-1" id="4sk-31-7Q9"/>
</connections>
</menuItem>
<menuItem title="Lower" id="1tx-W0-xDw">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="lowerBaseline:" target="-1" id="OF1-bc-KW4"/>
</connections>
</menuItem>
</items>
</menu>
</menuItem>
<menuItem isSeparatorItem="YES" id="Ndw-q3-faq"/>
<menuItem title="Show Colors" keyEquivalent="C" id="bgn-CT-cEk">
<connections>
<action selector="orderFrontColorPanel:" target="-1" id="mSX-Xz-DV3"/>
</connections>
</menuItem>
<menuItem isSeparatorItem="YES" id="iMs-zA-UFJ"/>
<menuItem title="Copy Style" keyEquivalent="c" id="5Vv-lz-BsD">
<modifierMask key="keyEquivalentModifierMask" option="YES" command="YES"/>
<connections>
<action selector="copyFont:" target="-1" id="GJO-xA-L4q"/>
</connections>
</menuItem>
<menuItem title="Paste Style" keyEquivalent="v" id="vKC-jM-MkH">
<modifierMask key="keyEquivalentModifierMask" option="YES" command="YES"/>
<connections>
<action selector="pasteFont:" target="-1" id="JfD-CL-leO"/>
</connections>
</menuItem>
</items>
</menu>
</menuItem>
<menuItem title="Text" id="Fal-I4-PZk">
<modifierMask key="keyEquivalentModifierMask"/>
<menu key="submenu" title="Text" id="d9c-me-L2H">
<items>
<menuItem title="Align Left" keyEquivalent="{" id="ZM1-6Q-yy1">
<connections>
<action selector="alignLeft:" target="-1" id="zUv-R1-uAa"/>
</connections>
</menuItem>
<menuItem title="Center" keyEquivalent="|" id="VIY-Ag-zcb">
<connections>
<action selector="alignCenter:" target="-1" id="spX-mk-kcS"/>
</connections>
</menuItem>
<menuItem title="Justify" id="J5U-5w-g23">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="alignJustified:" target="-1" id="ljL-7U-jND"/>
</connections>
</menuItem>
<menuItem title="Align Right" keyEquivalent="}" id="wb2-vD-lq4">
<connections>
<action selector="alignRight:" target="-1" id="r48-bG-YeY"/>
</connections>
</menuItem>
<menuItem isSeparatorItem="YES" id="4s2-GY-VfK"/>
<menuItem title="Writing Direction" id="H1b-Si-o9J">
<modifierMask key="keyEquivalentModifierMask"/>
<menu key="submenu" title="Writing Direction" id="8mr-sm-Yjd">
<items>
<menuItem title="Paragraph" enabled="NO" id="ZvO-Gk-QUH">
<modifierMask key="keyEquivalentModifierMask"/>
</menuItem>
<menuItem id="YGs-j5-SAR">
<string key="title"> Default</string>
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="makeBaseWritingDirectionNatural:" target="-1" id="qtV-5e-UBP"/>
</connections>
</menuItem>
<menuItem id="Lbh-J2-qVU">
<string key="title"> Left to Right</string>
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="makeBaseWritingDirectionLeftToRight:" target="-1" id="S0X-9S-QSf"/>
</connections>
</menuItem>
<menuItem id="jFq-tB-4Kx">
<string key="title"> Right to Left</string>
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="makeBaseWritingDirectionRightToLeft:" target="-1" id="5fk-qB-AqJ"/>
</connections>
</menuItem>
<menuItem isSeparatorItem="YES" id="swp-gr-a21"/>
<menuItem title="Selection" enabled="NO" id="cqv-fj-IhA">
<modifierMask key="keyEquivalentModifierMask"/>
</menuItem>
<menuItem id="Nop-cj-93Q">
<string key="title"> Default</string>
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="makeTextWritingDirectionNatural:" target="-1" id="lPI-Se-ZHp"/>
</connections>
</menuItem>
<menuItem id="BgM-ve-c93">
<string key="title"> Left to Right</string>
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="makeTextWritingDirectionLeftToRight:" target="-1" id="caW-Bv-w94"/>
</connections>
</menuItem>
<menuItem id="RB4-Sm-HuC">
<string key="title"> Right to Left</string>
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="makeTextWritingDirectionRightToLeft:" target="-1" id="EXD-6r-ZUu"/>
</connections>
</menuItem>
</items>
</menu>
</menuItem>
<menuItem isSeparatorItem="YES" id="fKy-g9-1gm"/>
<menuItem title="Show Ruler" id="vLm-3I-IUL">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="toggleRuler:" target="-1" id="FOx-HJ-KwY"/>
</connections>
</menuItem>
<menuItem title="Copy Ruler" keyEquivalent="c" id="MkV-Pr-PK5">
<modifierMask key="keyEquivalentModifierMask" control="YES" command="YES"/>
<connections>
<action selector="copyRuler:" target="-1" id="71i-fW-3W2"/>
</connections>
</menuItem>
<menuItem title="Paste Ruler" keyEquivalent="v" id="LVM-kO-fVI">
<modifierMask key="keyEquivalentModifierMask" control="YES" command="YES"/>
<connections>
<action selector="pasteRuler:" target="-1" id="cSh-wd-qM2"/>
</connections>
</menuItem>
</items>
</menu>
</menuItem>
</items>
</menu>
</menuItem>
<menuItem title="View" id="H8h-7b-M4v">
<modifierMask key="keyEquivalentModifierMask"/>
<menu key="submenu" title="View" id="HyV-fh-RgO">
<items>
<menuItem title="Show Toolbar" keyEquivalent="t" id="snW-S8-Cw5">
<modifierMask key="keyEquivalentModifierMask" option="YES" command="YES"/>
<connections>
<action selector="toggleToolbarShown:" target="-1" id="BXY-wc-z0C"/>
</connections>
</menuItem>
<menuItem title="Customize Toolbar…" id="1UK-8n-QPP">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="runToolbarCustomizationPalette:" target="-1" id="pQI-g3-MTW"/>
</connections>
</menuItem>
</items>
</menu>
</menuItem>
<menuItem title="Window" id="aUF-d1-5bR">
<modifierMask key="keyEquivalentModifierMask"/>
<menu key="submenu" title="Window" systemMenu="window" id="Td7-aD-5lo">
<items>
<menuItem title="Minimize" keyEquivalent="m" id="OY7-WF-poV">
<connections>
<action selector="performMiniaturize:" target="-1" id="VwT-WD-YPe"/>
</connections>
</menuItem>
<menuItem title="Zoom" id="R4o-n2-Eq4">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="performZoom:" target="-1" id="DIl-cC-cCs"/>
</connections>
</menuItem>
<menuItem isSeparatorItem="YES" id="eu3-7i-yIM"/>
<menuItem title="Bring All to Front" id="LE2-aR-0XJ">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="arrangeInFront:" target="-1" id="DRN-fu-gQh"/>
</connections>
</menuItem>
</items>
</menu>
</menuItem>
<menuItem title="Help" id="wpr-3q-Mcd">
<modifierMask key="keyEquivalentModifierMask"/>
<menu key="submenu" title="Help" systemMenu="help" id="F2S-fz-NVQ">
<items>
<menuItem title="OSX-Sample Help" keyEquivalent="?" id="FKE-Sm-Kum">
<connections>
<action selector="showHelp:" target="-1" id="y7X-2Q-9no"/>
</connections>
</menuItem>
</items>
</menu>
</menuItem>
</items>
</menu>
<window title="OSX-Sample" allowsToolTipsWhenApplicationIsInactive="NO" autorecalculatesKeyViewLoop="NO" releasedWhenClosed="NO" animationBehavior="default" id="QvC-M9-y7g">
<windowStyleMask key="styleMask" titled="YES" closable="YES" miniaturizable="YES" resizable="YES"/>
<windowPositionMask key="initialPositionMask" leftStrut="YES" rightStrut="YES" topStrut="YES" bottomStrut="YES"/>
<rect key="contentRect" x="335" y="390" width="480" height="360"/>
<rect key="screenRect" x="0.0" y="0.0" width="1920" height="1177"/>
<view key="contentView" id="EiT-Mj-1SZ">
<rect key="frame" x="0.0" y="0.0" width="480" height="360"/>
<autoresizingMask key="autoresizingMask"/>
</view>
</window>
</objects>
</document>
-160
View File
@@ -1,160 +0,0 @@
//
// Leaderboard.swift
// OSX-Sample
//
// Copyright © 2016 Peter Zignego. All rights reserved.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
import SlackKit
import Foundation
class Leaderboard: MessageEventsDelegate {
var leaderboard: [String: Int] = [String: Int]()
let atSet = NSCharacterSet(charactersInString: "@")
let client: Client
init(token: String) {
client = Client(apiToken: token)
client.messageEventsDelegate = self
}
enum Command: String {
case Leaderboard = "leaderboard"
}
enum Trigger: String {
case PlusPlus = "++"
case MinusMinus = "--"
}
// MARK: MessageEventsDelegate
func messageReceived(message: Message) {
listen(message)
}
func messageSent(message: Message){}
func messageChanged(message: Message){}
func messageDeleted(message: Message?){}
// MARK: Leaderboard Internal Logic
private func listen(message: Message) {
if let id = client.authenticatedUser?.id, text = message.text {
if text.lowercaseString.containsString(Command.Leaderboard.rawValue) && text.containsString(id) == true {
handleCommand(.Leaderboard, channel: message.channel)
}
}
if message.text?.containsString(Trigger.PlusPlus.rawValue) == true {
handleMessageWithTrigger(message, trigger: .PlusPlus)
}
if message.text?.containsString(Trigger.MinusMinus.rawValue) == true {
handleMessageWithTrigger(message, trigger: .MinusMinus)
}
}
private func handleMessageWithTrigger(message: Message, trigger: Trigger) {
if let text = message.text,
end = text.rangeOfString(trigger.rawValue)?.startIndex.predecessor(),
start = text.rangeOfCharacterFromSet(atSet, options: .BackwardsSearch, range: text.startIndex..<end)?.startIndex {
let string = text.substringWithRange(start...end)
let users = client.users.values.filter{$0.id == self.userID(string)}
if users.count > 0 {
let idString = userID(string)
initalizationForValue(&leaderboard, value: idString)
scoringForValue(&leaderboard, value: idString, trigger: trigger)
} else {
initalizationForValue(&leaderboard, value: string)
scoringForValue(&leaderboard, value: string, trigger: trigger)
}
}
}
private func handleCommand(command: Command, channel:String?) {
switch command {
case .Leaderboard:
if let id = channel {
client.webAPI.sendMessage(id, text: "", linkNames: true, attachments: [constructLeaderboardAttachment()], success: {(response) in
}, failure: { (error) in
print("Leaderboard failed to post due to error:\(error)")
})
}
}
}
private func initalizationForValue(inout dictionary: [String: Int], value: String) {
if dictionary[value] == nil {
dictionary[value] = 0
}
}
private func scoringForValue(inout dictionary: [String: Int], value: String, trigger: Trigger) {
switch trigger {
case .PlusPlus:
dictionary[value]?+=1
case .MinusMinus:
dictionary[value]?-=1
}
}
// MARK: Leaderboard Interface
private func constructLeaderboardAttachment() -> Attachment? {
let 💯 = AttachmentField(title: "💯", value: swapIDsForNames(topItems(&leaderboard)), short: true)
let 💩 = AttachmentField(title: "💩", value: swapIDsForNames(bottomItems(&leaderboard)), short: true)
return Attachment(fallback: "Leaderboard", title: "Leaderboard", colorHex: AttachmentColor.Good.rawValue, text: "", fields: [💯, 💩])
}
private func topItems(inout dictionary: [String: Int]) -> String {
let sortedKeys = Array(dictionary.keys).sort({dictionary[$0] > dictionary[$1]}).filter({dictionary[$0] > 0})
let sortedValues = Array(dictionary.values).sort({$0 > $1}).filter({$0 > 0})
return leaderboardString(sortedKeys, values: sortedValues)
}
private func bottomItems(inout dictionary: [String: Int]) -> String {
let sortedKeys = Array(dictionary.keys).sort({dictionary[$0] < dictionary[$1]}).filter({dictionary[$0] < 0})
let sortedValues = Array(dictionary.values).sort({$0 < $1}).filter({$0 < 0})
return leaderboardString(sortedKeys, values: sortedValues)
}
private func leaderboardString(keys: [String], values: [Int]) -> String {
var returnValue = ""
for i in 0..<values.count {
returnValue += keys[i] + " (" + "\(values[i])" + ")\n"
}
return returnValue
}
// MARK: - Utilities
private func swapIDsForNames(string: String) -> String {
var returnString = string
for key in client.users.keys {
if let name = client.users[key]?.name {
returnString = returnString.stringByReplacingOccurrencesOfString(key, withString: "@"+name, options: NSStringCompareOptions.LiteralSearch, range: returnString.startIndex..<returnString.endIndex)
}
}
return returnString
}
private func userID(string: String) -> String {
return string.stringByTrimmingCharactersInSet(NSCharacterSet.alphanumericCharacterSet().invertedSet)
}
}
+1 -1
View File
@@ -27,7 +27,7 @@ let package = Package(
name: "SlackKit",
targets: [],
dependencies: [
.Package(url: "https://github.com/pvzig/Starscream.git",
.Package(url: "https://github.com/daltoniam/Starscream",
majorVersion: 1),
]
)
+8
View File
@@ -4,4 +4,12 @@ use_frameworks!
target 'SlackKit' do
pod 'Starscream'
end
target 'SlackKit_iOS' do
pod 'Starscream'
end
target 'SlackKit_tvOS' do
pod 'Starscream'
end
+5 -3
View File
@@ -1,10 +1,12 @@
PODS:
- Starscream (1.1.2)
- Starscream (1.1.3)
DEPENDENCIES:
- Starscream
SPEC CHECKSUMS:
Starscream: 58a12fd35a3cb6aaa105716c2d42765f7c1c732f
Starscream: d662732354b40dd19ed1ece3e3c44c80b536b83c
COCOAPODS: 0.39.0
PODFILE CHECKSUM: d22778f772dbbded8b17fbebf5fa1c879d785aee
COCOAPODS: 1.0.0
+34 -23
View File
@@ -1,9 +1,39 @@
![SlackKit](https://cloud.githubusercontent.com/assets/8311605/10260893/5ec60f96-694e-11e5-91fd-da6845942201.png)
##iOS/OS X Slack Client Library
##iOS, OS X, and tvOS Slack Client Library
###Description
This is a Slack client library for iOS and OS X written in Swift. It's intended to expose all of the functionality of Slack's [Real Time Messaging API](https://api.slack.com/rtm) as well as the [web APIs](https://api.slack.com/web) that are accessible by [bot users](https://api.slack.com/bot-users).
This is a Slack client library for OS X, iOS, and tvOS written in Swift. It's intended to expose all of the functionality of Slack's [Real Time Messaging API](https://api.slack.com/rtm) as well as the [web APIs](https://api.slack.com/web) that are accessible by [bot users](https://api.slack.com/bot-users).
####Building the SlackKit Framework
To build the SlackKit project directly, first build the dependencies using Carthage or CocoaPods. To use the framework in your application, install it in one of the following ways:
###Installation
####CocoaPods
Add the pod to your podfile:
```
pod 'SlackKit'
```
and run
```
pod install
```
####Carthage
Add SlackKit to your Cartfile:
```
github "pvzig/SlackKit" ~> 1.0
```
and run
```
carthage bootstrap
```
**Note:** SlackKit currently takes a _long_ time for the compiler to compile with optimizations turned on. I'm currently exploring a potential fix for this issue. In the meantime, you may want to skip the waiting and build it in the debug configuration instead:
```
carthage bootstrap --configuration "Debug"
```
Drag the built `SlackKit.framework` into your Xcode project.
####Swift Package Manager
Add SlackKit to your Package.swift
@@ -17,17 +47,7 @@ let package = Package(
)
```
Run `swift-build` on your applications main directory.
####CocoaPods
Add the pod to your podfile:
```
pod 'SlackKit'
```
and run
```
pod install
```
Run `swift build` on your applications main directory.
To use the library in your project import it:
```
@@ -219,16 +239,7 @@ func subteamSelfRemoved(subteamID: String)
```
###Examples
####Leaderboard
Included in the OSX-Sample is an example application of a bot you might make using SlackKit. Its a basic leaderboard scoring bot, in the spirit of [PlusPlus](https://plusplus.chat).
To configure it, enter your bots API token in `AppDelegate.swift` for the Leaderboard bot:
```swift
let learderboard = Leaderboard(token: "SLACK_AUTH_TOKEN")
```
It adds a point for every `@thing++`, subtracts a point for every `@thing--`, and shows a leaderboard when asked `@botname leaderboard`.
[Check out example applications here.](https://github.com/pvzig/SlackKit-examples)
###Get In Touch
[@pvzig](https://twitter.com/pvzig)
-30
View File
@@ -1,30 +0,0 @@
//
// SlackKit.h
//
// Copyright © 2016 Peter Zignego. All rights reserved.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
#import <Foundation/Foundation.h>
//! Project version number for SlackKit.
FOUNDATION_EXPORT double SlackKitVersionNumber;
//! Project version string for SlackKit.
FOUNDATION_EXPORT const unsigned char SlackKitVersionString[];
+5 -4
View File
@@ -1,17 +1,18 @@
Pod::Spec.new do |s|
s.name = "SlackKit"
s.version = "1.0.2"
s.summary = "a Slack client library for iOS and OS X written in Swift"
s.version = "1.1.1"
s.summary = "a Slack client library for OS X, iOS, and tvOS written in Swift"
s.homepage = "https://github.com/pvzig/SlackKit"
s.license = 'MIT'
s.author = { "Peter Zignego" => "peter@launchsoft.co" }
s.source = { :git => "https://github.com/pvzig/SlackKit.git", :tag => s.version.to_s }
s.social_media_url = 'https://twitter.com/pvzig'
s.ios.deployment_target = '8.0'
s.osx.deployment_target = '10.9'
s.osx.deployment_target = '10.10'
s.tvos.deployment_target = '9.0'
s.requires_arc = true
s.source_files = 'SlackKit/Sources/*.swift'
s.frameworks = 'Foundation'
s.dependency 'Starscream', '~> 1.1.2'
s.dependency 'Starscream', '~> 1.1.3'
end
+361 -186
View File
@@ -8,19 +8,57 @@
/* Begin PBXBuildFile section */
2601D61B1C7646B80012BF22 /* SlackWebAPIErrorDispatcher.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2601D61A1C7646B80012BF22 /* SlackWebAPIErrorDispatcher.swift */; };
2601D6271C7688610012BF22 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2601D6261C7688610012BF22 /* AppDelegate.swift */; };
2601D6291C7688610012BF22 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 2601D6281C7688610012BF22 /* Assets.xcassets */; };
2601D62C1C7688610012BF22 /* MainMenu.xib in Resources */ = {isa = PBXBuildFile; fileRef = 2601D62A1C7688610012BF22 /* MainMenu.xib */; };
260EC2331C4DC61D0093B253 /* ClientExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 260EC2301C4DC61D0093B253 /* ClientExtensions.swift */; };
260EC2331C4DC61D0093B253 /* Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 260EC2301C4DC61D0093B253 /* Extensions.swift */; };
260EC2341C4DC61D0093B253 /* NetworkInterface.swift in Sources */ = {isa = PBXBuildFile; fileRef = 260EC2311C4DC61D0093B253 /* NetworkInterface.swift */; };
260EC2351C4DC61D0093B253 /* SlackWebAPI.swift in Sources */ = {isa = PBXBuildFile; fileRef = 260EC2321C4DC61D0093B253 /* SlackWebAPI.swift */; };
263993901CE90C87004A6E93 /* SlackKit.h in Headers */ = {isa = PBXBuildFile; fileRef = 2661A6A41BBF62FF0026F67B /* SlackKit.h */; settings = {ATTRIBUTES = (Public, ); }; };
263993971CE90EE0004A6E93 /* Client+Utilities.swift in Sources */ = {isa = PBXBuildFile; fileRef = C16C98791CE7D3DD00692776 /* Client+Utilities.swift */; };
263993981CE90EE0004A6E93 /* Channel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 26BBA1881C398E3C00BF7225 /* Channel.swift */; };
263993991CE90EE0004A6E93 /* User.swift in Sources */ = {isa = PBXBuildFile; fileRef = 26BBA1921C398E3C00BF7225 /* User.swift */; };
2639939A1CE90EE0004A6E93 /* Types.swift in Sources */ = {isa = PBXBuildFile; fileRef = 26BBA1911C398E3C00BF7225 /* Types.swift */; };
2639939B1CE90EE0004A6E93 /* Client.swift in Sources */ = {isa = PBXBuildFile; fileRef = 26BBA1891C398E3C00BF7225 /* Client.swift */; };
2639939C1CE90EE0004A6E93 /* Event.swift in Sources */ = {isa = PBXBuildFile; fileRef = 26BBA18A1C398E3C00BF7225 /* Event.swift */; };
2639939D1CE90EE0004A6E93 /* Bot.swift in Sources */ = {isa = PBXBuildFile; fileRef = 26BBA1871C398E3C00BF7225 /* Bot.swift */; };
2639939E1CE90EE0004A6E93 /* Client+EventDispatching.swift in Sources */ = {isa = PBXBuildFile; fileRef = C1A85FF71CE3BCEF00756C40 /* Client+EventDispatching.swift */; };
2639939F1CE90EE0004A6E93 /* File.swift in Sources */ = {isa = PBXBuildFile; fileRef = 26BBA18E1C398E3C00BF7225 /* File.swift */; };
263993A01CE90EE0004A6E93 /* SlackWebAPI.swift in Sources */ = {isa = PBXBuildFile; fileRef = 260EC2321C4DC61D0093B253 /* SlackWebAPI.swift */; };
263993A11CE90EE0004A6E93 /* Attachment.swift in Sources */ = {isa = PBXBuildFile; fileRef = 26DF40341C7A0FA300E19241 /* Attachment.swift */; };
263993A21CE90EE0004A6E93 /* Message.swift in Sources */ = {isa = PBXBuildFile; fileRef = 26BBA18F1C398E3C00BF7225 /* Message.swift */; };
263993A31CE90EE0004A6E93 /* Team.swift in Sources */ = {isa = PBXBuildFile; fileRef = 26BBA1901C398E3C00BF7225 /* Team.swift */; };
263993A41CE90EE0004A6E93 /* Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 260EC2301C4DC61D0093B253 /* Extensions.swift */; };
263993A51CE90EE0004A6E93 /* UserGroup.swift in Sources */ = {isa = PBXBuildFile; fileRef = 26BBA1931C398E3C00BF7225 /* UserGroup.swift */; };
263993A61CE90EE0004A6E93 /* SlackWebAPIErrorDispatcher.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2601D61A1C7646B80012BF22 /* SlackWebAPIErrorDispatcher.swift */; };
263993A71CE90EE0004A6E93 /* Client+EventHandling.swift in Sources */ = {isa = PBXBuildFile; fileRef = C1A85FF81CE3BCEF00756C40 /* Client+EventHandling.swift */; };
263993A81CE90EE0004A6E93 /* NetworkInterface.swift in Sources */ = {isa = PBXBuildFile; fileRef = 260EC2311C4DC61D0093B253 /* NetworkInterface.swift */; };
263993A91CE90EE0004A6E93 /* EventDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 26BBA18B1C398E3C00BF7225 /* EventDelegate.swift */; };
263993AB1CE90EE0004A6E93 /* Starscream.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 4307A07F1CC6D0910011D5DE /* Starscream.framework */; };
263993AD1CE90EE0004A6E93 /* SlackKit.h in Headers */ = {isa = PBXBuildFile; fileRef = 2661A6A41BBF62FF0026F67B /* SlackKit.h */; settings = {ATTRIBUTES = (Public, ); }; };
263993B61CE90EED004A6E93 /* Client+Utilities.swift in Sources */ = {isa = PBXBuildFile; fileRef = C16C98791CE7D3DD00692776 /* Client+Utilities.swift */; };
263993B71CE90EED004A6E93 /* Channel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 26BBA1881C398E3C00BF7225 /* Channel.swift */; };
263993B81CE90EED004A6E93 /* User.swift in Sources */ = {isa = PBXBuildFile; fileRef = 26BBA1921C398E3C00BF7225 /* User.swift */; };
263993B91CE90EED004A6E93 /* Types.swift in Sources */ = {isa = PBXBuildFile; fileRef = 26BBA1911C398E3C00BF7225 /* Types.swift */; };
263993BA1CE90EED004A6E93 /* Client.swift in Sources */ = {isa = PBXBuildFile; fileRef = 26BBA1891C398E3C00BF7225 /* Client.swift */; };
263993BB1CE90EED004A6E93 /* Event.swift in Sources */ = {isa = PBXBuildFile; fileRef = 26BBA18A1C398E3C00BF7225 /* Event.swift */; };
263993BC1CE90EED004A6E93 /* Bot.swift in Sources */ = {isa = PBXBuildFile; fileRef = 26BBA1871C398E3C00BF7225 /* Bot.swift */; };
263993BD1CE90EED004A6E93 /* Client+EventDispatching.swift in Sources */ = {isa = PBXBuildFile; fileRef = C1A85FF71CE3BCEF00756C40 /* Client+EventDispatching.swift */; };
263993BE1CE90EED004A6E93 /* File.swift in Sources */ = {isa = PBXBuildFile; fileRef = 26BBA18E1C398E3C00BF7225 /* File.swift */; };
263993BF1CE90EED004A6E93 /* SlackWebAPI.swift in Sources */ = {isa = PBXBuildFile; fileRef = 260EC2321C4DC61D0093B253 /* SlackWebAPI.swift */; };
263993C01CE90EED004A6E93 /* Attachment.swift in Sources */ = {isa = PBXBuildFile; fileRef = 26DF40341C7A0FA300E19241 /* Attachment.swift */; };
263993C11CE90EED004A6E93 /* Message.swift in Sources */ = {isa = PBXBuildFile; fileRef = 26BBA18F1C398E3C00BF7225 /* Message.swift */; };
263993C21CE90EED004A6E93 /* Team.swift in Sources */ = {isa = PBXBuildFile; fileRef = 26BBA1901C398E3C00BF7225 /* Team.swift */; };
263993C31CE90EED004A6E93 /* Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 260EC2301C4DC61D0093B253 /* Extensions.swift */; };
263993C41CE90EED004A6E93 /* UserGroup.swift in Sources */ = {isa = PBXBuildFile; fileRef = 26BBA1931C398E3C00BF7225 /* UserGroup.swift */; };
263993C51CE90EED004A6E93 /* SlackWebAPIErrorDispatcher.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2601D61A1C7646B80012BF22 /* SlackWebAPIErrorDispatcher.swift */; };
263993C61CE90EED004A6E93 /* Client+EventHandling.swift in Sources */ = {isa = PBXBuildFile; fileRef = C1A85FF81CE3BCEF00756C40 /* Client+EventHandling.swift */; };
263993C71CE90EED004A6E93 /* NetworkInterface.swift in Sources */ = {isa = PBXBuildFile; fileRef = 260EC2311C4DC61D0093B253 /* NetworkInterface.swift */; };
263993C81CE90EED004A6E93 /* EventDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 26BBA18B1C398E3C00BF7225 /* EventDelegate.swift */; };
263993CA1CE90EED004A6E93 /* Starscream.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 4307A07F1CC6D0910011D5DE /* Starscream.framework */; };
263993CC1CE90EED004A6E93 /* SlackKit.h in Headers */ = {isa = PBXBuildFile; fileRef = 2661A6A41BBF62FF0026F67B /* SlackKit.h */; settings = {ATTRIBUTES = (Public, ); }; };
26BBA1941C398E3C00BF7225 /* Bot.swift in Sources */ = {isa = PBXBuildFile; fileRef = 26BBA1871C398E3C00BF7225 /* Bot.swift */; };
26BBA1951C398E3C00BF7225 /* Channel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 26BBA1881C398E3C00BF7225 /* Channel.swift */; };
26BBA1961C398E3C00BF7225 /* Client.swift in Sources */ = {isa = PBXBuildFile; fileRef = 26BBA1891C398E3C00BF7225 /* Client.swift */; };
26BBA1971C398E3C00BF7225 /* Event.swift in Sources */ = {isa = PBXBuildFile; fileRef = 26BBA18A1C398E3C00BF7225 /* Event.swift */; };
26BBA1981C398E3C00BF7225 /* EventDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 26BBA18B1C398E3C00BF7225 /* EventDelegate.swift */; };
26BBA1991C398E3C00BF7225 /* EventDispatcher.swift in Sources */ = {isa = PBXBuildFile; fileRef = 26BBA18C1C398E3C00BF7225 /* EventDispatcher.swift */; };
26BBA19A1C398E3C00BF7225 /* EventHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = 26BBA18D1C398E3C00BF7225 /* EventHandler.swift */; };
26BBA19B1C398E3C00BF7225 /* File.swift in Sources */ = {isa = PBXBuildFile; fileRef = 26BBA18E1C398E3C00BF7225 /* File.swift */; };
26BBA19C1C398E3C00BF7225 /* Message.swift in Sources */ = {isa = PBXBuildFile; fileRef = 26BBA18F1C398E3C00BF7225 /* Message.swift */; };
26BBA19D1C398E3C00BF7225 /* Team.swift in Sources */ = {isa = PBXBuildFile; fileRef = 26BBA1901C398E3C00BF7225 /* Team.swift */; };
@@ -28,30 +66,29 @@
26BBA19F1C398E3C00BF7225 /* User.swift in Sources */ = {isa = PBXBuildFile; fileRef = 26BBA1921C398E3C00BF7225 /* User.swift */; };
26BBA1A01C398E3C00BF7225 /* UserGroup.swift in Sources */ = {isa = PBXBuildFile; fileRef = 26BBA1931C398E3C00BF7225 /* UserGroup.swift */; };
26DF40351C7A0FA300E19241 /* Attachment.swift in Sources */ = {isa = PBXBuildFile; fileRef = 26DF40341C7A0FA300E19241 /* Attachment.swift */; };
26F4BAC31C9DEBD1000910BA /* Leaderboard.swift in Sources */ = {isa = PBXBuildFile; fileRef = 26F4BAC21C9DEBD1000910BA /* Leaderboard.swift */; };
FFE3AC870D1C42EF276CCA2D /* Pods_SlackKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 407A2ABFC0611867E2BE34D0 /* Pods_SlackKit.framework */; };
4307A0801CC6D0910011D5DE /* Starscream.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 4307A07F1CC6D0910011D5DE /* Starscream.framework */; };
C16C987A1CE7D3DD00692776 /* Client+Utilities.swift in Sources */ = {isa = PBXBuildFile; fileRef = C16C98791CE7D3DD00692776 /* Client+Utilities.swift */; };
C1A85FF91CE3BCEF00756C40 /* Client+EventDispatching.swift in Sources */ = {isa = PBXBuildFile; fileRef = C1A85FF71CE3BCEF00756C40 /* Client+EventDispatching.swift */; };
C1A85FFA1CE3BCEF00756C40 /* Client+EventHandling.swift in Sources */ = {isa = PBXBuildFile; fileRef = C1A85FF81CE3BCEF00756C40 /* Client+EventHandling.swift */; };
/* End PBXBuildFile section */
/* Begin PBXFileReference section */
2601D61A1C7646B80012BF22 /* SlackWebAPIErrorDispatcher.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = SlackWebAPIErrorDispatcher.swift; path = Sources/SlackWebAPIErrorDispatcher.swift; sourceTree = "<group>"; };
2601D6241C7688610012BF22 /* OSX-Sample.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "OSX-Sample.app"; sourceTree = BUILT_PRODUCTS_DIR; };
2601D6261C7688610012BF22 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = "<group>"; };
2601D6281C7688610012BF22 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
2601D62B1C7688610012BF22 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Base.lproj/MainMenu.xib; sourceTree = "<group>"; };
2601D62D1C7688610012BF22 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
26072A341BB48B3A00CD650C /* SlackKit.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = SlackKit.framework; sourceTree = BUILT_PRODUCTS_DIR; };
260EC2301C4DC61D0093B253 /* ClientExtensions.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = ClientExtensions.swift; path = Sources/ClientExtensions.swift; sourceTree = "<group>"; };
260EC2301C4DC61D0093B253 /* Extensions.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = Extensions.swift; path = Sources/Extensions.swift; sourceTree = "<group>"; };
260EC2311C4DC61D0093B253 /* NetworkInterface.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = NetworkInterface.swift; path = Sources/NetworkInterface.swift; sourceTree = "<group>"; };
260EC2321C4DC61D0093B253 /* SlackWebAPI.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = SlackWebAPI.swift; path = Sources/SlackWebAPI.swift; sourceTree = "<group>"; };
263993B21CE90EE0004A6E93 /* SlackKit.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = SlackKit.framework; sourceTree = BUILT_PRODUCTS_DIR; };
263993D11CE90EED004A6E93 /* SlackKit.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = SlackKit.framework; sourceTree = BUILT_PRODUCTS_DIR; };
2661A6A41BBF62FF0026F67B /* SlackKit.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = SlackKit.h; sourceTree = "<group>"; };
266E05F01BBF780C00840D76 /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
268E46131CE8F79D009F19CC /* Info-iOS.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; name = "Info-iOS.plist"; path = "Supporting Files/Info-iOS.plist"; sourceTree = "<group>"; };
268E46141CE8F79D009F19CC /* Info-tvOS.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; name = "Info-tvOS.plist"; path = "Supporting Files/Info-tvOS.plist"; sourceTree = "<group>"; };
268E46151CE8F79D009F19CC /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; name = Info.plist; path = "Supporting Files/Info.plist"; sourceTree = "<group>"; };
26BBA1871C398E3C00BF7225 /* Bot.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = Bot.swift; path = Sources/Bot.swift; sourceTree = "<group>"; };
26BBA1881C398E3C00BF7225 /* Channel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = Channel.swift; path = Sources/Channel.swift; sourceTree = "<group>"; };
26BBA1891C398E3C00BF7225 /* Client.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = Client.swift; path = Sources/Client.swift; sourceTree = "<group>"; };
26BBA18A1C398E3C00BF7225 /* Event.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = Event.swift; path = Sources/Event.swift; sourceTree = "<group>"; };
26BBA18B1C398E3C00BF7225 /* EventDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = EventDelegate.swift; path = Sources/EventDelegate.swift; sourceTree = "<group>"; };
26BBA18C1C398E3C00BF7225 /* EventDispatcher.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = EventDispatcher.swift; path = Sources/EventDispatcher.swift; sourceTree = "<group>"; };
26BBA18D1C398E3C00BF7225 /* EventHandler.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = EventHandler.swift; path = Sources/EventHandler.swift; sourceTree = "<group>"; };
26BBA18E1C398E3C00BF7225 /* File.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = File.swift; path = Sources/File.swift; sourceTree = "<group>"; };
26BBA18F1C398E3C00BF7225 /* Message.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = Message.swift; path = Sources/Message.swift; sourceTree = "<group>"; };
26BBA1901C398E3C00BF7225 /* Team.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = Team.swift; path = Sources/Team.swift; sourceTree = "<group>"; };
@@ -59,59 +96,45 @@
26BBA1921C398E3C00BF7225 /* User.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = User.swift; path = Sources/User.swift; sourceTree = "<group>"; };
26BBA1931C398E3C00BF7225 /* UserGroup.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = UserGroup.swift; path = Sources/UserGroup.swift; sourceTree = "<group>"; };
26DF40341C7A0FA300E19241 /* Attachment.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = Attachment.swift; path = Sources/Attachment.swift; sourceTree = "<group>"; };
26F4BAC21C9DEBD1000910BA /* Leaderboard.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Leaderboard.swift; sourceTree = "<group>"; };
407A2ABFC0611867E2BE34D0 /* Pods_SlackKit.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_SlackKit.framework; sourceTree = BUILT_PRODUCTS_DIR; };
4347F92F3932C96C23B10B2A /* Pods-SlackKit.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-SlackKit.release.xcconfig"; path = "Pods/Target Support Files/Pods-SlackKit/Pods-SlackKit.release.xcconfig"; sourceTree = "<group>"; };
F59B6A12F1C4C4E24C58E1BF /* Pods-SlackKit.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-SlackKit.debug.xcconfig"; path = "Pods/Target Support Files/Pods-SlackKit/Pods-SlackKit.debug.xcconfig"; sourceTree = "<group>"; };
4307A07F1CC6D0910011D5DE /* Starscream.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Starscream.framework; path = Carthage/Build/Mac/Starscream.framework; sourceTree = "<group>"; };
C16C98791CE7D3DD00692776 /* Client+Utilities.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = "Client+Utilities.swift"; path = "Sources/Client+Utilities.swift"; sourceTree = "<group>"; };
C1A85FF71CE3BCEF00756C40 /* Client+EventDispatching.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = "Client+EventDispatching.swift"; path = "Sources/Client+EventDispatching.swift"; sourceTree = "<group>"; };
C1A85FF81CE3BCEF00756C40 /* Client+EventHandling.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = "Client+EventHandling.swift"; path = "Sources/Client+EventHandling.swift"; sourceTree = "<group>"; };
/* End PBXFileReference section */
/* Begin PBXFrameworksBuildPhase section */
2601D6211C7688610012BF22 /* Frameworks */ = {
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
);
runOnlyForDeploymentPostprocessing = 0;
};
26072A301BB48B3A00CD650C /* Frameworks */ = {
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
FFE3AC870D1C42EF276CCA2D /* Pods_SlackKit.framework in Frameworks */,
4307A0801CC6D0910011D5DE /* Starscream.framework in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
263993AA1CE90EE0004A6E93 /* Frameworks */ = {
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
263993AB1CE90EE0004A6E93 /* Starscream.framework in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
263993C91CE90EED004A6E93 /* Frameworks */ = {
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
263993CA1CE90EED004A6E93 /* Starscream.framework in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXFrameworksBuildPhase section */
/* Begin PBXGroup section */
0C2928E508A686B69F9F0117 /* Pods */ = {
isa = PBXGroup;
children = (
F59B6A12F1C4C4E24C58E1BF /* Pods-SlackKit.debug.xcconfig */,
4347F92F3932C96C23B10B2A /* Pods-SlackKit.release.xcconfig */,
);
name = Pods;
sourceTree = "<group>";
};
2601D6251C7688610012BF22 /* OSX-Sample */ = {
isa = PBXGroup;
children = (
2601D6261C7688610012BF22 /* AppDelegate.swift */,
26F4BAC21C9DEBD1000910BA /* Leaderboard.swift */,
2601D6281C7688610012BF22 /* Assets.xcassets */,
2601D62A1C7688610012BF22 /* MainMenu.xib */,
2601D62D1C7688610012BF22 /* Info.plist */,
);
path = "OSX-Sample";
sourceTree = "<group>";
};
26072A2A1BB48B3A00CD650C = {
isa = PBXGroup;
children = (
2661A6811BBF60E60026F67B /* SlackKit */,
2601D6251C7688610012BF22 /* OSX-Sample */,
26072A351BB48B3A00CD650C /* Products */,
0C2928E508A686B69F9F0117 /* Pods */,
CA70A3A1A9A1A259960DFBCF /* Frameworks */,
);
sourceTree = "<group>";
@@ -120,7 +143,8 @@
isa = PBXGroup;
children = (
26072A341BB48B3A00CD650C /* SlackKit.framework */,
2601D6241C7688610012BF22 /* OSX-Sample.app */,
263993B21CE90EE0004A6E93 /* SlackKit.framework */,
263993D11CE90EED004A6E93 /* SlackKit.framework */,
);
name = Products;
sourceTree = "<group>";
@@ -132,11 +156,12 @@
26BBA1871C398E3C00BF7225 /* Bot.swift */,
26BBA1881C398E3C00BF7225 /* Channel.swift */,
26BBA1891C398E3C00BF7225 /* Client.swift */,
260EC2301C4DC61D0093B253 /* ClientExtensions.swift */,
C1A85FF71CE3BCEF00756C40 /* Client+EventDispatching.swift */,
C1A85FF81CE3BCEF00756C40 /* Client+EventHandling.swift */,
C16C98791CE7D3DD00692776 /* Client+Utilities.swift */,
26BBA18A1C398E3C00BF7225 /* Event.swift */,
26BBA18B1C398E3C00BF7225 /* EventDelegate.swift */,
26BBA18C1C398E3C00BF7225 /* EventDispatcher.swift */,
26BBA18D1C398E3C00BF7225 /* EventHandler.swift */,
260EC2301C4DC61D0093B253 /* Extensions.swift */,
26BBA18E1C398E3C00BF7225 /* File.swift */,
26BBA18F1C398E3C00BF7225 /* Message.swift */,
260EC2311C4DC61D0093B253 /* NetworkInterface.swift */,
@@ -146,16 +171,26 @@
26BBA1911C398E3C00BF7225 /* Types.swift */,
26BBA1921C398E3C00BF7225 /* User.swift */,
26BBA1931C398E3C00BF7225 /* UserGroup.swift */,
2661A6A41BBF62FF0026F67B /* SlackKit.h */,
266E05F01BBF780C00840D76 /* Info.plist */,
268E46161CE8F7A2009F19CC /* Supporting Files */,
);
path = SlackKit;
sourceTree = "<group>";
};
268E46161CE8F7A2009F19CC /* Supporting Files */ = {
isa = PBXGroup;
children = (
2661A6A41BBF62FF0026F67B /* SlackKit.h */,
268E46151CE8F79D009F19CC /* Info.plist */,
268E46131CE8F79D009F19CC /* Info-iOS.plist */,
268E46141CE8F79D009F19CC /* Info-tvOS.plist */,
);
name = "Supporting Files";
sourceTree = "<group>";
};
CA70A3A1A9A1A259960DFBCF /* Frameworks */ = {
isa = PBXGroup;
children = (
407A2ABFC0611867E2BE34D0 /* Pods_SlackKit.framework */,
4307A07F1CC6D0910011D5DE /* Starscream.framework */,
);
name = Frameworks;
sourceTree = "<group>";
@@ -167,49 +202,83 @@
isa = PBXHeadersBuildPhase;
buildActionMask = 2147483647;
files = (
263993901CE90C87004A6E93 /* SlackKit.h in Headers */,
);
runOnlyForDeploymentPostprocessing = 0;
};
263993AC1CE90EE0004A6E93 /* Headers */ = {
isa = PBXHeadersBuildPhase;
buildActionMask = 2147483647;
files = (
263993AD1CE90EE0004A6E93 /* SlackKit.h in Headers */,
);
runOnlyForDeploymentPostprocessing = 0;
};
263993CB1CE90EED004A6E93 /* Headers */ = {
isa = PBXHeadersBuildPhase;
buildActionMask = 2147483647;
files = (
263993CC1CE90EED004A6E93 /* SlackKit.h in Headers */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXHeadersBuildPhase section */
/* Begin PBXNativeTarget section */
2601D6231C7688610012BF22 /* OSX-Sample */ = {
26072A331BB48B3A00CD650C /* SlackKit OS X */ = {
isa = PBXNativeTarget;
buildConfigurationList = 2601D62E1C7688610012BF22 /* Build configuration list for PBXNativeTarget "OSX-Sample" */;
buildConfigurationList = 26072A3C1BB48B3B00CD650C /* Build configuration list for PBXNativeTarget "SlackKit OS X" */;
buildPhases = (
2601D6201C7688610012BF22 /* Sources */,
2601D6211C7688610012BF22 /* Frameworks */,
2601D6221C7688610012BF22 /* Resources */,
);
buildRules = (
);
dependencies = (
);
name = "OSX-Sample";
productName = "OSX-Sample";
productReference = 2601D6241C7688610012BF22 /* OSX-Sample.app */;
productType = "com.apple.product-type.application";
};
26072A331BB48B3A00CD650C /* SlackKit */ = {
isa = PBXNativeTarget;
buildConfigurationList = 26072A3C1BB48B3B00CD650C /* Build configuration list for PBXNativeTarget "SlackKit" */;
buildPhases = (
EBD7A091EB278C5BA34791C5 /* Check Pods Manifest.lock */,
26072A2F1BB48B3A00CD650C /* Sources */,
26072A301BB48B3A00CD650C /* Frameworks */,
26072A311BB48B3A00CD650C /* Headers */,
26072A321BB48B3A00CD650C /* Resources */,
AC19A945F408269E4B4132CC /* Copy Pods Resources */,
);
buildRules = (
);
dependencies = (
);
name = SlackKit;
name = "SlackKit OS X";
productName = SlackRTMKit;
productReference = 26072A341BB48B3A00CD650C /* SlackKit.framework */;
productType = "com.apple.product-type.framework";
};
263993951CE90EE0004A6E93 /* SlackKit iOS */ = {
isa = PBXNativeTarget;
buildConfigurationList = 263993AF1CE90EE0004A6E93 /* Build configuration list for PBXNativeTarget "SlackKit iOS" */;
buildPhases = (
263993961CE90EE0004A6E93 /* Sources */,
263993AA1CE90EE0004A6E93 /* Frameworks */,
263993AC1CE90EE0004A6E93 /* Headers */,
263993AE1CE90EE0004A6E93 /* Resources */,
);
buildRules = (
);
dependencies = (
);
name = "SlackKit iOS";
productName = SlackRTMKit;
productReference = 263993B21CE90EE0004A6E93 /* SlackKit.framework */;
productType = "com.apple.product-type.framework";
};
263993B41CE90EED004A6E93 /* SlackKit tvOS */ = {
isa = PBXNativeTarget;
buildConfigurationList = 263993CE1CE90EED004A6E93 /* Build configuration list for PBXNativeTarget "SlackKit tvOS" */;
buildPhases = (
263993B51CE90EED004A6E93 /* Sources */,
263993C91CE90EED004A6E93 /* Frameworks */,
263993CB1CE90EED004A6E93 /* Headers */,
263993CD1CE90EED004A6E93 /* Resources */,
);
buildRules = (
);
dependencies = (
);
name = "SlackKit tvOS";
productName = SlackRTMKit;
productReference = 263993D11CE90EED004A6E93 /* SlackKit.framework */;
productType = "com.apple.product-type.framework";
};
/* End PBXNativeTarget section */
/* Begin PBXProject section */
@@ -220,9 +289,6 @@
LastUpgradeCheck = 0700;
ORGANIZATIONNAME = "Launch Software LLC";
TargetAttributes = {
2601D6231C7688610012BF22 = {
CreatedOnToolsVersion = 7.2.1;
};
26072A331BB48B3A00CD650C = {
CreatedOnToolsVersion = 7.0;
};
@@ -241,23 +307,29 @@
projectDirPath = "";
projectRoot = "";
targets = (
26072A331BB48B3A00CD650C /* SlackKit */,
2601D6231C7688610012BF22 /* OSX-Sample */,
26072A331BB48B3A00CD650C /* SlackKit OS X */,
263993951CE90EE0004A6E93 /* SlackKit iOS */,
263993B41CE90EED004A6E93 /* SlackKit tvOS */,
);
};
/* End PBXProject section */
/* Begin PBXResourcesBuildPhase section */
2601D6221C7688610012BF22 /* Resources */ = {
26072A321BB48B3A00CD650C /* Resources */ = {
isa = PBXResourcesBuildPhase;
buildActionMask = 2147483647;
files = (
2601D6291C7688610012BF22 /* Assets.xcassets in Resources */,
2601D62C1C7688610012BF22 /* MainMenu.xib in Resources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
26072A321BB48B3A00CD650C /* Resources */ = {
263993AE1CE90EE0004A6E93 /* Resources */ = {
isa = PBXResourcesBuildPhase;
buildActionMask = 2147483647;
files = (
);
runOnlyForDeploymentPostprocessing = 0;
};
263993CD1CE90EED004A6E93 /* Resources */ = {
isa = PBXResourcesBuildPhase;
buildActionMask = 2147483647;
files = (
@@ -266,114 +338,88 @@
};
/* End PBXResourcesBuildPhase section */
/* Begin PBXShellScriptBuildPhase section */
AC19A945F408269E4B4132CC /* Copy Pods Resources */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
inputPaths = (
);
name = "Copy Pods Resources";
outputPaths = (
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods-SlackKit/Pods-SlackKit-resources.sh\"\n";
showEnvVarsInLog = 0;
};
EBD7A091EB278C5BA34791C5 /* Check Pods Manifest.lock */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
inputPaths = (
);
name = "Check Pods Manifest.lock";
outputPaths = (
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "diff \"${PODS_ROOT}/../Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [[ $? != 0 ]] ; then\n cat << EOM\nerror: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\nEOM\n exit 1\nfi\n";
showEnvVarsInLog = 0;
};
/* End PBXShellScriptBuildPhase section */
/* Begin PBXSourcesBuildPhase section */
2601D6201C7688610012BF22 /* Sources */ = {
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
2601D6271C7688610012BF22 /* AppDelegate.swift in Sources */,
26F4BAC31C9DEBD1000910BA /* Leaderboard.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
26072A2F1BB48B3A00CD650C /* Sources */ = {
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
26BBA1991C398E3C00BF7225 /* EventDispatcher.swift in Sources */,
C16C987A1CE7D3DD00692776 /* Client+Utilities.swift in Sources */,
26BBA1951C398E3C00BF7225 /* Channel.swift in Sources */,
26BBA19F1C398E3C00BF7225 /* User.swift in Sources */,
26BBA19E1C398E3C00BF7225 /* Types.swift in Sources */,
26BBA1961C398E3C00BF7225 /* Client.swift in Sources */,
26BBA19A1C398E3C00BF7225 /* EventHandler.swift in Sources */,
26BBA1971C398E3C00BF7225 /* Event.swift in Sources */,
26BBA1941C398E3C00BF7225 /* Bot.swift in Sources */,
C1A85FF91CE3BCEF00756C40 /* Client+EventDispatching.swift in Sources */,
26BBA19B1C398E3C00BF7225 /* File.swift in Sources */,
260EC2351C4DC61D0093B253 /* SlackWebAPI.swift in Sources */,
26DF40351C7A0FA300E19241 /* Attachment.swift in Sources */,
26BBA19C1C398E3C00BF7225 /* Message.swift in Sources */,
26BBA19D1C398E3C00BF7225 /* Team.swift in Sources */,
260EC2331C4DC61D0093B253 /* ClientExtensions.swift in Sources */,
260EC2331C4DC61D0093B253 /* Extensions.swift in Sources */,
26BBA1A01C398E3C00BF7225 /* UserGroup.swift in Sources */,
2601D61B1C7646B80012BF22 /* SlackWebAPIErrorDispatcher.swift in Sources */,
C1A85FFA1CE3BCEF00756C40 /* Client+EventHandling.swift in Sources */,
260EC2341C4DC61D0093B253 /* NetworkInterface.swift in Sources */,
26BBA1981C398E3C00BF7225 /* EventDelegate.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
263993961CE90EE0004A6E93 /* Sources */ = {
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
263993971CE90EE0004A6E93 /* Client+Utilities.swift in Sources */,
263993981CE90EE0004A6E93 /* Channel.swift in Sources */,
263993991CE90EE0004A6E93 /* User.swift in Sources */,
2639939A1CE90EE0004A6E93 /* Types.swift in Sources */,
2639939B1CE90EE0004A6E93 /* Client.swift in Sources */,
2639939C1CE90EE0004A6E93 /* Event.swift in Sources */,
2639939D1CE90EE0004A6E93 /* Bot.swift in Sources */,
2639939E1CE90EE0004A6E93 /* Client+EventDispatching.swift in Sources */,
2639939F1CE90EE0004A6E93 /* File.swift in Sources */,
263993A01CE90EE0004A6E93 /* SlackWebAPI.swift in Sources */,
263993A11CE90EE0004A6E93 /* Attachment.swift in Sources */,
263993A21CE90EE0004A6E93 /* Message.swift in Sources */,
263993A31CE90EE0004A6E93 /* Team.swift in Sources */,
263993A41CE90EE0004A6E93 /* Extensions.swift in Sources */,
263993A51CE90EE0004A6E93 /* UserGroup.swift in Sources */,
263993A61CE90EE0004A6E93 /* SlackWebAPIErrorDispatcher.swift in Sources */,
263993A71CE90EE0004A6E93 /* Client+EventHandling.swift in Sources */,
263993A81CE90EE0004A6E93 /* NetworkInterface.swift in Sources */,
263993A91CE90EE0004A6E93 /* EventDelegate.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
263993B51CE90EED004A6E93 /* Sources */ = {
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
263993B61CE90EED004A6E93 /* Client+Utilities.swift in Sources */,
263993B71CE90EED004A6E93 /* Channel.swift in Sources */,
263993B81CE90EED004A6E93 /* User.swift in Sources */,
263993B91CE90EED004A6E93 /* Types.swift in Sources */,
263993BA1CE90EED004A6E93 /* Client.swift in Sources */,
263993BB1CE90EED004A6E93 /* Event.swift in Sources */,
263993BC1CE90EED004A6E93 /* Bot.swift in Sources */,
263993BD1CE90EED004A6E93 /* Client+EventDispatching.swift in Sources */,
263993BE1CE90EED004A6E93 /* File.swift in Sources */,
263993BF1CE90EED004A6E93 /* SlackWebAPI.swift in Sources */,
263993C01CE90EED004A6E93 /* Attachment.swift in Sources */,
263993C11CE90EED004A6E93 /* Message.swift in Sources */,
263993C21CE90EED004A6E93 /* Team.swift in Sources */,
263993C31CE90EED004A6E93 /* Extensions.swift in Sources */,
263993C41CE90EED004A6E93 /* UserGroup.swift in Sources */,
263993C51CE90EED004A6E93 /* SlackWebAPIErrorDispatcher.swift in Sources */,
263993C61CE90EED004A6E93 /* Client+EventHandling.swift in Sources */,
263993C71CE90EED004A6E93 /* NetworkInterface.swift in Sources */,
263993C81CE90EED004A6E93 /* EventDelegate.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXSourcesBuildPhase section */
/* Begin PBXVariantGroup section */
2601D62A1C7688610012BF22 /* MainMenu.xib */ = {
isa = PBXVariantGroup;
children = (
2601D62B1C7688610012BF22 /* Base */,
);
name = MainMenu.xib;
sourceTree = "<group>";
};
/* End PBXVariantGroup section */
/* Begin XCBuildConfiguration section */
2601D62F1C7688610012BF22 /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CODE_SIGN_IDENTITY = "-";
COMBINE_HIDPI_IMAGES = YES;
INFOPLIST_FILE = "OSX-Sample/Info.plist";
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks";
PRODUCT_BUNDLE_IDENTIFIER = "LS.OSX-Sample";
PRODUCT_NAME = "$(TARGET_NAME)";
};
name = Debug;
};
2601D6301C7688610012BF22 /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CODE_SIGN_IDENTITY = "-";
COMBINE_HIDPI_IMAGES = YES;
INFOPLIST_FILE = "OSX-Sample/Info.plist";
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks";
PRODUCT_BUNDLE_IDENTIFIER = "LS.OSX-Sample";
PRODUCT_NAME = "$(TARGET_NAME)";
};
name = Release;
};
26072A3A1BB48B3B00CD650C /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
@@ -410,6 +456,7 @@
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 8.0;
MACOSX_DEPLOYMENT_TARGET = 10.11;
MTL_ENABLE_DEBUG_INFO = YES;
ONLY_ACTIVE_ARCH = YES;
@@ -450,6 +497,7 @@
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 8.0;
MACOSX_DEPLOYMENT_TARGET = 10.11;
MTL_ENABLE_DEBUG_INFO = NO;
SDKROOT = macosx;
@@ -460,7 +508,6 @@
};
26072A3D1BB48B3B00CD650C /* Debug */ = {
isa = XCBuildConfiguration;
baseConfigurationReference = F59B6A12F1C4C4E24C58E1BF /* Pods-SlackKit.debug.xcconfig */;
buildSettings = {
CLANG_ENABLE_MODULES = YES;
COMBINE_HIDPI_IMAGES = YES;
@@ -468,10 +515,15 @@
DYLIB_COMPATIBILITY_VERSION = 1;
DYLIB_CURRENT_VERSION = 1;
DYLIB_INSTALL_NAME_BASE = "@rpath";
FRAMEWORK_SEARCH_PATHS = (
"$(inherited)",
"$(PROJECT_DIR)/Carthage/Build/Mac",
);
FRAMEWORK_VERSION = A;
INFOPLIST_FILE = SlackKit/Info.plist;
INFOPLIST_FILE = "$(SRCROOT)/SlackKit/Supporting Files/Info.plist";
INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/Frameworks";
MACOSX_DEPLOYMENT_TARGET = 10.10;
PRODUCT_BUNDLE_IDENTIFIER = com.launchsoft.SlackKit;
PRODUCT_NAME = SlackKit;
SKIP_INSTALL = YES;
@@ -481,7 +533,6 @@
};
26072A3E1BB48B3B00CD650C /* Release */ = {
isa = XCBuildConfiguration;
baseConfigurationReference = 4347F92F3932C96C23B10B2A /* Pods-SlackKit.release.xcconfig */;
buildSettings = {
CLANG_ENABLE_MODULES = YES;
COMBINE_HIDPI_IMAGES = YES;
@@ -489,28 +540,134 @@
DYLIB_COMPATIBILITY_VERSION = 1;
DYLIB_CURRENT_VERSION = 1;
DYLIB_INSTALL_NAME_BASE = "@rpath";
FRAMEWORK_SEARCH_PATHS = (
"$(inherited)",
"$(PROJECT_DIR)/Carthage/Build/Mac",
);
FRAMEWORK_VERSION = A;
INFOPLIST_FILE = SlackKit/Info.plist;
INFOPLIST_FILE = "$(SRCROOT)/SlackKit/Supporting Files/Info.plist";
INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/Frameworks";
MACOSX_DEPLOYMENT_TARGET = 10.10;
PRODUCT_BUNDLE_IDENTIFIER = com.launchsoft.SlackKit;
PRODUCT_NAME = SlackKit;
SKIP_INSTALL = YES;
};
name = Release;
};
263993B01CE90EE0004A6E93 /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
CLANG_ENABLE_MODULES = YES;
COMBINE_HIDPI_IMAGES = YES;
DEFINES_MODULE = YES;
DYLIB_COMPATIBILITY_VERSION = 1;
DYLIB_CURRENT_VERSION = 1;
DYLIB_INSTALL_NAME_BASE = "@rpath";
FRAMEWORK_SEARCH_PATHS = (
"$(inherited)",
"$(PROJECT_DIR)/Carthage/Build/iOS",
);
FRAMEWORK_VERSION = A;
INFOPLIST_FILE = "$(SRCROOT)/SlackKit/Supporting Files/Info-iOS.plist";
INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
IPHONEOS_DEPLOYMENT_TARGET = 8.0;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/Frameworks";
MACOSX_DEPLOYMENT_TARGET = 10.10;
PRODUCT_BUNDLE_IDENTIFIER = com.launchsoft.SlackKit;
PRODUCT_NAME = SlackKit;
SDKROOT = iphoneos;
SKIP_INSTALL = YES;
SUPPORTED_PLATFORMS = "iphonesimulator iphoneos";
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
};
name = Debug;
};
263993B11CE90EE0004A6E93 /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
CLANG_ENABLE_MODULES = YES;
COMBINE_HIDPI_IMAGES = YES;
DEFINES_MODULE = YES;
DYLIB_COMPATIBILITY_VERSION = 1;
DYLIB_CURRENT_VERSION = 1;
DYLIB_INSTALL_NAME_BASE = "@rpath";
FRAMEWORK_SEARCH_PATHS = (
"$(inherited)",
"$(PROJECT_DIR)/Carthage/Build/iOS",
);
FRAMEWORK_VERSION = A;
INFOPLIST_FILE = "$(SRCROOT)/SlackKit/Supporting Files/Info-iOS.plist";
INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
IPHONEOS_DEPLOYMENT_TARGET = 8.0;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/Frameworks";
MACOSX_DEPLOYMENT_TARGET = 10.10;
PRODUCT_BUNDLE_IDENTIFIER = com.launchsoft.SlackKit;
PRODUCT_NAME = SlackKit;
SDKROOT = iphoneos;
SKIP_INSTALL = YES;
SUPPORTED_PLATFORMS = "iphonesimulator iphoneos";
};
name = Release;
};
263993CF1CE90EED004A6E93 /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
CLANG_ENABLE_MODULES = YES;
COMBINE_HIDPI_IMAGES = YES;
DEFINES_MODULE = YES;
DYLIB_COMPATIBILITY_VERSION = 1;
DYLIB_CURRENT_VERSION = 1;
DYLIB_INSTALL_NAME_BASE = "@rpath";
FRAMEWORK_SEARCH_PATHS = (
"$(inherited)",
"$(PROJECT_DIR)/Carthage/Build/tvOS",
);
FRAMEWORK_VERSION = A;
INFOPLIST_FILE = "$(SRCROOT)/SlackKit/Supporting Files/Info-tvOS.plist";
INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/Frameworks";
MACOSX_DEPLOYMENT_TARGET = 10.10;
PRODUCT_BUNDLE_IDENTIFIER = com.launchsoft.SlackKit;
PRODUCT_NAME = SlackKit;
SDKROOT = appletvos;
SKIP_INSTALL = YES;
SUPPORTED_PLATFORMS = "appletvsimulator appletvos";
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
TVOS_DEPLOYMENT_TARGET = 9.0;
};
name = Debug;
};
263993D01CE90EED004A6E93 /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
CLANG_ENABLE_MODULES = YES;
COMBINE_HIDPI_IMAGES = YES;
DEFINES_MODULE = YES;
DYLIB_COMPATIBILITY_VERSION = 1;
DYLIB_CURRENT_VERSION = 1;
DYLIB_INSTALL_NAME_BASE = "@rpath";
FRAMEWORK_SEARCH_PATHS = (
"$(inherited)",
"$(PROJECT_DIR)/Carthage/Build/tvOS",
);
FRAMEWORK_VERSION = A;
INFOPLIST_FILE = "$(SRCROOT)/SlackKit/Supporting Files/Info-tvOS.plist";
INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/Frameworks";
MACOSX_DEPLOYMENT_TARGET = 10.10;
PRODUCT_BUNDLE_IDENTIFIER = com.launchsoft.SlackKit;
PRODUCT_NAME = SlackKit;
SDKROOT = appletvos;
SKIP_INSTALL = YES;
SUPPORTED_PLATFORMS = "appletvsimulator appletvos";
TVOS_DEPLOYMENT_TARGET = 9.0;
};
name = Release;
};
/* End XCBuildConfiguration section */
/* Begin XCConfigurationList section */
2601D62E1C7688610012BF22 /* Build configuration list for PBXNativeTarget "OSX-Sample" */ = {
isa = XCConfigurationList;
buildConfigurations = (
2601D62F1C7688610012BF22 /* Debug */,
2601D6301C7688610012BF22 /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
26072A2E1BB48B3A00CD650C /* Build configuration list for PBXProject "SlackKit" */ = {
isa = XCConfigurationList;
buildConfigurations = (
@@ -520,7 +677,7 @@
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
26072A3C1BB48B3B00CD650C /* Build configuration list for PBXNativeTarget "SlackKit" */ = {
26072A3C1BB48B3B00CD650C /* Build configuration list for PBXNativeTarget "SlackKit OS X" */ = {
isa = XCConfigurationList;
buildConfigurations = (
26072A3D1BB48B3B00CD650C /* Debug */,
@@ -529,6 +686,24 @@
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
263993AF1CE90EE0004A6E93 /* Build configuration list for PBXNativeTarget "SlackKit iOS" */ = {
isa = XCConfigurationList;
buildConfigurations = (
263993B01CE90EE0004A6E93 /* Debug */,
263993B11CE90EE0004A6E93 /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
263993CE1CE90EED004A6E93 /* Build configuration list for PBXNativeTarget "SlackKit tvOS" */ = {
isa = XCConfigurationList;
buildConfigurations = (
263993CF1CE90EED004A6E93 /* Debug */,
263993D01CE90EED004A6E93 /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
/* End XCConfigurationList section */
};
rootObject = 26072A2B1BB48B3A00CD650C /* Project object */;
@@ -0,0 +1,80 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "0730"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
buildImplicitDependencies = "YES">
<BuildActionEntries>
<BuildActionEntry
buildForTesting = "YES"
buildForRunning = "YES"
buildForProfiling = "YES"
buildForArchiving = "YES"
buildForAnalyzing = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "26072A331BB48B3A00CD650C"
BuildableName = "SlackKit.framework"
BlueprintName = "SlackKit OS X"
ReferencedContainer = "container:SlackKit.xcodeproj">
</BuildableReference>
</BuildActionEntry>
</BuildActionEntries>
</BuildAction>
<TestAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
shouldUseLaunchSchemeArgsEnv = "YES">
<Testables>
</Testables>
<AdditionalOptions>
</AdditionalOptions>
</TestAction>
<LaunchAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
launchStyle = "0"
useCustomWorkingDirectory = "NO"
ignoresPersistentStateOnLaunch = "NO"
debugDocumentVersioning = "YES"
debugServiceExtension = "internal"
allowLocationSimulation = "YES">
<MacroExpansion>
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "26072A331BB48B3A00CD650C"
BuildableName = "SlackKit.framework"
BlueprintName = "SlackKit OS X"
ReferencedContainer = "container:SlackKit.xcodeproj">
</BuildableReference>
</MacroExpansion>
<AdditionalOptions>
</AdditionalOptions>
</LaunchAction>
<ProfileAction
buildConfiguration = "Release"
shouldUseLaunchSchemeArgsEnv = "YES"
savedToolIdentifier = ""
useCustomWorkingDirectory = "NO"
debugDocumentVersioning = "YES">
<MacroExpansion>
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "26072A331BB48B3A00CD650C"
BuildableName = "SlackKit.framework"
BlueprintName = "SlackKit OS X"
ReferencedContainer = "container:SlackKit.xcodeproj">
</BuildableReference>
</MacroExpansion>
</ProfileAction>
<AnalyzeAction
buildConfiguration = "Debug">
</AnalyzeAction>
<ArchiveAction
buildConfiguration = "Release"
revealArchiveInOrganizer = "YES">
</ArchiveAction>
</Scheme>
@@ -0,0 +1,80 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "0730"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
buildImplicitDependencies = "YES">
<BuildActionEntries>
<BuildActionEntry
buildForTesting = "YES"
buildForRunning = "YES"
buildForProfiling = "YES"
buildForArchiving = "YES"
buildForAnalyzing = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "263993951CE90EE0004A6E93"
BuildableName = "SlackKit.framework"
BlueprintName = "SlackKit iOS"
ReferencedContainer = "container:SlackKit.xcodeproj">
</BuildableReference>
</BuildActionEntry>
</BuildActionEntries>
</BuildAction>
<TestAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
shouldUseLaunchSchemeArgsEnv = "YES">
<Testables>
</Testables>
<AdditionalOptions>
</AdditionalOptions>
</TestAction>
<LaunchAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
launchStyle = "0"
useCustomWorkingDirectory = "NO"
ignoresPersistentStateOnLaunch = "NO"
debugDocumentVersioning = "YES"
debugServiceExtension = "internal"
allowLocationSimulation = "YES">
<MacroExpansion>
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "263993951CE90EE0004A6E93"
BuildableName = "SlackKit.framework"
BlueprintName = "SlackKit iOS"
ReferencedContainer = "container:SlackKit.xcodeproj">
</BuildableReference>
</MacroExpansion>
<AdditionalOptions>
</AdditionalOptions>
</LaunchAction>
<ProfileAction
buildConfiguration = "Release"
shouldUseLaunchSchemeArgsEnv = "YES"
savedToolIdentifier = ""
useCustomWorkingDirectory = "NO"
debugDocumentVersioning = "YES">
<MacroExpansion>
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "263993951CE90EE0004A6E93"
BuildableName = "SlackKit.framework"
BlueprintName = "SlackKit iOS"
ReferencedContainer = "container:SlackKit.xcodeproj">
</BuildableReference>
</MacroExpansion>
</ProfileAction>
<AnalyzeAction
buildConfiguration = "Debug">
</AnalyzeAction>
<ArchiveAction
buildConfiguration = "Release"
revealArchiveInOrganizer = "YES">
</ArchiveAction>
</Scheme>
@@ -0,0 +1,80 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "0730"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
buildImplicitDependencies = "YES">
<BuildActionEntries>
<BuildActionEntry
buildForTesting = "YES"
buildForRunning = "YES"
buildForProfiling = "YES"
buildForArchiving = "YES"
buildForAnalyzing = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "263993B41CE90EED004A6E93"
BuildableName = "SlackKit.framework"
BlueprintName = "SlackKit tvOS"
ReferencedContainer = "container:SlackKit.xcodeproj">
</BuildableReference>
</BuildActionEntry>
</BuildActionEntries>
</BuildAction>
<TestAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
shouldUseLaunchSchemeArgsEnv = "YES">
<Testables>
</Testables>
<AdditionalOptions>
</AdditionalOptions>
</TestAction>
<LaunchAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
launchStyle = "0"
useCustomWorkingDirectory = "NO"
ignoresPersistentStateOnLaunch = "NO"
debugDocumentVersioning = "YES"
debugServiceExtension = "internal"
allowLocationSimulation = "YES">
<MacroExpansion>
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "263993B41CE90EED004A6E93"
BuildableName = "SlackKit.framework"
BlueprintName = "SlackKit tvOS"
ReferencedContainer = "container:SlackKit.xcodeproj">
</BuildableReference>
</MacroExpansion>
<AdditionalOptions>
</AdditionalOptions>
</LaunchAction>
<ProfileAction
buildConfiguration = "Release"
shouldUseLaunchSchemeArgsEnv = "YES"
savedToolIdentifier = ""
useCustomWorkingDirectory = "NO"
debugDocumentVersioning = "YES">
<MacroExpansion>
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "263993B41CE90EED004A6E93"
BuildableName = "SlackKit.framework"
BlueprintName = "SlackKit tvOS"
ReferencedContainer = "container:SlackKit.xcodeproj">
</BuildableReference>
</MacroExpansion>
</ProfileAction>
<AnalyzeAction
buildConfiguration = "Debug">
</AnalyzeAction>
<ArchiveAction
buildConfiguration = "Release"
revealArchiveInOrganizer = "YES">
</ArchiveAction>
</Scheme>
-10
View File
@@ -1,10 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<Workspace
version = "1.0">
<FileRef
location = "group:SlackKit.xcodeproj">
</FileRef>
<FileRef
location = "group:Pods/Pods.xcodeproj">
</FileRef>
</Workspace>
+22 -6
View File
@@ -37,8 +37,11 @@ public struct Attachment {
public let fields: [AttachmentField]?
public let imageURL: String?
public let thumbURL: String?
public let footer: String?
public let footerIcon: String?
public let ts: Int?
internal init?(attachment: [String: AnyObject]?) {
internal init(attachment: [String: AnyObject]?) {
fallback = attachment?["fallback"] as? String
color = attachment?["color"] as? String
pretext = attachment?["pretext"] as? String
@@ -50,12 +53,13 @@ public struct Attachment {
text = attachment?["text"] as? String
imageURL = attachment?["image_url"] as? String
thumbURL = attachment?["thumb_url"] as? String
fields = (attachment?["fields"] as? [[String: AnyObject]])?.objectArrayFromDictionaryArray({(field) -> AttachmentField? in
return AttachmentField(field: field)
})
footer = attachment?["footer"] as? String
footerIcon = attachment?["footer_icon"] as? String
ts = attachment?["ts"] as? Int
fields = (attachment?["fields"] as? [[String: AnyObject]])?.map { AttachmentField(field: $0) }
}
public init?(fallback: String, title:String, colorHex: String? = nil, pretext: String? = nil, authorName: String? = nil, authorLink: String? = nil, authorIcon: String? = nil, titleLink: String? = nil, text: String? = nil, fields: [AttachmentField]? = nil, imageURL: String? = nil, thumbURL: String? = nil) {
public init(fallback: String, title:String, colorHex: String? = nil, pretext: String? = nil, authorName: String? = nil, authorLink: String? = nil, authorIcon: String? = nil, titleLink: String? = nil, text: String? = nil, fields: [AttachmentField]? = nil, imageURL: String? = nil, thumbURL: String? = nil, footer: String? = nil, footerIcon:String? = nil, ts:Int? = nil) {
self.fallback = fallback
self.color = colorHex
self.pretext = pretext
@@ -68,6 +72,9 @@ public struct Attachment {
self.fields = fields
self.imageURL = imageURL
self.thumbURL = thumbURL
self.footer = footer
self.footerIcon = footerIcon
self.ts = ts
}
internal func dictionary() -> [String: AnyObject] {
@@ -84,6 +91,9 @@ public struct Attachment {
attachment["fields"] = fieldJSONArray(fields)
attachment["image_url"] = imageURL
attachment["thumb_url"] = thumbURL
attachment["footer"] = footer
attachment["footer_icon"] = footerIcon
attachment["ts"] = ts
return attachment
}
@@ -105,7 +115,7 @@ public struct AttachmentField {
public let value: String?
public let short: Bool?
internal init?(field: [String: AnyObject]?) {
internal init(field: [String: AnyObject]?) {
title = field?["title"] as? String
value = field?["value"] as? String
short = field?["short"] as? Bool
@@ -126,3 +136,9 @@ public struct AttachmentField {
}
}
public enum AttachmentColor: String {
case Good = "good"
case Warning = "warning"
case Danger = "danger"
}
+1 -1
View File
@@ -27,7 +27,7 @@ public struct Bot {
internal(set) public var name: String?
internal(set) public var icons: [String: AnyObject]?
internal init?(bot: [String: AnyObject]?) {
internal init(bot: [String: AnyObject]?) {
id = bot?["id"] as? String
name = bot?["name"] as? String
icons = bot?["icons"] as? [String: AnyObject]
+9 -9
View File
@@ -38,10 +38,10 @@ public struct Channel {
internal(set) public var topic: Topic?
internal(set) public var purpose: Topic?
internal(set) public var isMember: Bool?
internal(set) public var lastRead: String?
public var lastRead: String?
internal(set) public var latest: Message?
internal(set) public var unread: Int?
internal(set) public var unreadCountDisplay: Int?
public var unread: Int?
public var unreadCountDisplay: Int?
internal(set) public var hasPins: Bool?
internal(set) public var members: [String]?
// Client use
@@ -49,7 +49,7 @@ public struct Channel {
internal(set) public var usersTyping = [String]()
internal(set) public var messages = [String: Message]()
internal init?(channel: [String: AnyObject]?) {
internal init(channel: [String: AnyObject]?) {
id = channel?["id"] as? String
name = channel?["name"] as? String
created = channel?["created"] as? Int
@@ -70,15 +70,15 @@ public struct Channel {
unreadCountDisplay = channel?["unread_count_display"] as? Int
hasPins = channel?["has_pins"] as? Bool
members = channel?["members"] as? [String]
if (Message(message: channel?["latest"] as? [String: AnyObject])?.ts == nil) {
latest = Message(ts: channel?["latest"] as? String)
if let latestMsgDictionary = channel?["latest"] as? [String: AnyObject] {
latest = Message(message: latestMsgDictionary)
} else {
latest = Message(message: channel?["latest"] as? [String: AnyObject])
latest = Message(ts: channel?["latest"] as? String)
}
}
internal init?(id:String?) {
internal init(id:String?) {
self.id = id
created = nil
creator = nil
@@ -0,0 +1,177 @@
//
// Client+EventDispatching.swift
//
// Copyright © 2016 Peter Zignego. All rights reserved.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
internal extension Client {
func dispatch(event: [String: AnyObject]) {
let event = Event(event: event)
guard let type = event.type else {
return
}
switch type {
case .Hello:
connected = true
slackEventsDelegate?.clientConnected()
case .Ok:
messageSent(event)
case .Message:
if (event.subtype != nil) {
messageDispatcher(event)
} else {
messageReceived(event)
}
case .UserTyping:
userTyping(event)
case .ChannelMarked, .IMMarked, .GroupMarked:
channelMarked(event)
case .ChannelCreated, .IMCreated:
channelCreated(event)
case .ChannelJoined, .GroupJoined:
channelJoined(event)
case .ChannelLeft, .GroupLeft:
channelLeft(event)
case .ChannelDeleted:
channelDeleted(event)
case .ChannelRenamed, .GroupRename:
channelRenamed(event)
case .ChannelArchive, .GroupArchive:
channelArchived(event, archived: true)
case .ChannelUnarchive, .GroupUnarchive:
channelArchived(event, archived: false)
case .ChannelHistoryChanged, .IMHistoryChanged, .GroupHistoryChanged:
channelHistoryChanged(event)
case .DNDUpdated:
doNotDisturbUpdated(event)
case .DNDUpatedUser:
doNotDisturbUserUpdated(event)
case .IMOpen, .GroupOpen:
open(event, open: true)
case .IMClose, .GroupClose:
open(event, open: false)
case .FileCreated:
processFile(event)
case .FileShared:
processFile(event)
case .FileUnshared:
processFile(event)
case .FilePublic:
processFile(event)
case .FilePrivate:
filePrivate(event)
case .FileChanged:
processFile(event)
case .FileDeleted:
deleteFile(event)
case .FileCommentAdded:
fileCommentAdded(event)
case .FileCommentEdited:
fileCommentEdited(event)
case .FileCommentDeleted:
fileCommentDeleted(event)
case .PinAdded:
pinAdded(event)
case .PinRemoved:
pinRemoved(event)
case .Pong:
pong(event)
case .PresenceChange:
presenceChange(event)
case .ManualPresenceChange:
manualPresenceChange(event)
case .PrefChange:
changePreference(event)
case .UserChange:
userChange(event)
case .TeamJoin:
teamJoin(event)
case .StarAdded:
itemStarred(event, star: true)
case .StarRemoved:
itemStarred(event, star: false)
case .ReactionAdded:
addedReaction(event)
case .ReactionRemoved:
removedReaction(event)
case .EmojiChanged:
emojiChanged(event)
case .CommandsChanged:
// 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:
teamPlanChange(event)
case .TeamPrefChange:
teamPreferenceChange(event)
case .TeamRename:
teamNameChange(event)
case .TeamDomainChange:
teamDomainChange(event)
case .EmailDomainChange:
emailDomainChange(event)
case .TeamProfileChange:
teamProfileChange(event)
case .TeamProfileDelete:
teamProfileDeleted(event)
case .TeamProfileReorder:
teamProfileReordered(event)
case .BotAdded:
bot(event)
case .BotChanged:
bot(event)
case .AccountsChanged:
// 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:
connect(pingInterval: pingInterval, timeout: timeout, reconnect: reconnect)
case .ReconnectURL:
// The reconnect_url event is currently unsupported and experimental.
break
case .SubteamCreated, .SubteamUpdated:
subteam(event)
case .SubteamSelfAdded:
subteamAddedSelf(event)
case.SubteamSelfRemoved:
subteamRemovedSelf(event)
case .Error:
print("Error: \(event)")
break
}
}
func messageDispatcher(event:Event) {
guard let value = event.subtype, subtype = MessageSubtype(rawValue:value) else {
return
}
switch subtype {
case .MessageChanged:
messageChanged(event)
case .MessageDeleted:
messageDeleted(event)
default:
messageReceived(event)
}
}
}
+563
View File
@@ -0,0 +1,563 @@
//
// Client+EventHandling.swift
//
// Copyright © 2016 Peter Zignego. All rights reserved.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
import Foundation
internal extension Client {
//MARK: - Pong
func pong(event: Event) {
pong = event.replyTo
}
//MARK: - Messages
func messageSent(event: Event) {
guard let reply = event.replyTo, message = sentMessages[NSNumber(double: reply).stringValue], channel = message.channel, ts = message.ts else {
return
}
message.ts = event.ts
message.text = event.text
channels[channel]?.messages[ts] = message
messageEventsDelegate?.messageSent(message)
}
func messageReceived(event: Event) {
guard let channel = event.channel, message = event.message, id = channel.id, ts = message.ts else {
return
}
channels[id]?.messages[ts] = message
messageEventsDelegate?.messageReceived(message)
}
func messageChanged(event: Event) {
guard let id = event.channel?.id, nested = event.nestedMessage, ts = nested.ts else {
return
}
channels[id]?.messages[ts] = nested
messageEventsDelegate?.messageChanged(nested)
}
func messageDeleted(event: Event) {
guard let id = event.channel?.id, key = event.message?.deletedTs, message = channels[id]?.messages[key] else {
return
}
channels[id]?.messages.removeValueForKey(key)
messageEventsDelegate?.messageDeleted(message)
}
//MARK: - Channels
func userTyping(event: Event) {
guard let channel = event.channel, channelID = channel.id, user = event.user, userID = user.id where
channels.indexForKey(channelID) != nil && !channels[channelID]!.usersTyping.contains(userID) else {
return
}
channels[channelID]?.usersTyping.append(userID)
channelEventsDelegate?.userTyping(channel, user: user)
let timeout = dispatch_time(DISPATCH_TIME_NOW, Int64(5.0 * Double(NSEC_PER_SEC)))
dispatch_after(timeout, dispatch_get_main_queue()) {
if let index = self.channels[channelID]?.usersTyping.indexOf(userID) {
self.channels[channelID]?.usersTyping.removeAtIndex(index)
}
}
}
func channelMarked(event: Event) {
guard let channel = event.channel, id = channel.id, timestamp = event.ts else {
return
}
channels[id]?.lastRead = event.ts
channelEventsDelegate?.channelMarked(channel, timestamp: timestamp)
}
func channelCreated(event: Event) {
guard let channel = event.channel, id = channel.id else {
return
}
channels[id] = channel
channelEventsDelegate?.channelCreated(channel)
}
func channelDeleted(event: Event) {
guard let channel = event.channel, id = channel.id else {
return
}
channels.removeValueForKey(id)
channelEventsDelegate?.channelDeleted(channel)
}
func channelJoined(event: Event) {
guard let channel = event.channel, id = channel.id else {
return
}
channels[id] = event.channel
channelEventsDelegate?.channelJoined(channel)
}
func channelLeft(event: Event) {
guard let channel = event.channel, id = channel.id else {
return
}
if let userID = authenticatedUser?.id, index = channels[id]?.members?.indexOf(userID) {
channels[id]?.members?.removeAtIndex(index)
}
channelEventsDelegate?.channelLeft(channel)
}
func channelRenamed(event: Event) {
guard let channel = event.channel, id = channel.id else {
return
}
channels[id]?.name = channel.name
channelEventsDelegate?.channelRenamed(channel)
}
func channelArchived(event: Event, archived: Bool) {
guard let channel = event.channel, id = channel.id else {
return
}
channels[id]?.isArchived = archived
channelEventsDelegate?.channelArchived(channel)
}
func channelHistoryChanged(event: Event) {
guard let channel = event.channel else {
return
}
channelEventsDelegate?.channelHistoryChanged(channel)
}
//MARK: - Do Not Disturb
func doNotDisturbUpdated(event: Event) {
guard let dndStatus = event.dndStatus else {
return
}
authenticatedUser?.doNotDisturbStatus = dndStatus
doNotDisturbEventsDelegate?.doNotDisturbUpdated(dndStatus)
}
func doNotDisturbUserUpdated(event: Event) {
guard let dndStatus = event.dndStatus, user = event.user, id = user.id else {
return
}
users[id]?.doNotDisturbStatus = dndStatus
doNotDisturbEventsDelegate?.doNotDisturbUserUpdated(dndStatus, user: user)
}
//MARK: - IM & Group Open/Close
func open(event: Event, open: Bool) {
guard let channel = event.channel, id = channel.id else {
return
}
channels[id]?.isOpen = open
groupEventsDelegate?.groupOpened(channel)
}
//MARK: - Files
func processFile(event: Event) {
guard let file = event.file, id = file.id else {
return
}
if let comment = file.initialComment, commentID = comment.id {
if files[id]?.comments[commentID] == nil {
files[id]?.comments[commentID] = comment
}
}
files[id] = file
fileEventsDelegate?.fileProcessed(file)
}
func filePrivate(event: Event) {
guard let file = event.file, id = file.id else {
return
}
files[id]?.isPublic = false
fileEventsDelegate?.fileMadePrivate(file)
}
func deleteFile(event: Event) {
guard let file = event.file, id = file.id else {
return
}
if files[id] != nil {
files.removeValueForKey(id)
}
fileEventsDelegate?.fileDeleted(file)
}
func fileCommentAdded(event: Event) {
guard let file = event.file, id = file.id, comment = event.comment, commentID = comment.id else {
return
}
files[id]?.comments[commentID] = comment
fileEventsDelegate?.fileCommentAdded(file, comment: comment)
}
func fileCommentEdited(event: Event) {
guard let file = event.file, id = file.id, comment = event.comment, commentID = comment.id else {
return
}
files[id]?.comments[commentID]?.comment = comment.comment
fileEventsDelegate?.fileCommentEdited(file, comment: comment)
}
func fileCommentDeleted(event: Event) {
guard let file = event.file, id = file.id, comment = event.comment, commentID = comment.id else {
return
}
files[id]?.comments.removeValueForKey(commentID)
fileEventsDelegate?.fileCommentDeleted(file, comment: comment)
}
//MARK: - Pins
func pinAdded(event: Event) {
guard let id = event.channelID, item = event.item else {
return
}
channels[id]?.pinnedItems.append(item)
pinEventsDelegate?.itemPinned(item, channel: channels[id])
}
func pinRemoved(event: Event) {
guard let id = event.channelID, item = event.item else {
return
}
if let pins = channels[id]?.pinnedItems.filter({$0 != item}) {
channels[id]?.pinnedItems = pins
}
pinEventsDelegate?.itemUnpinned(item, channel: channels[id])
}
//MARK: - Stars
func itemStarred(event: Event, star: Bool) {
guard let item = event.item, type = item.type else {
return
}
switch type {
case "message":
starMessage(item, star: star)
case "file":
starFile(item, star: star)
case "file_comment":
starComment(item)
default:
break
}
starEventsDelegate?.itemStarred(item, star: star)
}
func starMessage(item: Item, star: Bool) {
guard let message = item.message, ts = message.ts, channel = item.channel where channels[channel]?.messages[ts] != nil else {
return
}
channels[channel]?.messages[ts]?.isStarred = star
}
func starFile(item: Item, star: Bool) {
guard let file = item.file, id = file.id else {
return
}
files[id]?.isStarred = star
if let stars = files[id]?.stars {
if star == true {
files[id]?.stars = stars + 1
} else {
if stars > 0 {
files[id]?.stars = stars - 1
}
}
}
}
func starComment(item: Item) {
guard let file = item.file, id = file.id, comment = item.comment, commentID = comment.id else {
return
}
files[id]?.comments[commentID] = comment
}
//MARK: - Reactions
func addedReaction(event: Event) {
guard let item = event.item, type = item.type, reaction = event.reaction, userID = event.user?.id, itemUser = event.itemUser else {
return
}
switch type {
case "message":
guard let channel = item.channel, ts = item.ts, message = channels[channel]?.messages[ts] else {
return
}
message.reactions.append(Reaction(name: reaction, user: userID))
case "file":
guard let id = item.file?.id else {
return
}
files[id]?.reactions.append(Reaction(name: reaction, user: userID))
case "file_comment":
guard let id = item.file?.id, commentID = item.fileCommentID else {
return
}
files[id]?.comments[commentID]?.reactions.append(Reaction(name: reaction, user: userID))
default:
break
}
reactionEventsDelegate?.reactionAdded(reaction, item: item, itemUser: itemUser)
}
func removedReaction(event: Event) {
guard let item = event.item, type = item.type, key = event.reaction, userID = event.user?.id, itemUser = event.itemUser else {
return
}
switch type {
case "message":
guard let channel = item.channel, ts = item.ts, message = channels[channel]?.messages[ts] else {
return
}
message.reactions = message.reactions.filter({$0.name != key && $0.user != userID})
case "file":
guard let itemFile = item.file, id = itemFile.id else {
return
}
files[id]?.reactions = files[id]!.reactions.filter({$0.name != key && $0.user != userID})
case "file_comment":
guard let id = item.file?.id, commentID = item.fileCommentID else {
return
}
files[id]?.comments[commentID]?.reactions = files[id]!.comments[commentID]!.reactions.filter({$0.name != key && $0.user != userID})
default:
break
}
reactionEventsDelegate?.reactionRemoved(key, item: item, itemUser: itemUser)
}
//MARK: - Preferences
func changePreference(event: Event) {
guard let name = event.name else {
return
}
authenticatedUser?.preferences?[name] = event.value
slackEventsDelegate?.preferenceChanged(name, value: event.value)
}
//Mark: - User Change
func userChange(event: Event) {
guard let user = event.user, id = user.id else {
return
}
let preferences = users[id]?.preferences
users[id] = user
users[id]?.preferences = preferences
slackEventsDelegate?.userChanged(user)
}
//MARK: - User Presence
func presenceChange(event: Event) {
guard let user = event.user, id = user.id, presence = event.presence else {
return
}
users[id]?.presence = event.presence
slackEventsDelegate?.presenceChanged(user, presence: presence)
}
//MARK: - Team
func teamJoin(event: Event) {
guard let user = event.user, id = user.id else {
return
}
users[id] = user
teamEventsDelegate?.teamJoined(user)
}
func teamPlanChange(event: Event) {
guard let plan = event.plan else {
return
}
team?.plan = plan
teamEventsDelegate?.teamPlanChanged(plan)
}
func teamPreferenceChange(event: Event) {
guard let name = event.name else {
return
}
team?.prefs?[name] = event.value
teamEventsDelegate?.teamPreferencesChanged(name, value: event.value)
}
func teamNameChange(event: Event) {
guard let name = event.name else {
return
}
team?.name = name
teamEventsDelegate?.teamNameChanged(name)
}
func teamDomainChange(event: Event) {
guard let domain = event.domain else {
return
}
team?.domain = domain
teamEventsDelegate?.teamDomainChanged(domain)
}
func emailDomainChange(event: Event) {
guard let domain = event.emailDomain else {
return
}
team?.emailDomain = domain
teamEventsDelegate?.teamEmailDomainChanged(domain)
}
func emojiChanged(event: Event) {
teamEventsDelegate?.teamEmojiChanged()
}
//MARK: - Bots
func bot(event: Event) {
guard let bot = event.bot, id = bot.id else {
return
}
bots[id] = bot
slackEventsDelegate?.botEvent(bot)
}
//MARK: - Subteams
func subteam(event: Event) {
guard let subteam = event.subteam, id = subteam.id else {
return
}
userGroups[id] = subteam
subteamEventsDelegate?.subteamEvent(subteam)
}
func subteamAddedSelf(event: Event) {
guard let subteamID = event.subteamID, _ = authenticatedUser?.userGroups else {
return
}
authenticatedUser?.userGroups![subteamID] = subteamID
subteamEventsDelegate?.subteamSelfAdded(subteamID)
}
func subteamRemovedSelf(event: Event) {
guard let subteamID = event.subteamID else {
return
}
authenticatedUser?.userGroups?.removeValueForKey(subteamID)
subteamEventsDelegate?.subteamSelfRemoved(subteamID)
}
//MARK: - Team Profiles
func teamProfileChange(event: Event) {
guard let profile = event.profile else {
return
}
for user in users {
for key in profile.fields.keys {
users[user.0]?.profile?.customProfile?.fields[key]?.updateProfileField(profile.fields[key])
}
}
teamProfileEventsDelegate?.teamProfileChanged(profile)
}
func teamProfileDeleted(event: Event) {
guard let profile = event.profile else {
return
}
for user in users {
if let id = profile.fields.first?.0 {
users[user.0]?.profile?.customProfile?.fields[id] = nil
}
}
teamProfileEventsDelegate?.teamProfileDeleted(profile)
}
func teamProfileReordered(event: Event) {
guard let profile = event.profile else {
return
}
for user in users {
for key in profile.fields.keys {
users[user.0]?.profile?.customProfile?.fields[key]?.ordering = profile.fields[key]?.ordering
}
}
teamProfileEventsDelegate?.teamProfileReordered(profile)
}
//MARK: - Authenticated User
func manualPresenceChange(event: Event) {
guard let presence = event.presence, user = authenticatedUser else {
return
}
authenticatedUser?.presence = presence
slackEventsDelegate?.manualPresenceChanged(user, presence: presence)
}
}
@@ -1,5 +1,5 @@
//
// ClientExtensions.swift
// Client+Utilities.swift
//
// Copyright © 2016 Peter Zignego. All rights reserved.
//
@@ -23,17 +23,28 @@
import Foundation
extension Client {
public enum ClientError: ErrorType {
case ChannelDoesNotExist
case UserDoesNotExist
}
public extension Client {
//MARK: - User & Channel
public func getChannelIDByName(name: String) -> String? {
return channels.filter{$0.1.name == stripString(name)}.first?.0
public func getChannelIDByName(name: String) throws -> String {
guard let id = channels.filter({$0.1.name == stripString(name)}).first?.0 else {
throw ClientError.ChannelDoesNotExist
}
return id
}
public func getUserIDByName(name: String) -> String? {
return users.filter{$0.1.name == stripString(name)}.first?.0
public func getUserIDByName(name: String) throws -> String {
guard let id = users.filter({$0.1.name == stripString(name)}).first?.0 else {
throw ClientError.UserDoesNotExist
}
return id
}
public func getImIDForUserWithID(id: String, success: (imID: String?)->Void, failure: (error: SlackError)->Void) {
let ims = channels.filter{$0.1.isIM == true}
let channel = ims.filter{$0.1.user == id}.first
@@ -43,9 +54,9 @@ extension Client {
webAPI.openIM(id, success: success, failure: failure)
}
}
//MARK: - Utilities
internal func stripString(string: String) -> String? {
internal func stripString(string: String) -> String {
var strippedString = string
if string[string.startIndex] == "@" || string[string.startIndex] == "#" {
strippedString = string.substringFromIndex(string.startIndex.advancedBy(1))
@@ -53,44 +64,3 @@ extension Client {
return strippedString
}
}
public enum AttachmentColor: String {
case Good = "good"
case Warning = "warning"
case Danger = "danger"
}
public extension NSDate {
func slackTimestamp() -> Double {
return NSNumber(double: timeIntervalSince1970).doubleValue
}
}
internal extension String {
func slackFormatEscaping() -> String {
var escapedString = stringByReplacingOccurrencesOfString("&", withString: "&amp;")
escapedString = stringByReplacingOccurrencesOfString("<", withString: "&lt;")
escapedString = stringByReplacingOccurrencesOfString(">", withString: "&gt;")
return escapedString
}
}
internal extension Array {
func objectArrayFromDictionaryArray<T>(intializer:([String: AnyObject])->T?) -> [T] {
var returnValue = [T]()
for object in self {
if let dictionary = object as? [String: AnyObject] {
if let value = intializer(dictionary) {
returnValue.append(value)
}
}
}
return returnValue
}
}
+55 -67
View File
@@ -27,7 +27,6 @@ import Starscream
public class Client: WebSocketDelegate {
internal(set) public var connected = false
internal(set) public var authenticated = false
internal(set) public var authenticatedUser: User?
internal(set) public var team: Team?
@@ -39,18 +38,18 @@ public class Client: WebSocketDelegate {
internal(set) public var sentMessages = [String: Message]()
//MARK: - Delegates
public var slackEventsDelegate: SlackEventsDelegate?
public var messageEventsDelegate: MessageEventsDelegate?
public var doNotDisturbEventsDelegate: DoNotDisturbEventsDelegate?
public var channelEventsDelegate: ChannelEventsDelegate?
public var groupEventsDelegate: GroupEventsDelegate?
public var fileEventsDelegate: FileEventsDelegate?
public var pinEventsDelegate: PinEventsDelegate?
public var starEventsDelegate: StarEventsDelegate?
public var reactionEventsDelegate: ReactionEventsDelegate?
public var teamEventsDelegate: TeamEventsDelegate?
public var subteamEventsDelegate: SubteamEventsDelegate?
public var teamProfileEventsDelegate: TeamProfileEventsDelegate?
public weak var slackEventsDelegate: SlackEventsDelegate?
public weak var messageEventsDelegate: MessageEventsDelegate?
public weak var doNotDisturbEventsDelegate: DoNotDisturbEventsDelegate?
public weak var channelEventsDelegate: ChannelEventsDelegate?
public weak var groupEventsDelegate: GroupEventsDelegate?
public weak var fileEventsDelegate: FileEventsDelegate?
public weak var pinEventsDelegate: PinEventsDelegate?
public weak var starEventsDelegate: StarEventsDelegate?
public weak var reactionEventsDelegate: ReactionEventsDelegate?
public weak var teamEventsDelegate: TeamEventsDelegate?
public weak var subteamEventsDelegate: SubteamEventsDelegate?
public weak var teamProfileEventsDelegate: TeamProfileEventsDelegate?
public var token = "SLACK_AUTH_TOKEN"
@@ -64,8 +63,7 @@ public class Client: WebSocketDelegate {
internal var webSocket: WebSocket?
internal let api = NetworkInterface()
private var dispatcher: EventDispatcher?
private let pingPongQueue = dispatch_queue_create("com.launchsoft.SlackKit", DISPATCH_QUEUE_SERIAL)
internal var ping: Double?
internal var pong: Double?
@@ -82,7 +80,6 @@ public class Client: WebSocketDelegate {
self.pingInterval = pingInterval
self.timeout = timeout
self.reconnect = reconnect
dispatcher = EventDispatcher(client: self)
webAPI.rtmStart(simpleLatest, noUnreads: noUnreads, mpimAware: mpimAware, success: {
(response) -> Void in
self.initialSetup(response)
@@ -103,16 +100,15 @@ public class Client: WebSocketDelegate {
//MARK: - RTM Message send
public func sendMessage(message: String, channelID: String) {
if (connected) {
if let data = formatMessageToSlackJsonString(msg: message, channel: channelID) {
if let string = NSString(data: data, encoding: NSUTF8StringEncoding) as? String {
webSocket?.writeString(string)
}
}
guard connected else { return }
if let data = try? formatMessageToSlackJsonString(msg: message, channel: channelID),
string = NSString(data: data, encoding: NSUTF8StringEncoding) as? String {
webSocket?.writeString(string)
}
}
private func formatMessageToSlackJsonString(message: (msg: String, channel: String)) -> NSData? {
private func formatMessageToSlackJsonString(message: (msg: String, channel: String)) throws -> NSData {
let json: [String: AnyObject] = [
"id": NSDate().slackTimestamp(),
"type": "message",
@@ -120,54 +116,49 @@ public class Client: WebSocketDelegate {
"text": message.msg.slackFormatEscaping()
]
addSentMessage(json)
do {
let data = try NSJSONSerialization.dataWithJSONObject(json, options: NSJSONWritingOptions.PrettyPrinted)
return data
}
catch _ {
return nil
}
return try NSJSONSerialization.dataWithJSONObject(json, options: [])
}
private func addSentMessage(dictionary: [String: AnyObject]) {
var message = dictionary
let ts = message["id"] as? NSNumber
guard let id = message["id"] as? NSNumber else {
return
}
let ts = String(id)
message.removeValueForKey("id")
message["ts"] = ts?.stringValue
message["ts"] = ts
message["user"] = self.authenticatedUser?.id
sentMessages[ts!.stringValue] = Message(message: message)
sentMessages[ts] = 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 {
guard self.connected && self.timeoutCheck() else {
self.disconnect()
return
}
self.sendRTMPing()
self.pingRTMServerAtInterval(interval)
})
}
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 _ {
}
guard connected else {
return
}
let json: [String: AnyObject] = [
"id": NSDate().slackTimestamp(),
"type": "ping",
]
guard let data = try? NSJSONSerialization.dataWithJSONObject(json, options: []) else {
return
}
let string = NSString(data: data, encoding: NSUTF8StringEncoding)
if let writePing = string as? String {
ping = json["id"] as? Double
webSocket?.writeString(writePing)
}
}
@@ -199,19 +190,22 @@ public class Client: WebSocketDelegate {
}
private func addUser(aUser: [String: AnyObject]) {
if let user = User(user: aUser), id = user.id {
let user = User(user: aUser)
if let id = user.id {
users[id] = user
}
}
private func addChannel(aChannel: [String: AnyObject]) {
if let channel = Channel(channel: aChannel), id = channel.id {
let channel = Channel(channel: aChannel)
if let id = channel.id {
channels[id] = channel
}
}
private func addBot(aBot: [String: AnyObject]) {
if let bot = Bot(bot: aBot), id = bot.id {
let bot = Bot(bot: aBot)
if let id = bot.id {
bots[id] = bot
}
}
@@ -221,7 +215,7 @@ public class Client: WebSocketDelegate {
if let all = subteams["all"] as? [[String: AnyObject]] {
for item in all {
let u = UserGroup(userGroup: item)
self.userGroups[u!.id!] = u
self.userGroups[u.id!] = u
}
}
if let auth = subteams["self"] as? [String] {
@@ -253,9 +247,7 @@ public class Client: WebSocketDelegate {
public func websocketDidDisconnect(socket: WebSocket, error: NSError?) {
connected = false
authenticated = false
webSocket = nil
dispatcher = nil
authenticatedUser = nil
slackEventsDelegate?.clientDisconnected()
if reconnect == true {
@@ -267,16 +259,12 @@ public class Client: WebSocketDelegate {
guard let data = text.dataUsingEncoding(NSUTF8StringEncoding) else {
return
}
do {
if let json = try NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions.AllowFragments) as? [String: AnyObject] {
dispatcher?.dispatch(json)
}
}
catch _ {
if let json = (try? NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions.AllowFragments)) as? [String: AnyObject] {
dispatch(json)
}
}
public func websocketDidReceiveData(socket: WebSocket, data: NSData) {}
}
+15 -19
View File
@@ -1,5 +1,5 @@
//
// Message.swift
// Event.swift
//
// Copyright © 2016 Peter Zignego. All rights reserved.
//
@@ -197,31 +197,27 @@ internal struct Event {
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) {
file = File(id: event["file"] as? String)
// Comment, Channel, and User can come across as Strings or Dictionaries
if let commentDictionary = event["comment"] as? [String: AnyObject] {
comment = Comment(comment: commentDictionary)
} else {
comment = Comment(id: event["comment"] as? String)
} else {
comment = Comment(comment: event["comment"] as? [String: AnyObject])
}
if (User(user: event["user"] as? [String: AnyObject])?.id == nil) {
if let userDictionary = event["user"] as? [String: AnyObject] {
user = User(user: userDictionary)
} else {
user = User(id: event["user"] as? String)
} else {
user = User(user: event["user"] as? [String: AnyObject])
}
if (File(file: event["file"] as? [String: AnyObject])?.id == nil) {
file = File(id: event["file"] as? String)
if let channelDictionary = event["channel"] as? [String: AnyObject] {
channel = Channel(channel: channelDictionary)
} else {
file = File(file: event["file"] as? [String: AnyObject])
}
if (Channel(channel: event["channel"] as? [String: AnyObject])?.id == nil) {
channel = Channel(id: event["channel"] as? String)
} else {
channel = Channel(channel: event["channel"] as? [String: AnyObject])
}
}
}
+26 -26
View File
@@ -23,27 +23,27 @@
import Foundation
public protocol SlackEventsDelegate {
public protocol SlackEventsDelegate: class {
func clientConnectionFailed(error: SlackError)
func clientConnected()
func clientDisconnected()
func preferenceChanged(preference: String, value: AnyObject)
func preferenceChanged(preference: String, value: AnyObject?)
func userChanged(user: User)
func presenceChanged(user: User?, presence: String?)
func manualPresenceChanged(user: User?, presence: String?)
func presenceChanged(user: User, presence: String)
func manualPresenceChanged(user: User, presence: String)
func botEvent(bot: Bot)
}
public protocol MessageEventsDelegate {
public protocol MessageEventsDelegate: class {
func messageSent(message: Message)
func messageReceived(message: Message)
func messageChanged(message: Message)
func messageDeleted(message: Message?)
}
public protocol ChannelEventsDelegate {
func userTyping(channel: Channel?, user: User?)
func channelMarked(channel: Channel, timestamp: String?)
public protocol ChannelEventsDelegate: class {
func userTyping(channel: Channel, user: User)
func channelMarked(channel: Channel, timestamp: String)
func channelCreated(channel: Channel)
func channelDeleted(channel: Channel)
func channelRenamed(channel: Channel)
@@ -53,16 +53,16 @@ public protocol ChannelEventsDelegate {
func channelLeft(channel: Channel)
}
public protocol DoNotDisturbEventsDelegate {
public protocol DoNotDisturbEventsDelegate: class {
func doNotDisturbUpdated(dndStatus: DoNotDisturbStatus)
func doNotDisturbUserUpdated(dndStatus: DoNotDisturbStatus, user: User?)
func doNotDisturbUserUpdated(dndStatus: DoNotDisturbStatus, user: User)
}
public protocol GroupEventsDelegate {
public protocol GroupEventsDelegate: class {
func groupOpened(group: Channel)
}
public protocol FileEventsDelegate {
public protocol FileEventsDelegate: class {
func fileProcessed(file: File)
func fileMadePrivate(file: File)
func fileDeleted(file: File)
@@ -71,38 +71,38 @@ public protocol FileEventsDelegate {
func fileCommentDeleted(file: File, comment: Comment)
}
public protocol PinEventsDelegate {
func itemPinned(item: Item?, channel: Channel?)
func itemUnpinned(item: Item?, channel: Channel?)
public protocol PinEventsDelegate: class {
func itemPinned(item: Item, channel: Channel?)
func itemUnpinned(item: Item, channel: Channel?)
}
public protocol StarEventsDelegate {
public protocol StarEventsDelegate: class {
func itemStarred(item: Item, star: Bool)
}
public protocol ReactionEventsDelegate {
func reactionAdded(reaction: String?, item: Item?, itemUser: String?)
func reactionRemoved(reaction: String?, item: Item?, itemUser: String?)
public protocol ReactionEventsDelegate: class {
func reactionAdded(reaction: String, item: Item, itemUser: String)
func reactionRemoved(reaction: String, item: Item, itemUser: String)
}
public protocol TeamEventsDelegate {
public protocol TeamEventsDelegate: class {
func teamJoined(user: User)
func teamPlanChanged(plan: String)
func teamPreferencesChanged(preference: String, value: AnyObject)
func teamPreferencesChanged(preference: String, value: AnyObject?)
func teamNameChanged(name: String)
func teamDomainChanged(domain: String)
func teamEmailDomainChanged(domain: String)
func teamEmojiChanged()
}
public protocol SubteamEventsDelegate {
public protocol SubteamEventsDelegate: class {
func subteamEvent(userGroup: UserGroup)
func subteamSelfAdded(subteamID: String)
func subteamSelfRemoved(subteamID: String)
}
public protocol TeamProfileEventsDelegate {
func teamProfileChanged(profile: CustomProfile?)
func teamProfileDeleted(profile: CustomProfile?)
func teamProfileReordered(profile: CustomProfile?)
public protocol TeamProfileEventsDelegate: class {
func teamProfileChanged(profile: CustomProfile)
func teamProfileDeleted(profile: CustomProfile)
func teamProfileReordered(profile: CustomProfile)
}
-180
View File
@@ -1,180 +0,0 @@
//
// EventDispatcher.swift
//
// Copyright © 2016 Peter Zignego. All rights reserved.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
internal class EventDispatcher {
let client: Client
let handler: EventHandler
required init(client: Client) {
self.client = client
handler = EventHandler(client: client)
}
func dispatch(event: [String: AnyObject]) {
let event = Event(event: event)
if let type = event.type {
switch type {
case .Hello:
handler.connected()
case .Ok:
handler.messageSent(event)
case .Message:
if (event.subtype != nil) {
messageDispatcher(event)
} else {
handler.messageReceived(event)
}
case .UserTyping:
handler.userTyping(event)
case .ChannelMarked, .IMMarked, .GroupMarked:
handler.channelMarked(event)
case .ChannelCreated, .IMCreated:
handler.channelCreated(event)
case .ChannelJoined, .GroupJoined:
handler.channelJoined(event)
case .ChannelLeft, .GroupLeft:
handler.channelLeft(event)
case .ChannelDeleted:
handler.channelDeleted(event)
case .ChannelRenamed, .GroupRename:
handler.channelRenamed(event)
case .ChannelArchive, .GroupArchive:
handler.channelArchived(event, archived: true)
case .ChannelUnarchive, .GroupUnarchive:
handler.channelArchived(event, archived: false)
case .ChannelHistoryChanged, .IMHistoryChanged, .GroupHistoryChanged:
handler.channelHistoryChanged(event)
case .DNDUpdated:
handler.doNotDisturbUpdated(event)
case .DNDUpatedUser:
handler.doNotDisturbUserUpdated(event)
case .IMOpen, .GroupOpen:
handler.open(event, open: true)
case .IMClose, .GroupClose:
handler.open(event, open: false)
case .FileCreated:
handler.processFile(event)
case .FileShared:
handler.processFile(event)
case .FileUnshared:
handler.processFile(event)
case .FilePublic:
handler.processFile(event)
case .FilePrivate:
handler.filePrivate(event)
case .FileChanged:
handler.processFile(event)
case .FileDeleted:
handler.deleteFile(event)
case .FileCommentAdded:
handler.fileCommentAdded(event)
case .FileCommentEdited:
handler.fileCommentEdited(event)
case .FileCommentDeleted:
handler.fileCommentDeleted(event)
case .PinAdded:
handler.pinAdded(event)
case .PinRemoved:
handler.pinRemoved(event)
case .Pong:
handler.pong(event)
case .PresenceChange:
handler.presenceChange(event)
case .ManualPresenceChange:
handler.manualPresenceChange(event)
case .PrefChange:
handler.changePreference(event)
case .UserChange:
handler.userChange(event)
case .TeamJoin:
handler.teamJoin(event)
case .StarAdded:
handler.itemStarred(event, star: true)
case .StarRemoved:
handler.itemStarred(event, star: false)
case .ReactionAdded:
handler.addedReaction(event)
case .ReactionRemoved:
handler.removedReaction(event)
case .EmojiChanged:
handler.emojiChanged(event)
case .CommandsChanged:
// 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)
case .TeamPrefChange:
handler.teamPreferenceChange(event)
case .TeamRename:
handler.teamNameChange(event)
case .TeamDomainChange:
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:
// 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(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:
handler.subteamAddedSelf(event)
case.SubteamSelfRemoved:
handler.subteamRemovedSelf(event)
case .Error:
print("Error: \(event)")
break
}
}
}
func messageDispatcher(event:Event) {
let subtype = MessageSubtype(rawValue: event.subtype!)!
switch subtype {
case .MessageChanged:
handler.messageChanged(event)
case .MessageDeleted:
handler.messageDeleted(event)
default:
handler.messageReceived(event)
}
}
}
-649
View File
@@ -1,649 +0,0 @@
//
// EventHandler.swift
//
// Copyright © 2016 Peter Zignego. All rights reserved.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
import Foundation
internal class EventHandler {
let client: Client
required init(client: Client) {
self.client = client
}
//MARK: - Initial connection
func connected() {
client.connected = true
if let delegate = client.slackEventsDelegate {
delegate.clientConnected()
}
}
//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
message.text = event.text
client.channels[channel]?.messages[ts] = message
if let delegate = client.messageEventsDelegate {
delegate.messageSent(message)
}
}
}
func messageReceived(event: Event) {
if let channel = event.channel, message = event.message, id = channel.id, ts = message.ts {
client.channels[id]?.messages[ts] = message
if let delegate = client.messageEventsDelegate {
delegate.messageReceived(message)
}
}
}
func messageChanged(event: Event) {
if let id = event.channel?.id, nested = event.nestedMessage, ts = nested.ts {
client.channels[id]?.messages[ts] = nested
if let delegate = client.messageEventsDelegate {
delegate.messageChanged(nested)
}
}
}
func messageDeleted(event: Event) {
if let id = event.channel?.id, key = event.message?.deletedTs {
let message = client.channels[id]?.messages[key]
client.channels[id]?.messages.removeValueForKey(key)
if let delegate = client.messageEventsDelegate {
delegate.messageDeleted(message)
}
}
}
//MARK: - Channels
func userTyping(event: Event) {
if let channelID = event.channel?.id, userID = event.user?.id {
if let _ = client.channels[channelID] {
if (!client.channels[channelID]!.usersTyping.contains(userID)) {
client.channels[channelID]?.usersTyping.append(userID)
if let delegate = client.channelEventsDelegate {
delegate.userTyping(event.channel, user: event.user)
}
}
}
let timeout = dispatch_time(DISPATCH_TIME_NOW, Int64(5.0 * Double(NSEC_PER_SEC)))
dispatch_after(timeout, dispatch_get_main_queue()) {
if let index = self.client.channels[channelID]?.usersTyping.indexOf(userID) {
self.client.channels[channelID]?.usersTyping.removeAtIndex(index)
}
}
}
}
func channelMarked(event: Event) {
if let channel = event.channel, id = channel.id {
client.channels[id]?.lastRead = event.ts
if let delegate = client.channelEventsDelegate {
delegate.channelMarked(channel, timestamp: event.ts)
}
}
//TODO: Recalculate unreads
}
func channelCreated(event: Event) {
if let channel = event.channel, id = channel.id {
client.channels[id] = channel
if let delegate = client.channelEventsDelegate {
delegate.channelCreated(channel)
}
}
}
func channelDeleted(event: Event) {
if let channel = event.channel, id = channel.id {
client.channels.removeValueForKey(id)
if let delegate = client.channelEventsDelegate {
delegate.channelDeleted(channel)
}
}
}
func channelJoined(event: Event) {
if let channel = event.channel, id = channel.id {
client.channels[id] = event.channel
if let delegate = client.channelEventsDelegate {
delegate.channelJoined(channel)
}
}
}
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 delegate = client.channelEventsDelegate {
delegate.channelLeft(channel)
}
}
}
}
func channelRenamed(event: Event) {
if let channel = event.channel, id = channel.id {
client.channels[id]?.name = channel.name
if let delegate = client.channelEventsDelegate {
delegate.channelRenamed(channel)
}
}
}
func channelArchived(event: Event, archived: Bool) {
if let channel = event.channel, id = channel.id {
client.channels[id]?.isArchived = archived
if let delegate = client.channelEventsDelegate {
delegate.channelArchived(channel)
}
}
}
func channelHistoryChanged(event: Event) {
if let channel = event.channel {
//TODO: Reload chat history if there are any cached messages before latest
if let delegate = client.channelEventsDelegate {
delegate.channelHistoryChanged(channel)
}
}
}
//MARK: - Do Not Disturb
func doNotDisturbUpdated(event: Event) {
if let dndStatus = event.dndStatus {
client.authenticatedUser?.doNotDisturbStatus = dndStatus
if let delegate = client.doNotDisturbEventsDelegate {
delegate.doNotDisturbUpdated(dndStatus)
}
}
}
func doNotDisturbUserUpdated(event: Event) {
if let dndStatus = event.dndStatus, user = event.user, id = user.id {
client.users[id]?.doNotDisturbStatus = dndStatus
if let delegate = client.doNotDisturbEventsDelegate {
delegate.doNotDisturbUserUpdated(dndStatus, user: user)
}
}
}
//MARK: - IM & Group Open/Close
func open(event: Event, open: Bool) {
if let channel = event.channel, id = channel.id {
client.channels[id]?.isOpen = open
if let delegate = client.groupEventsDelegate {
delegate.groupOpened(channel)
}
}
}
//MARK: - Files
func processFile(event: Event) {
if let file = event.file, id = file.id {
if let comment = file.initialComment, commentID = comment.id {
if client.files[id]?.comments[commentID] == nil {
client.files[id]?.comments[commentID] = comment
}
}
client.files[id] = file
if let delegate = client.fileEventsDelegate {
delegate.fileProcessed(file)
}
}
}
func filePrivate(event: Event) {
if let file = event.file, id = file.id {
client.files[id]?.isPublic = false
if let delegate = client.fileEventsDelegate {
delegate.fileMadePrivate(file)
}
}
}
func deleteFile(event: Event) {
if let file = event.file, id = file.id {
if client.files[id] != nil {
client.files.removeValueForKey(id)
}
if let delegate = client.fileEventsDelegate {
delegate.fileDeleted(file)
}
}
}
func fileCommentAdded(event: Event) {
if let file = event.file, id = file.id, comment = event.comment, commentID = comment.id {
client.files[id]?.comments[commentID] = comment
if let delegate = client.fileEventsDelegate {
delegate.fileCommentAdded(file, comment: comment)
}
}
}
func fileCommentEdited(event: Event) {
if let file = event.file, id = file.id, comment = event.comment, commentID = comment.id {
client.files[id]?.comments[commentID]?.comment = comment.comment
if let delegate = client.fileEventsDelegate {
delegate.fileCommentEdited(file, comment: comment)
}
}
}
func fileCommentDeleted(event: Event) {
if let file = event.file, id = file.id, comment = event.comment, commentID = comment.id {
client.files[id]?.comments.removeValueForKey(commentID)
if let delegate = client.fileEventsDelegate {
delegate.fileCommentDeleted(file, comment: comment)
}
}
}
//MARK: - Pins
func pinAdded(event: Event) {
if let id = event.channelID, item = event.item {
client.channels[id]?.pinnedItems.append(item)
if let delegate = client.pinEventsDelegate {
delegate.itemPinned(item, channel: client.channels[id])
}
}
}
func pinRemoved(event: Event) {
if let id = event.channelID {
if let pins = client.channels[id]?.pinnedItems.filter({$0 != event.item}) {
client.channels[id]?.pinnedItems = pins
}
if let delegate = client.pinEventsDelegate {
delegate.itemUnpinned(event.item, channel: client.channels[id])
}
}
}
//MARK: - Stars
func itemStarred(event: Event, star: Bool) {
if let item = event.item, type = item.type {
switch type {
case "message":
starMessage(item, star: star)
case "file":
starFile(item, star: star)
case "file_comment":
starComment(item)
default:
break
}
if let delegate = client.starEventsDelegate {
delegate.itemStarred(item, star: star)
}
}
}
func starMessage(item: Item, star: Bool) {
if let message = item.message, ts = message.ts, channel = item.channel {
if let _ = client.channels[channel]?.messages[ts] {
client.channels[channel]?.messages[ts]?.isStarred = star
}
}
}
func starFile(item: Item, star: Bool) {
if let file = item.file, id = file.id {
client.files[id]?.isStarred = star
if let stars = client.files[id]?.stars {
if star == true {
client.files[id]?.stars = stars + 1
} else {
if stars > 0 {
client.files[id]?.stars = stars - 1
}
}
}
}
}
func starComment(item: Item) {
if let file = item.file, id = file.id, comment = item.comment, commentID = comment.id {
client.files[id]?.comments[commentID] = comment
}
}
//MARK: - Reactions
func addedReaction(event: Event) {
if let item = event.item, type = item.type, key = event.reaction, userID = event.user?.id {
switch type {
case "message":
if let channel = item.channel, ts = item.ts {
if let message = client.channels[channel]?.messages[ts] {
if (message.reactions[key]) == nil {
message.reactions[key] = Reaction(name: event.reaction, user: userID)
} else {
message.reactions[key]?.users[userID] = userID
}
}
}
case "file":
if let id = item.file?.id, file = client.files[id] {
if file.reactions[key] == nil {
client.files[id]?.reactions[key] = Reaction(name: event.reaction, user: userID)
} else {
client.files[id]?.reactions[key]?.users[userID] = userID
}
}
case "file_comment":
if let id = item.file?.id, file = client.files[id], commentID = item.fileCommentID {
if file.comments[commentID]?.reactions[key] == nil {
client.files[id]?.comments[commentID]?.reactions[key] = Reaction(name: event.reaction, user: userID)
} else {
client.files[id]?.comments[commentID]?.reactions[key]?.users[userID] = userID
}
}
break
default:
break
}
if let delegate = client.reactionEventsDelegate {
delegate.reactionAdded(event.reaction, item: event.item, itemUser: event.itemUser)
}
}
}
func removedReaction(event: Event) {
if let item = event.item, type = item.type, key = event.reaction, userID = event.user?.id {
switch type {
case "message":
if let channel = item.channel, ts = item.ts {
if let message = client.channels[channel]?.messages[ts] {
if (message.reactions[key]) != nil {
message.reactions[key]?.users.removeValueForKey(userID)
}
if (message.reactions[key]?.users.count == 0) {
message.reactions.removeValueForKey(key)
}
}
}
case "file":
if let itemFile = item.file, id = itemFile.id, file = client.files[id] {
if file.reactions[key] != nil {
client.files[id]?.reactions[key]?.users.removeValueForKey(userID)
}
if client.files[id]?.reactions[key]?.users.count == 0 {
client.files[id]?.reactions.removeValueForKey(key)
}
}
case "file_comment":
if let id = item.file?.id, file = client.files[id], commentID = item.fileCommentID {
if file.comments[commentID]?.reactions[key] != nil {
client.files[id]?.comments[commentID]?.reactions[key]?.users.removeValueForKey(userID)
}
if client.files[id]?.comments[commentID]?.reactions[key]?.users.count == 0 {
client.files[id]?.comments[commentID]?.reactions.removeValueForKey(key)
}
}
break
default:
break
}
if let delegate = client.reactionEventsDelegate {
delegate.reactionAdded(event.reaction, item: event.item, itemUser: event.itemUser)
}
}
}
//MARK: - Preferences
func changePreference(event: Event) {
if let name = event.name {
client.authenticatedUser?.preferences?[name] = event.value
if let delegate = client.slackEventsDelegate, value = event.value {
delegate.preferenceChanged(name, value: value)
}
}
}
//Mark: - User Change
func userChange(event: Event) {
if let user = event.user, id = user.id {
let preferences = client.users[id]?.preferences
client.users[id] = user
client.users[id]?.preferences = preferences
if let delegate = client.slackEventsDelegate {
delegate.userChanged(user)
}
}
}
//MARK: - User Presence
func presenceChange(event: Event) {
if let user = event.user, id = user.id {
client.users[id]?.presence = event.presence
if let delegate = client.slackEventsDelegate {
delegate.presenceChanged(user, presence: event.presence)
}
}
}
//MARK: - Team
func teamJoin(event: Event) {
if let user = event.user, id = user.id {
client.users[id] = user
if let delegate = client.teamEventsDelegate {
delegate.teamJoined(user)
}
}
}
func teamPlanChange(event: Event) {
if let plan = event.plan {
client.team?.plan = plan
if let delegate = client.teamEventsDelegate {
delegate.teamPlanChanged(plan)
}
}
}
func teamPreferenceChange(event: Event) {
if let name = event.name {
client.team?.prefs?[name] = event.value
if let delegate = client.teamEventsDelegate, value = event.value {
delegate.teamPreferencesChanged(name, value: value)
}
}
}
func teamNameChange(event: Event) {
if let name = event.name {
client.team?.name = name
if let delegate = client.teamEventsDelegate {
delegate.teamNameChanged(name)
}
}
}
func teamDomainChange(event: Event) {
if let domain = event.domain {
client.team?.domain = domain
if let delegate = client.teamEventsDelegate {
delegate.teamDomainChanged(domain)
}
}
}
func emailDomainChange(event: Event) {
if let domain = event.emailDomain {
client.team?.emailDomain = domain
if let delegate = client.teamEventsDelegate {
delegate.teamEmailDomainChanged(domain)
}
}
}
func emojiChanged(event: Event) {
//TODO: Call emoji.list here
if let delegate = client.teamEventsDelegate {
delegate.teamEmojiChanged()
}
}
//MARK: - Bots
func bot(event: Event) {
if let bot = event.bot, id = bot.id {
client.bots[id] = bot
if let delegate = client.slackEventsDelegate {
delegate.botEvent(bot)
}
}
}
//MARK: - Subteams
func subteam(event: Event) {
if let subteam = event.subteam, id = subteam.id {
client.userGroups[id] = subteam
if let delegate = client.subteamEventsDelegate {
delegate.subteamEvent(subteam)
}
}
}
func subteamAddedSelf(event: Event) {
if let subteamID = event.subteamID, _ = client.authenticatedUser?.userGroups {
client.authenticatedUser?.userGroups![subteamID] = subteamID
if let delegate = client.subteamEventsDelegate {
delegate.subteamSelfAdded(subteamID)
}
}
}
func subteamRemovedSelf(event: Event) {
if let subteamID = event.subteamID {
client.authenticatedUser?.userGroups?.removeValueForKey(subteamID)
if let delegate = client.subteamEventsDelegate {
delegate.subteamSelfRemoved(subteamID)
}
}
}
//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
if let delegate = client.slackEventsDelegate {
delegate.manualPresenceChanged(client.authenticatedUser, presence: event.presence)
}
}
}
@@ -1,6 +1,5 @@
//
// AppDelegate.swift
// OSX-Sample
// Extensions.swift
//
// Copyright © 2016 Peter Zignego. All rights reserved.
//
@@ -22,23 +21,23 @@
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
import Cocoa
import SlackKit
import Foundation
@NSApplicationMain
class AppDelegate: NSObject, NSApplicationDelegate {
public extension NSDate {
@IBOutlet weak var window: NSWindow!
let leaderboard = Leaderboard(token: "SLACK_AUTH_TOKEN")
func applicationDidFinishLaunching(aNotification: NSNotification) {
leaderboard.client.connect()
}
func applicationWillTerminate(aNotification: NSNotification) {
// Insert code here to tear down your application
func slackTimestamp() -> Double {
return NSNumber(double: timeIntervalSince1970).doubleValue
}
}
internal extension String {
func slackFormatEscaping() -> String {
var escapedString = stringByReplacingOccurrencesOfString("&", withString: "&amp;")
escapedString = stringByReplacingOccurrencesOfString("<", withString: "&lt;")
escapedString = stringByReplacingOccurrencesOfString(">", withString: "&gt;")
return escapedString
}
}
+52 -7
View File
@@ -44,6 +44,22 @@ public struct File {
public let thumb360gif: String?
public let thumb360w: String?
public let thumb360h: String?
public let thumb480: String?
public let thumb480gif: String?
public let thumb480w: String?
public let thumb480h: String?
public let thumb720: String?
public let thumb720gif: String?
public let thumb720w: String?
public let thumb720h: String?
public let thumb960: String?
public let thumb960gif: String?
public let thumb960w: String?
public let thumb960h: String?
public let thumb1024: String?
public let thumb1024gif: String?
public let thumb1024w: String?
public let thumb1024h: String?
public let permalink: String?
public let editLink: String?
public let preview: String?
@@ -60,9 +76,9 @@ public struct File {
internal(set) public var isStarred: Bool?
internal(set) public var pinnedTo: [String]?
internal(set) public var comments = [String: Comment]()
internal(set) public var reactions = [String: Reaction]()
internal(set) public var reactions = [Reaction]()
public init?(file:[String: AnyObject]?) {
public init(file:[String: AnyObject]?) {
id = file?["id"] as? String
created = file?["created"] as? Int
name = file?["name"] as? String
@@ -84,6 +100,22 @@ public struct File {
thumb360gif = file?["thumb_360_gif"] as? String
thumb360w = file?["thumb_360_w"] as? String
thumb360h = file?["thumb_360_h"] as? String
thumb480 = file?["thumb_480"] as? String
thumb480gif = file?["thumb_480_gif"] as? String
thumb480w = file?["thumb_480_w"] as? String
thumb480h = file?["thumb_480_h"] as? String
thumb720 = file?["thumb_720"] as? String
thumb720gif = file?["thumb_720_gif"] as? String
thumb720w = file?["thumb_720_w"] as? String
thumb720h = file?["thumb_720_h"] as? String
thumb960 = file?["thumb_960"] as? String
thumb960gif = file?["thumb_960_gif"] as? String
thumb960w = file?["thumb_960_w"] as? String
thumb960h = file?["thumb_960_h"] as? String
thumb1024 = file?["thumb_1024"] as? String
thumb1024gif = file?["thumb_1024_gif"] as? String
thumb1024w = file?["thumb_1024_w"] as? String
thumb1024h = file?["thumb_1024_h"] as? String
permalink = file?["permalink"] as? String
editLink = file?["edit_link"] as? String
preview = file?["preview"] as? String
@@ -99,13 +131,10 @@ public struct File {
stars = file?["num_stars"] as? Int
isStarred = file?["is_starred"] as? Bool
pinnedTo = file?["pinned_to"] as? [String]
if let reactions = file?["reactions"] as? [[String: AnyObject]] {
self.reactions = Reaction.reactionsFromArray(reactions)
}
reactions = Reaction.reactionsFromArray(file?["reactions"] as? [[String: AnyObject]])
}
internal init?(id:String?) {
internal init(id:String?) {
self.id = id
created = nil
name = nil
@@ -126,6 +155,22 @@ public struct File {
thumb360gif = nil
thumb360w = nil
thumb360h = nil
thumb480 = nil
thumb480gif = nil
thumb480w = nil
thumb480h = nil
thumb720 = nil
thumb720gif = nil
thumb720w = nil
thumb720h = nil
thumb960 = nil
thumb960gif = nil
thumb960w = nil
thumb960h = nil
thumb1024 = nil
thumb1024gif = nil
thumb1024w = nil
thumb1024h = nil
permalink = nil
editLink = nil
preview = nil
+6 -17
View File
@@ -45,10 +45,10 @@ public class Message {
internal(set) var pinnedTo: [String]?
public let comment: Comment?
public let file: File?
internal(set) public var reactions = [String: Reaction]()
internal(set) public var reactions = [Reaction]()
internal(set) public var attachments: [Attachment]?
public init?(message: [String: AnyObject]?) {
public init(message: [String: AnyObject]?) {
subtype = message?["subtype"] as? String
ts = message?["ts"] as? String
user = message?["user"] as? String
@@ -70,13 +70,13 @@ public class Message {
pinnedTo = message?["pinned_to"] as? [String]
comment = Comment(comment: message?["comment"] as? [String: AnyObject])
file = File(file: message?["file"] as? [String: AnyObject])
reactions = messageReactions(message?["reactions"] as? [[String: AnyObject]])
attachments = (message?["attachments"] as? [[String: AnyObject]])?.objectArrayFromDictionaryArray({(attachment) -> Attachment? in
reactions = Reaction.reactionsFromArray(message?["reactions"] as? [[String: AnyObject]])
attachments = (message?["attachments"] as? [[String: AnyObject]])?.map({(attachment) -> Attachment in
return Attachment(attachment: attachment)
})
}
internal init?(ts:String?) {
internal init(ts:String?) {
self.ts = ts
subtype = nil
user = nil
@@ -90,18 +90,7 @@ public class Message {
comment = nil
file = nil
}
private func messageReactions(reactions: [[String: AnyObject]]?) -> [String: Reaction] {
var returnValue = [String: Reaction]()
if let r = reactions {
for react in r {
if let reaction = Reaction(reaction: react), reactionName = reaction.name {
returnValue[reactionName] = reaction
}
}
}
return returnValue
}
}
extension Message: Equatable {}
+48 -38
View File
@@ -32,31 +32,18 @@ internal struct NetworkInterface {
if let params = parameters {
requestString += requestStringFromParameters(params)
}
let request = NSURLRequest(URL: NSURL(string: requestString)!)
guard let url = NSURL(string: requestString) else {
errorClosure(SlackError.ClientNetworkError)
return
}
let request = NSURLRequest(URL:url)
NSURLSession.sharedSession().dataTaskWithRequest(request) {
(data, response, internalError) -> Void in
guard let data = data else {
errorClosure(SlackError.ClientNetworkError)
return
}
do {
let result = try NSJSONSerialization.JSONObjectWithData(data, options: []) as! [String: AnyObject]
if (result["ok"] as! Bool == true) {
successClosure(result)
} else {
if let errorString = result["error"] as? String {
throw ErrorDispatcher.dispatch(errorString)
} else {
throw SlackError.UnknownError
}
}
} catch let error {
if let slackError = error as? SlackError {
errorClosure(slackError)
} else {
errorClosure(SlackError.UnknownError)
}
}
self.handleResponse(data, response: response, internalError: internalError, successClosure: {(json) in
successClosure(json)
}, errorClosure: {(error) in
errorClosure(error)
})
}.resume()
}
@@ -65,8 +52,11 @@ internal struct NetworkInterface {
if let params = parameters {
requestString = requestString + requestStringFromParameters(params)
}
let request = NSMutableURLRequest(URL: NSURL(string: requestString)!)
guard let url = NSURL(string: requestString) else {
errorClosure(SlackError.ClientNetworkError)
return
}
let request = NSMutableURLRequest(URL:url)
request.HTTPMethod = "POST"
let boundaryConstant = randomBoundary()
let contentType = "multipart/form-data; boundary=" + boundaryConstant
@@ -88,28 +78,48 @@ internal struct NetworkInterface {
NSURLSession.sharedSession().dataTaskWithRequest(request) {
(data, response, internalError) -> Void in
guard let data = data else {
self.handleResponse(data, response: response, internalError: internalError, successClosure: {(json) in
successClosure(json)
}, errorClosure: {(error) in
errorClosure(error)
})
}.resume()
}
private func handleResponse(data: NSData?, response:NSURLResponse?, internalError:NSError?, successClosure: ([String: AnyObject])->Void, errorClosure: (SlackError)->Void) {
guard let data = data, response = response as? NSHTTPURLResponse else {
errorClosure(SlackError.ClientNetworkError)
return
}
do {
guard let json = try NSJSONSerialization.JSONObjectWithData(data, options: []) as? [String: AnyObject] else {
errorClosure(SlackError.ClientJSONError)
return
}
do {
let result = try NSJSONSerialization.JSONObjectWithData(data, options: []) as! [String: AnyObject]
if (result["ok"] as! Bool == true) {
successClosure(result)
switch response.statusCode {
case 200:
if (json["ok"] as! Bool == true) {
successClosure(json)
} else {
if let errorString = result["error"] as? String {
if let errorString = json["error"] as? String {
throw ErrorDispatcher.dispatch(errorString)
} else {
throw SlackError.UnknownError
}
}
} catch let error {
if let slackError = error as? SlackError {
errorClosure(slackError)
} else {
errorClosure(SlackError.UnknownError)
}
case 429:
throw SlackError.TooManyRequests
default:
throw SlackError.ClientNetworkError
}
}.resume()
} catch let error {
if let slackError = error as? SlackError {
errorClosure(slackError)
} else {
errorClosure(SlackError.UnknownError)
}
}
}
private func randomBoundary() -> String {
+72 -64
View File
@@ -104,16 +104,22 @@ public class SlackWebAPI {
case IM = "im"
}
private let client: Client
required public init(client: Client) {
self.client = client
private let networkInterface: NetworkInterface
private let token: String
init(networkInterface: NetworkInterface, token: String) {
self.networkInterface = networkInterface
self.token = token
}
convenience public init(client: Client) {
self.init(networkInterface: client.api, token: client.token)
}
//MARK: - RTM
public func rtmStart(simpleLatest: Bool? = nil, noUnreads: Bool? = nil, mpimAware: Bool? = nil, success: ((response: [String: AnyObject])->Void)?, failure: FailureClosure?) {
let parameters: [String: AnyObject?] = ["simple_latest": simpleLatest, "no_unreads": noUnreads, "mpim_aware": mpimAware]
client.api.request(.RTMStart, token: client.token, parameters: filterNilParameters(parameters), successClosure: {
networkInterface.request(.RTMStart, token: token, parameters: filterNilParameters(parameters), successClosure: {
(response) -> Void in
success?(response: response)
}) {(error) -> Void in
@@ -123,7 +129,7 @@ public class SlackWebAPI {
//MARK: - Auth Test
public func authenticationTest(success: ((authenticated: Bool)->Void)?, failure: FailureClosure?) {
client.api.request(.AuthTest, token: client.token, parameters: nil, successClosure: {
networkInterface.request(.AuthTest, token: token, parameters: nil, successClosure: {
(response) -> Void in
success?(authenticated: true)
}) {(error) -> Void in
@@ -132,7 +138,7 @@ public class SlackWebAPI {
}
//MARK: - Channels
public func channelHistory(id: String, latest: String = "\(NSDate().timeIntervalSince1970)", oldest: String = "0", inclusive: Bool = false, count: Int = 100, unreads: Bool = false, success: ((history: History?)->Void)?, failure: FailureClosure?) {
public func channelHistory(id: String, latest: String = "\(NSDate().timeIntervalSince1970)", oldest: String = "0", inclusive: Bool = false, count: Int = 100, unreads: Bool = false, success: ((history: History)->Void)?, failure: FailureClosure?) {
history(.ChannelsHistory, id: id, latest: latest, oldest: oldest, inclusive: inclusive, count: count, unreads: unreads, success: {
(history) -> Void in
success?(history: history)
@@ -141,7 +147,7 @@ public class SlackWebAPI {
}
}
public func channelInfo(id: String, success: ((channel: Channel?)->Void)?, failure: FailureClosure?) {
public func channelInfo(id: String, success: ((channel: Channel)->Void)?, failure: FailureClosure?) {
info(.ChannelsInfo, type:ChannelType.Channel, id: id, success: {
(channel) -> Void in
success?(channel: channel)
@@ -189,7 +195,7 @@ public class SlackWebAPI {
//MARK: - Messaging
public func deleteMessage(channel: String, ts: String, success: ((deleted: Bool)->Void)?, failure: FailureClosure?) {
let parameters: [String: AnyObject] = ["channel": channel, "ts": ts]
client.api.request(.ChatDelete, token: client.token, parameters: parameters, successClosure: { (response) -> Void in
networkInterface.request(.ChatDelete, token: token, parameters: parameters, successClosure: { (response) -> Void in
success?(deleted: true)
}) {(error) -> Void in
failure?(error: error)
@@ -198,7 +204,7 @@ public class SlackWebAPI {
public func sendMessage(channel: String, text: String, username: String? = nil, asUser: Bool? = nil, parse: ParseMode? = nil, linkNames: Bool? = nil, attachments: [Attachment?]? = nil, unfurlLinks: Bool? = nil, unfurlMedia: Bool? = nil, iconURL: String? = nil, iconEmoji: String? = nil, success: (((ts: String?, channel: String?))->Void)?, failure: FailureClosure?) {
let parameters: [String: AnyObject?] = ["channel":channel, "text":text.slackFormatEscaping(), "as_user":asUser, "parse":parse?.rawValue, "link_names":linkNames, "unfurl_links":unfurlLinks, "unfurlMedia":unfurlMedia, "username":username, "attachments":encodeAttachments(attachments), "icon_url":iconURL, "icon_emoji":iconEmoji]
client.api.request(.ChatPostMessage, token: client.token, parameters: filterNilParameters(parameters), successClosure: {
networkInterface.request(.ChatPostMessage, token: token, parameters: filterNilParameters(parameters), successClosure: {
(response) -> Void in
success?((ts: response["ts"] as? String, response["channel"] as? String))
}) {(error) -> Void in
@@ -208,7 +214,7 @@ public class SlackWebAPI {
public func updateMessage(channel: String, ts: String, message: String, attachments: [Attachment?]? = nil, parse:ParseMode = .None, linkNames: Bool = false, success: ((updated: Bool)->Void)?, failure: FailureClosure?) {
let parameters: [String: AnyObject?] = ["channel": channel, "ts": ts, "text": message.slackFormatEscaping(), "parse": parse.rawValue, "link_names": linkNames, "attachments":encodeAttachments(attachments)]
client.api.request(.ChatUpdate, token: client.token, parameters: filterNilParameters(parameters), successClosure: {
networkInterface.request(.ChatUpdate, token: token, parameters: filterNilParameters(parameters), successClosure: {
(response) -> Void in
success?(updated: true)
}) {(error) -> Void in
@@ -217,9 +223,9 @@ public class SlackWebAPI {
}
//MARK: - Do Not Disturb
public func dndInfo(user: String? = nil, success: ((status: DoNotDisturbStatus?)->Void)?, failure: FailureClosure?) {
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: {
networkInterface.request(.DNDInfo, token: token, parameters: filterNilParameters(parameters), successClosure: {
(response) -> Void in
success?(status: DoNotDisturbStatus(status: response))
}) {(error) -> Void in
@@ -227,11 +233,15 @@ public class SlackWebAPI {
}
}
public func dndTeamInfo(users: [String]? = nil, success: ((statuses: [String: DoNotDisturbStatus]?)->Void)?, failure: FailureClosure?) {
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: {
networkInterface.request(.DNDTeamInfo, token: token, parameters: filterNilParameters(parameters), successClosure: {
(response) -> Void in
success?(statuses: self.enumerateDNDStauses(response["users"] as? [String: AnyObject]))
guard let usersDictionary = response["users"] as? [String: AnyObject] else {
success?(statuses: [:])
return
}
success?(statuses: self.enumerateDNDStatuses(usersDictionary))
}) {(error) -> Void in
failure?(error: error)
}
@@ -239,7 +249,7 @@ public class SlackWebAPI {
//MARK: - Emoji
public func emojiList(success: ((emojiList: [String: AnyObject]?)->Void)?, failure: FailureClosure?) {
client.api.request(.EmojiList, token: client.token, parameters: nil, successClosure: {
networkInterface.request(.EmojiList, token: token, parameters: nil, successClosure: {
(response) -> Void in
success?(emojiList: response["emoji"] as? [String: AnyObject])
}) { (error) -> Void in
@@ -250,7 +260,7 @@ public class SlackWebAPI {
//MARK: - Files
public func deleteFile(fileID: String, success: ((deleted: Bool)->Void)?, failure: FailureClosure?) {
let parameters = ["file":fileID]
client.api.request(.FilesDelete, token: client.token, parameters: parameters, successClosure: {
networkInterface.request(.FilesDelete, token: token, parameters: parameters, successClosure: {
(response) -> Void in
success?(deleted: true)
}) {(error) -> Void in
@@ -258,26 +268,26 @@ public class SlackWebAPI {
}
}
public func fileInfo(fileID: String, commentCount: Int = 100, totalPages: Int = 1, success: ((file: File?)->Void)?, failure: FailureClosure?) {
public func fileInfo(fileID: String, commentCount: Int = 100, totalPages: Int = 1, success: ((file: File)->Void)?, failure: FailureClosure?) {
let parameters: [String: AnyObject] = ["file":fileID, "count": commentCount, "totalPages":totalPages]
client.api.request(.FilesInfo, token: client.token, parameters: parameters, successClosure: {
networkInterface.request(.FilesInfo, token: token, parameters: parameters, successClosure: {
(response) in
var file = File(file: response["file"] as? [String: AnyObject])
(response["comments"] as? [[String: AnyObject]])?.objectArrayFromDictionaryArray({(comment) -> Comment? in
if let comment = Comment(comment: comment), id = comment.id {
file?.comments[id] = comment
(response["comments"] as? [[String: AnyObject]])?.forEach { comment in
let comment = Comment(comment: comment)
if let id = comment.id {
file.comments[id] = comment
}
return nil
})
}
success?(file: file)
}) {(error) in
failure?(error: error)
}
}
public func uploadFile(file: NSData, filename: String, filetype: String = "auto", title: String? = nil, initialComment: String? = nil, channels: [String]? = nil, success: ((file: File?)->Void)?, failure: FailureClosure?) {
public func uploadFile(file: NSData, filename: String, filetype: String = "auto", title: String? = nil, initialComment: String? = nil, channels: [String]? = nil, success: ((file: File)->Void)?, failure: FailureClosure?) {
let parameters: [String: AnyObject?] = ["file":file, "filename": filename, "filetype":filetype, "title":title, "initial_comment":initialComment, "channels":channels?.joinWithSeparator(",")]
client.api.uploadRequest(client.token, data: file, parameters: filterNilParameters(parameters), successClosure: {
networkInterface.uploadRequest(token, data: file, parameters: filterNilParameters(parameters), successClosure: {
(response) -> Void in
success?(file: File(file: response["file"] as? [String: AnyObject]))
}) {(error) -> Void in
@@ -286,9 +296,9 @@ public class SlackWebAPI {
}
//MARK: - File Comments
public func addFileComment(fileID: String, comment: String, success: ((comment: Comment?)->Void)?, failure: FailureClosure?) {
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: {
networkInterface.request(.FilesCommentsAdd, token: token, parameters: parameters, successClosure: {
(response) -> Void in
success?(comment: Comment(comment: response["comment"] as? [String: AnyObject]))
}) {(error) -> Void in
@@ -296,9 +306,9 @@ public class SlackWebAPI {
}
}
public func editFileComment(fileID: String, commentID: String, comment: String, success: ((comment: Comment?)->Void)?, failure: FailureClosure?) {
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: {
networkInterface.request(.FilesCommentsEdit, token: token, parameters: parameters, successClosure: {
(response) -> Void in
success?(comment: Comment(comment: response["comment"] as? [String: AnyObject]))
}) {(error) -> Void in
@@ -308,7 +318,7 @@ public class SlackWebAPI {
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: {
networkInterface.request(.FilesCommentsDelete, token: token, parameters: parameters, successClosure: {
(response) -> Void in
success?(deleted: true)
}) {(error) -> Void in
@@ -326,7 +336,7 @@ public class SlackWebAPI {
}
}
public func groupHistory(id: String, latest: String = "\(NSDate().timeIntervalSince1970)", oldest: String = "0", inclusive: Bool = false, count: Int = 100, unreads: Bool = false, success: ((history: History?)->Void)?, failure: FailureClosure?) {
public func groupHistory(id: String, latest: String = "\(NSDate().timeIntervalSince1970)", oldest: String = "0", inclusive: Bool = false, count: Int = 100, unreads: Bool = false, success: ((history: History)->Void)?, failure: FailureClosure?) {
history(.GroupsHistory, id: id, latest: latest, oldest: oldest, inclusive: inclusive, count: count, unreads: unreads, success: {
(history) -> Void in
success?(history: history)
@@ -335,7 +345,7 @@ public class SlackWebAPI {
}
}
public func groupInfo(id: String, success: ((channel: Channel?)->Void)?, failure: FailureClosure?) {
public func groupInfo(id: String, success: ((channel: Channel)->Void)?, failure: FailureClosure?) {
info(.GroupsInfo, type:ChannelType.Group, id: id, success: {
(channel) -> Void in
success?(channel: channel)
@@ -364,7 +374,7 @@ public class SlackWebAPI {
public func openGroup(channel: String, success: ((opened: Bool)->Void)?, failure: FailureClosure?) {
let parameters = ["channel":channel]
client.api.request(.GroupsOpen, token: client.token, parameters: parameters, successClosure: {
networkInterface.request(.GroupsOpen, token: token, parameters: parameters, successClosure: {
(response) -> Void in
success?(opened: true)
}) {(error) -> Void in
@@ -400,7 +410,7 @@ public class SlackWebAPI {
}
}
public func imHistory(id: String, latest: String = "\(NSDate().timeIntervalSince1970)", oldest: String = "0", inclusive: Bool = false, count: Int = 100, unreads: Bool = false, success: ((history: History?)->Void)?, failure: FailureClosure?) {
public func imHistory(id: String, latest: String = "\(NSDate().timeIntervalSince1970)", oldest: String = "0", inclusive: Bool = false, count: Int = 100, unreads: Bool = false, success: ((history: History)->Void)?, failure: FailureClosure?) {
history(.IMHistory, id: id, latest: latest, oldest: oldest, inclusive: inclusive, count: count, unreads: unreads, success: {
(history) -> Void in
success?(history: history)
@@ -429,7 +439,7 @@ public class SlackWebAPI {
public func openIM(userID: String, success: ((imID: String?)->Void)?, failure: FailureClosure?) {
let parameters = ["user":userID]
client.api.request(.IMOpen, token: client.token, parameters: parameters, successClosure: {
networkInterface.request(.IMOpen, token: token, parameters: parameters, successClosure: {
(response) -> Void in
let group = response["channel"] as? [String: AnyObject]
success?(imID: group?["id"] as? String)
@@ -448,7 +458,7 @@ public class SlackWebAPI {
}
}
public func mpimHistory(id: String, latest: String = "\(NSDate().timeIntervalSince1970)", oldest: String = "0", inclusive: Bool = false, count: Int = 100, unreads: Bool = false, success: ((history: History?)->Void)?, failure: FailureClosure?) {
public func mpimHistory(id: String, latest: String = "\(NSDate().timeIntervalSince1970)", oldest: String = "0", inclusive: Bool = false, count: Int = 100, unreads: Bool = false, success: ((history: History)->Void)?, failure: FailureClosure?) {
history(.MPIMHistory, id: id, latest: latest, oldest: oldest, inclusive: inclusive, count: count, unreads: unreads, success: {
(history) -> Void in
success?(history: history)
@@ -477,7 +487,7 @@ public class SlackWebAPI {
public func openMPIM(userIDs: [String], success: ((mpimID: String?)->Void)?, failure: FailureClosure?) {
let parameters = ["users":userIDs.joinWithSeparator(",")]
client.api.request(.MPIMOpen, token: client.token, parameters: parameters, successClosure: {
networkInterface.request(.MPIMOpen, token: token, parameters: parameters, successClosure: {
(response) -> Void in
let group = response["group"] as? [String: AnyObject]
success?(mpimID: group?["id"] as? String)
@@ -507,7 +517,7 @@ public class SlackWebAPI {
private func pin(endpoint: SlackAPIEndpoint, channel: String, file: String? = nil, fileComment: String? = nil, timestamp: String? = nil, success: ((ok: Bool)->Void)?, failure: FailureClosure?) {
let parameters: [String: AnyObject?] = ["channel":channel, "file":file, "file_comment":fileComment, "timestamp":timestamp]
client.api.request(endpoint, token: client.token, parameters: filterNilParameters(parameters), successClosure: {
networkInterface.request(endpoint, token: token, parameters: filterNilParameters(parameters), successClosure: {
(response) -> Void in
success?(ok: true)
}){(error) -> Void in
@@ -538,7 +548,7 @@ public class SlackWebAPI {
private func react(endpoint: SlackAPIEndpoint, name: String, file: String? = nil, fileComment: String? = nil, channel: String? = nil, timestamp: String? = nil, success: ((ok: Bool)->Void)?, failure: FailureClosure?) {
let parameters: [String: AnyObject?] = ["name":name, "file":file, "file_comment":fileComment, "channel":channel, "timestamp":timestamp]
client.api.request(endpoint, token: client.token, parameters: filterNilParameters(parameters), successClosure: {
networkInterface.request(endpoint, token: token, parameters: filterNilParameters(parameters), successClosure: {
(response) -> Void in
success?(ok: true)
}) {(error) -> Void in
@@ -569,7 +579,7 @@ public class SlackWebAPI {
private func star(endpoint: SlackAPIEndpoint, file: String?, fileComment: String?, channel: String?, timestamp: String?, success: ((ok: Bool)->Void)?, failure: FailureClosure?) {
let parameters: [String: AnyObject?] = ["file":file, "file_comment":fileComment, "channel":channel, "timestamp":timestamp]
client.api.request(endpoint, token: client.token, parameters: filterNilParameters(parameters), successClosure: {
networkInterface.request(endpoint, token: token, parameters: filterNilParameters(parameters), successClosure: {
(response) -> Void in
success?(ok: true)
}) {(error) -> Void in
@@ -580,7 +590,7 @@ public class SlackWebAPI {
//MARK: - Team
public func teamInfo(success: ((info: [String: AnyObject]?)->Void)?, failure: FailureClosure?) {
client.api.request(.TeamInfo, token: client.token, parameters: nil, successClosure: {
networkInterface.request(.TeamInfo, token: token, parameters: nil, successClosure: {
(response) -> Void in
success?(info: response["team"] as? [String: AnyObject])
}) {(error) -> Void in
@@ -591,7 +601,7 @@ public class SlackWebAPI {
//MARK: - Users
public func userPresence(user: String, success: ((presence: String?)->Void)?, failure: FailureClosure?) {
let parameters: [String: AnyObject] = ["user":user]
client.api.request(.UsersGetPresence, token: client.token, parameters: parameters, successClosure: {
networkInterface.request(.UsersGetPresence, token: token, parameters: parameters, successClosure: {
(response) -> Void in
success?(presence: response["presence"] as? String)
}){(error) -> Void in
@@ -599,9 +609,9 @@ public class SlackWebAPI {
}
}
public func userInfo(id: String, success: ((user: User?)->Void)?, failure: FailureClosure?) {
public func userInfo(id: String, success: ((user: User)->Void)?, failure: FailureClosure?) {
let parameters: [String: AnyObject] = ["user":id]
client.api.request(.UsersInfo, token: client.token, parameters: parameters, successClosure: {
networkInterface.request(.UsersInfo, token: token, parameters: parameters, successClosure: {
(response) -> Void in
success?(user: User(user: response["user"] as? [String: AnyObject]))
}) {(error) -> Void in
@@ -611,7 +621,7 @@ public class SlackWebAPI {
public func usersList(includePresence: Bool = false, success: ((userList: [[String: AnyObject]]?)->Void)?, failure: FailureClosure?) {
let parameters: [String: AnyObject] = ["presence":includePresence]
client.api.request(.UsersList, token: client.token, parameters: parameters, successClosure: {
networkInterface.request(.UsersList, token: token, parameters: parameters, successClosure: {
(response) -> Void in
success?(userList: response["members"] as? [[String: AnyObject]])
}){(error) -> Void in
@@ -620,7 +630,7 @@ public class SlackWebAPI {
}
public func setUserActive(success: ((success: Bool)->Void)?, failure: FailureClosure?) {
client.api.request(.UsersSetActive, token: client.token, parameters: nil, successClosure: {
networkInterface.request(.UsersSetActive, token: token, parameters: nil, successClosure: {
(response) -> Void in
success?(success: true)
}) {(error) -> Void in
@@ -630,7 +640,7 @@ public class SlackWebAPI {
public func setUserPresence(presence: Presence, success: ((success: Bool)->Void)?, failure: FailureClosure?) {
let parameters: [String: AnyObject] = ["presence":presence.rawValue]
client.api.request(.UsersSetPresence, token: client.token, parameters: parameters, successClosure: {
networkInterface.request(.UsersSetPresence, token: token, parameters: parameters, successClosure: {
(response) -> Void in
success?(success:true)
}) {(error) -> Void in
@@ -641,7 +651,7 @@ public class SlackWebAPI {
//MARK: - Channel Utilities
private func close(endpoint: SlackAPIEndpoint, channelID: String, success: ((closed: Bool)->Void)?, failure: FailureClosure?) {
let parameters: [String: AnyObject] = ["channel":channelID]
client.api.request(endpoint, token: client.token, parameters: parameters, successClosure: {
networkInterface.request(endpoint, token: token, parameters: parameters, successClosure: {
(response) -> Void in
success?(closed: true)
}) {(error) -> Void in
@@ -649,9 +659,9 @@ public class SlackWebAPI {
}
}
private func history(endpoint: SlackAPIEndpoint, id: String, latest: String = "\(NSDate().timeIntervalSince1970)", oldest: String = "0", inclusive: Bool = false, count: Int = 100, unreads: Bool = false, success: ((history: History?)->Void)?, failure: FailureClosure?) {
private func history(endpoint: SlackAPIEndpoint, id: String, latest: String = "\(NSDate().timeIntervalSince1970)", oldest: String = "0", inclusive: Bool = false, count: Int = 100, unreads: Bool = false, success: ((history: History)->Void)?, failure: FailureClosure?) {
let parameters: [String: AnyObject] = ["channel": id, "latest": latest, "oldest": oldest, "inclusive":inclusive, "count":count, "unreads":unreads]
client.api.request(endpoint, token: client.token, parameters: parameters, successClosure: {
networkInterface.request(endpoint, token: token, parameters: parameters, successClosure: {
(response) -> Void in
success?(history: History(history: response))
}) {(error) -> Void in
@@ -659,9 +669,9 @@ public class SlackWebAPI {
}
}
private func info(endpoint: SlackAPIEndpoint, type: ChannelType, id: String, success: ((channel: Channel?)->Void)?, failure: FailureClosure?) {
private func info(endpoint: SlackAPIEndpoint, type: ChannelType, id: String, success: ((channel: Channel)->Void)?, failure: FailureClosure?) {
let parameters: [String: AnyObject] = ["channel": id]
client.api.request(endpoint, token: client.token, parameters: parameters, successClosure: {
networkInterface.request(endpoint, token: token, parameters: parameters, successClosure: {
(response) -> Void in
success?(channel: Channel(channel: response[type.rawValue] as? [String: AnyObject]))
}) {(error) -> Void in
@@ -671,7 +681,7 @@ public class SlackWebAPI {
private func list(endpoint: SlackAPIEndpoint, type: ChannelType, excludeArchived: Bool = false, success: ((channels: [[String: AnyObject]]?)->Void)?, failure: FailureClosure?) {
let parameters: [String: AnyObject] = ["exclude_archived": excludeArchived]
client.api.request(endpoint, token: client.token, parameters: parameters, successClosure: {
networkInterface.request(endpoint, token: token, parameters: parameters, successClosure: {
(response) -> Void in
success?(channels: response[type.rawValue+"s"] as? [[String: AnyObject]])
}) {(error) -> Void in
@@ -681,7 +691,7 @@ public class SlackWebAPI {
private func mark(endpoint: SlackAPIEndpoint, channel: String, timestamp: String, success: ((ts: String)->Void)?, failure: FailureClosure?) {
let parameters: [String: AnyObject] = ["channel": channel, "ts": timestamp]
client.api.request(endpoint, token: client.token, parameters: parameters, successClosure: {
networkInterface.request(endpoint, token: token, parameters: parameters, successClosure: {
(response) -> Void in
success?(ts: timestamp)
}) {(error) -> Void in
@@ -691,7 +701,7 @@ public class SlackWebAPI {
private func setInfo(endpoint: SlackAPIEndpoint, type: InfoType, channel: String, text: String, success: ((success: Bool)->Void)?, failure: FailureClosure?) {
let parameters: [String: AnyObject] = ["channel": channel, type.rawValue: text]
client.api.request(endpoint, token: client.token, parameters: parameters, successClosure: {
networkInterface.request(endpoint, token: token, parameters: parameters, successClosure: {
(response) -> Void in
success?(success: true)
}) {(error) -> Void in
@@ -720,7 +730,7 @@ public class SlackWebAPI {
}
}
do {
let data = try NSJSONSerialization.dataWithJSONObject(attachmentArray, options: NSJSONWritingOptions.PrettyPrinted)
let data = try NSJSONSerialization.dataWithJSONObject(attachmentArray, options: [])
let string = NSString(data: data, encoding: NSUTF8StringEncoding)
return string
} catch _ {
@@ -730,13 +740,11 @@ public class SlackWebAPI {
return nil
}
//MARK: - Enumerate Do Not Distrub Status
private func enumerateDNDStauses(statuses: [String: AnyObject]?) -> [String: DoNotDisturbStatus] {
//MARK: - Enumerate Do Not Disturb Status
private func enumerateDNDStatuses(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])
}
for key in statuses.keys {
retVal[key] = DoNotDisturbStatus(status: statuses[key] as? [String: AnyObject])
}
return retVal
}
@@ -1,5 +1,5 @@
//
// SlackWebAPIErrorHandling.swift
// SlackWebAPIErrorDispatcher.swift
//
// Copyright © 2016 Peter Zignego. All rights reserved.
//
@@ -113,6 +113,10 @@ public enum SlackError: ErrorType {
case UserNotVisible
// Client
case ClientNetworkError
case ClientJSONError
// HTTP
case TooManyRequests
case UnknownHTTPError
}
internal struct ErrorDispatcher {
+2 -2
View File
@@ -33,7 +33,7 @@ public struct Team {
internal(set) public var plan: String?
internal(set) public var icon: TeamIcon?
internal init?(team: [String: AnyObject]?) {
internal init(team: [String: AnyObject]?) {
id = team?["id"] as! String
name = team?["name"] as? String
domain = team?["domain"] as? String
@@ -56,7 +56,7 @@ public struct TeamIcon {
internal(set) public var imageOriginal: String?
internal(set) public var imageDefault: Bool?
internal init?(icon: [String: AnyObject]?) {
internal init(icon: [String: AnyObject]?) {
image34 = icon?["image_34"] as? String
image44 = icon?["image_44"] as? String
image68 = icon?["image_68"] as? String
+39 -48
View File
@@ -28,7 +28,7 @@ public struct Edited {
public let user: String?
public let ts: String?
internal init?(edited:[String: AnyObject]?) {
internal init(edited:[String: AnyObject]?) {
user = edited?["user"] as? String
ts = edited?["ts"] as? String
}
@@ -40,15 +40,13 @@ public struct History {
internal(set) public var messages = [Message]()
public let hasMore: Bool?
internal init?(history: [String: AnyObject]?) {
internal init(history: [String: AnyObject]?) {
if let latestStr = history?["latest"] as? String, latestDouble = Double(latestStr) {
latest = NSDate(timeIntervalSince1970: NSTimeInterval(latestDouble))
}
if let msgs = history?["messages"] as? [[String: AnyObject]] {
for message in msgs {
if let message = Message(message: message) {
messages.append(message)
}
messages.append(Message(message: message))
}
}
hasMore = history?["has_more"] as? Bool
@@ -58,34 +56,27 @@ public struct History {
// MARK: - Reaction
public struct Reaction {
public let name: String?
internal(set) public var users = [String: String]()
internal(set) public var user: String?
internal init?(reaction:[String: AnyObject]?) {
internal init(reaction:[String: AnyObject]?) {
name = reaction?["name"] as? String
}
internal init?(name: String?, user: String) {
internal init(name: String, user: String) {
self.name = name
users[user] = user
self.user = user
}
internal init?(name: String?, users: [String: String]) {
self.name = name
self.users = users
}
static func reactionsFromArray(array: [[String: AnyObject]]) -> [String: Reaction] {
var reactions = [String: Reaction]()
var userDictionary = [String: String]()
for reaction in array {
if let users = reaction["users"] as? [String] {
for user in users {
userDictionary[user] = user
static func reactionsFromArray(array: [[String: AnyObject]]?) -> [Reaction] {
var reactions = [Reaction]()
if let array = array {
for reaction in array {
if let users = reaction["users"] as? [String], name = reaction["name"] as? String {
for user in users {
reactions.append(Reaction(name: name, user: user))
}
}
}
if let name = reaction["name"] as? String {
reactions[name] = Reaction(name: name, users: userDictionary)
}
}
return reactions
}
@@ -106,9 +97,9 @@ public struct Comment {
internal(set) public var comment: String?
internal(set) public var starred: Bool?
internal(set) public var stars: Int?
internal(set) public var reactions = [String: Reaction]()
internal(set) public var reactions = [Reaction]()
internal init?(comment:[String: AnyObject]?) {
internal init(comment:[String: AnyObject]?) {
id = comment?["id"] as? String
created = comment?["created"] as? Int
user = comment?["user"] as? String
@@ -117,7 +108,7 @@ public struct Comment {
self.comment = comment?["comment"] as? String
}
internal init?(id: String?) {
internal init(id: String?) {
self.id = id
self.user = nil
}
@@ -139,7 +130,7 @@ public struct Item {
public let comment: Comment?
public let fileCommentID: String?
internal init?(item:[String: AnyObject]?) {
internal init(item:[String: AnyObject]?) {
type = item?["type"] as? String
ts = item?["ts"] as? String
channel = item?["channel"] as? String
@@ -147,15 +138,16 @@ public struct Item {
message = Message(message: item?["message"] as? [String: AnyObject])
// Comment and File can come across as Strings or Dictionaries
if (Comment(comment: item?["comment"] as? [String: AnyObject])?.id == nil) {
if let commentDictionary = item?["comment"] as? [String: AnyObject] {
comment = Comment(comment: commentDictionary)
} else {
comment = Comment(id: item?["comment"] as? String)
} else {
comment = Comment(comment: item?["comment"] as? [String: AnyObject])
}
if (File(file: item?["file"] as? [String: AnyObject])?.id == nil) {
file = File(id: item?["file"] as? String)
if let fileDictionary = item?["file"] as? [String: AnyObject] {
file = File(file: fileDictionary)
} else {
file = File(file: item?["file"] as? [String: AnyObject])
file = File(id: item?["file"] as? String)
}
fileCommentID = item?["file_comment"] as? String
@@ -174,7 +166,7 @@ public struct Topic {
public let creator: String?
public let lastSet: Int?
internal init?(topic: [String: AnyObject]?) {
internal init(topic: [String: AnyObject]?) {
value = topic?["value"] as? String
creator = topic?["creator"] as? String
lastSet = topic?["last_set"] as? Int
@@ -189,7 +181,7 @@ public struct DoNotDisturbStatus {
internal(set) public var snoozeEnabled: Bool?
internal(set) public var snoozeEndtime: Int?
internal init?(status: [String: AnyObject]?) {
internal init(status: [String: AnyObject]?) {
enabled = status?["dnd_enabled"] as? Bool
nextDoNotDisturbStart = status?["next_dnd_start_ts"] as? Int
nextDoNotDisturbEnd = status?["next_dnd_end_ts"] as? Int
@@ -203,26 +195,25 @@ public struct DoNotDisturbStatus {
public struct CustomProfile {
internal(set) public var fields = [String: CustomProfileField]()
internal init?(profile: [String: AnyObject]?) {
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
var cpf: CustomProfileField?
if let fieldDictionary = field as? [String: AnyObject] {
cpf = CustomProfileField(field: fieldDictionary)
} else {
if let cpf = CustomProfileField(id: field as? String), id = cpf.id {
fields[id] = cpf
}
cpf = CustomProfileField(id: field as? String)
}
if let id = cpf?.id { fields[id] = cpf }
}
}
}
internal init?(customFields: [String: AnyObject]?) {
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
}
let cpf = CustomProfileField(field: customFields[key] as? [String: AnyObject])
self.fields[key] = cpf
}
}
}
@@ -241,7 +232,7 @@ public struct CustomProfileField {
internal(set) public var possibleValues: [String]?
internal(set) public var type: String?
internal init?(field: [String: AnyObject]?) {
internal init(field: [String: AnyObject]?) {
id = field?["id"] as? String
alt = field?["alt"] as? String
value = field?["value"] as? String
@@ -254,7 +245,7 @@ public struct CustomProfileField {
type = field?["type"] as? String
}
internal init?(id: String?) {
internal init(id: String?) {
self.id = id
}
+3 -3
View File
@@ -37,7 +37,7 @@ public struct User {
internal(set) public var image192: String?
internal(set) public var customProfile: CustomProfile?
internal init?(profile: [String: AnyObject]?) {
internal init(profile: [String: AnyObject]?) {
firstName = profile?["first_name"] as? String
lastName = profile?["last_name"] as? String
realName = profile?["real_name"] as? String
@@ -77,7 +77,7 @@ public struct User {
// Client properties
internal(set) public var userGroups: [String: String]?
internal init?(user: [String: AnyObject]?) {
internal init(user: [String: AnyObject]?) {
id = user?["id"] as? String
name = user?["name"] as? String
deleted = user?["deleted"] as? Bool
@@ -99,7 +99,7 @@ public struct User {
preferences = user?["prefs"] as? [String: AnyObject]
}
internal init?(id: String?) {
internal init(id: String?) {
self.id = id
self.isBot = nil
}
+1 -1
View File
@@ -43,7 +43,7 @@ public struct UserGroup {
internal(set) public var users: [String]?
internal(set) public var userCount: Int?
internal init?(userGroup: [String: AnyObject]?) {
internal init(userGroup: [String: AnyObject]?) {
id = userGroup?["id"] as? String
teamID = userGroup?["team_id"] as? String
isUserGroup = userGroup?["is_usergroup"] as? Bool
@@ -6,8 +6,6 @@
<string>en</string>
<key>CFBundleExecutable</key>
<string>$(EXECUTABLE_NAME)</string>
<key>CFBundleIconFile</key>
<string></string>
<key>CFBundleIdentifier</key>
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
<key>CFBundleInfoDictionaryVersion</key>
@@ -15,20 +13,16 @@
<key>CFBundleName</key>
<string>$(PRODUCT_NAME)</string>
<key>CFBundlePackageType</key>
<string>APPL</string>
<string>FMWK</string>
<key>CFBundleShortVersionString</key>
<string>1.0</string>
<string>1.1.1</string>
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleVersion</key>
<string>1</string>
<key>LSMinimumSystemVersion</key>
<string>$(MACOSX_DEPLOYMENT_TARGET)</string>
<string>$(CURRENT_PROJECT_VERSION)</string>
<key>NSHumanReadableCopyright</key>
<string>Copyright © 2016 Launch Software LLC. All rights reserved.</string>
<key>NSMainNibFile</key>
<string>MainMenu</string>
<string>Copyright © 2016 Peter Zignego. All rights reserved.</string>
<key>NSPrincipalClass</key>
<string>NSApplication</string>
<string></string>
</dict>
</plist>
+28
View File
@@ -0,0 +1,28 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleDevelopmentRegion</key>
<string>en</string>
<key>CFBundleExecutable</key>
<string>$(EXECUTABLE_NAME)</string>
<key>CFBundleIdentifier</key>
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>
<string>$(PRODUCT_NAME)</string>
<key>CFBundlePackageType</key>
<string>FMWK</string>
<key>CFBundleShortVersionString</key>
<string>1.1.1</string>
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleVersion</key>
<string>$(CURRENT_PROJECT_VERSION)</string>
<key>NSHumanReadableCopyright</key>
<string>Copyright © 2016 Peter Zignego. All rights reserved.</string>
<key>NSPrincipalClass</key>
<string></string>
</dict>
</plist>
@@ -15,7 +15,7 @@
<key>CFBundlePackageType</key>
<string>FMWK</string>
<key>CFBundleShortVersionString</key>
<string>1.0.2</string>
<string>1.1.1</string>
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleVersion</key>