├── .gitignore ├── LICENSE ├── README.md ├── SwiftTweetGettr.xcodeproj ├── project.pbxproj ├── project.xcworkspace │ ├── contents.xcworkspacedata │ ├── xcshareddata │ │ └── SwiftTweetGettr.xccheckout │ └── xcuserdata │ │ └── jmenter.xcuserdatad │ │ └── UserInterfaceState.xcuserstate └── xcuserdata │ └── jmenter.xcuserdatad │ ├── xcdebugger │ └── Breakpoints_v2.xcbkptlist │ └── xcschemes │ ├── SwiftTweetGettr.xcscheme │ └── xcschememanagement.plist └── SwiftTweetGettr ├── AppDelegate.swift ├── Base.lproj └── Main.storyboard ├── Extensions.swift ├── Images.xcassets ├── AppIcon.appiconset │ └── Contents.json ├── LaunchImage.launchimage │ └── Contents.json └── default.imageset │ ├── Contents.json │ └── default@2x.png ├── Info.plist ├── Tweet.swift ├── TweetCell.swift ├── TweetsTableViewDelegate.swift ├── TwitterAuthorization.swift ├── TwitterClient.swift └── ViewController.swift /.gitignore: -------------------------------------------------------------------------------- 1 | # CocoaPods 2 | # 3 | # We recommend against adding the Pods directory to your .gitignore. However 4 | # you should judge for yourself, the pros and cons are mentioned at: 5 | # http://guides.cocoapods.org/using/using-cocoapods.html#should-i-ignore-the-pods-directory-in-source-control? 6 | # 7 | # Pods/ 8 | 9 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2014 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | SwiftTweetGettr 2 | =============== 3 | 4 | New to iOS or Swift? This tiny twitter client project will hopefully get you started building your own apps or seeing common iOS patterns in Swift. 5 | 6 | You'll need to replace the API Key and API Secret in the Client class with those of your own. You can get them by getting a Twitter developer account and creating an app. Just go here: https://apps.twitter.com/app/new 7 | -------------------------------------------------------------------------------- /SwiftTweetGettr.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 46; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 633537F81A3BC82800074E40 /* TweetCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 633537F71A3BC82800074E40 /* TweetCell.swift */; }; 11 | 6339480A1941504E00521224 /* Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 633948091941504E00521224 /* Extensions.swift */; }; 12 | 6339480C1941581E00521224 /* TweetsTableViewDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6339480B1941581E00521224 /* TweetsTableViewDelegate.swift */; }; 13 | 635A00F51A34CE2A000C9647 /* Tweet.swift in Sources */ = {isa = PBXBuildFile; fileRef = 635A00F41A34CE2A000C9647 /* Tweet.swift */; }; 14 | 63C33D9C1A337B66004B2495 /* TwitterAuthorization.swift in Sources */ = {isa = PBXBuildFile; fileRef = 63C33D9B1A337B66004B2495 /* TwitterAuthorization.swift */; }; 15 | 63C33D9E1A337DBE004B2495 /* TwitterClient.swift in Sources */ = {isa = PBXBuildFile; fileRef = 63C33D9D1A337DBE004B2495 /* TwitterClient.swift */; }; 16 | 63FEA3D4193E888700400515 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 63FEA3D3193E888700400515 /* AppDelegate.swift */; }; 17 | 63FEA3D6193E888700400515 /* ViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 63FEA3D5193E888700400515 /* ViewController.swift */; }; 18 | 63FEA3D9193E888700400515 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 63FEA3D7193E888700400515 /* Main.storyboard */; }; 19 | 63FEA3DB193E888700400515 /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 63FEA3DA193E888700400515 /* Images.xcassets */; }; 20 | /* End PBXBuildFile section */ 21 | 22 | /* Begin PBXFileReference section */ 23 | 633537F71A3BC82800074E40 /* TweetCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TweetCell.swift; sourceTree = ""; }; 24 | 633948091941504E00521224 /* Extensions.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Extensions.swift; sourceTree = ""; }; 25 | 6339480B1941581E00521224 /* TweetsTableViewDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TweetsTableViewDelegate.swift; sourceTree = ""; }; 26 | 635A00F41A34CE2A000C9647 /* Tweet.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Tweet.swift; sourceTree = ""; }; 27 | 63C33D9B1A337B66004B2495 /* TwitterAuthorization.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TwitterAuthorization.swift; sourceTree = ""; }; 28 | 63C33D9D1A337DBE004B2495 /* TwitterClient.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TwitterClient.swift; sourceTree = ""; }; 29 | 63FEA3CE193E888700400515 /* SwiftTweetGettr.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = SwiftTweetGettr.app; sourceTree = BUILT_PRODUCTS_DIR; }; 30 | 63FEA3D2193E888700400515 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 31 | 63FEA3D3193E888700400515 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; 32 | 63FEA3D5193E888700400515 /* ViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewController.swift; sourceTree = ""; }; 33 | 63FEA3D8193E888700400515 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; 34 | 63FEA3DA193E888700400515 /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Images.xcassets; sourceTree = ""; }; 35 | /* End PBXFileReference section */ 36 | 37 | /* Begin PBXFrameworksBuildPhase section */ 38 | 63FEA3CB193E888700400515 /* Frameworks */ = { 39 | isa = PBXFrameworksBuildPhase; 40 | buildActionMask = 2147483647; 41 | files = ( 42 | ); 43 | runOnlyForDeploymentPostprocessing = 0; 44 | }; 45 | /* End PBXFrameworksBuildPhase section */ 46 | 47 | /* Begin PBXGroup section */ 48 | 63FEA3C5193E888700400515 = { 49 | isa = PBXGroup; 50 | children = ( 51 | 63FEA3D0193E888700400515 /* SwiftTweetGettr */, 52 | 63FEA3CF193E888700400515 /* Products */, 53 | ); 54 | sourceTree = ""; 55 | }; 56 | 63FEA3CF193E888700400515 /* Products */ = { 57 | isa = PBXGroup; 58 | children = ( 59 | 63FEA3CE193E888700400515 /* SwiftTweetGettr.app */, 60 | ); 61 | name = Products; 62 | sourceTree = ""; 63 | }; 64 | 63FEA3D0193E888700400515 /* SwiftTweetGettr */ = { 65 | isa = PBXGroup; 66 | children = ( 67 | 633948091941504E00521224 /* Extensions.swift */, 68 | 63FEA3D3193E888700400515 /* AppDelegate.swift */, 69 | 63FEA3D5193E888700400515 /* ViewController.swift */, 70 | 635A00F41A34CE2A000C9647 /* Tweet.swift */, 71 | 63C33D9B1A337B66004B2495 /* TwitterAuthorization.swift */, 72 | 63C33D9D1A337DBE004B2495 /* TwitterClient.swift */, 73 | 6339480B1941581E00521224 /* TweetsTableViewDelegate.swift */, 74 | 63FEA3D7193E888700400515 /* Main.storyboard */, 75 | 633537F71A3BC82800074E40 /* TweetCell.swift */, 76 | 63FEA3DA193E888700400515 /* Images.xcassets */, 77 | 63FEA3D1193E888700400515 /* Supporting Files */, 78 | ); 79 | path = SwiftTweetGettr; 80 | sourceTree = ""; 81 | }; 82 | 63FEA3D1193E888700400515 /* Supporting Files */ = { 83 | isa = PBXGroup; 84 | children = ( 85 | 63FEA3D2193E888700400515 /* Info.plist */, 86 | ); 87 | name = "Supporting Files"; 88 | sourceTree = ""; 89 | }; 90 | /* End PBXGroup section */ 91 | 92 | /* Begin PBXNativeTarget section */ 93 | 63FEA3CD193E888700400515 /* SwiftTweetGettr */ = { 94 | isa = PBXNativeTarget; 95 | buildConfigurationList = 63FEA3EA193E888700400515 /* Build configuration list for PBXNativeTarget "SwiftTweetGettr" */; 96 | buildPhases = ( 97 | 63FEA3CA193E888700400515 /* Sources */, 98 | 63FEA3CB193E888700400515 /* Frameworks */, 99 | 63FEA3CC193E888700400515 /* Resources */, 100 | ); 101 | buildRules = ( 102 | ); 103 | dependencies = ( 104 | ); 105 | name = SwiftTweetGettr; 106 | productName = SwiftTweetGettr; 107 | productReference = 63FEA3CE193E888700400515 /* SwiftTweetGettr.app */; 108 | productType = "com.apple.product-type.application"; 109 | }; 110 | /* End PBXNativeTarget section */ 111 | 112 | /* Begin PBXProject section */ 113 | 63FEA3C6193E888700400515 /* Project object */ = { 114 | isa = PBXProject; 115 | attributes = { 116 | LastUpgradeCheck = 0600; 117 | ORGANIZATIONNAME = "Jeff Menter"; 118 | TargetAttributes = { 119 | 63FEA3CD193E888700400515 = { 120 | CreatedOnToolsVersion = 6.0; 121 | }; 122 | }; 123 | }; 124 | buildConfigurationList = 63FEA3C9193E888700400515 /* Build configuration list for PBXProject "SwiftTweetGettr" */; 125 | compatibilityVersion = "Xcode 3.2"; 126 | developmentRegion = English; 127 | hasScannedForEncodings = 0; 128 | knownRegions = ( 129 | en, 130 | Base, 131 | ); 132 | mainGroup = 63FEA3C5193E888700400515; 133 | productRefGroup = 63FEA3CF193E888700400515 /* Products */; 134 | projectDirPath = ""; 135 | projectRoot = ""; 136 | targets = ( 137 | 63FEA3CD193E888700400515 /* SwiftTweetGettr */, 138 | ); 139 | }; 140 | /* End PBXProject section */ 141 | 142 | /* Begin PBXResourcesBuildPhase section */ 143 | 63FEA3CC193E888700400515 /* Resources */ = { 144 | isa = PBXResourcesBuildPhase; 145 | buildActionMask = 2147483647; 146 | files = ( 147 | 63FEA3D9193E888700400515 /* Main.storyboard in Resources */, 148 | 63FEA3DB193E888700400515 /* Images.xcassets in Resources */, 149 | ); 150 | runOnlyForDeploymentPostprocessing = 0; 151 | }; 152 | /* End PBXResourcesBuildPhase section */ 153 | 154 | /* Begin PBXSourcesBuildPhase section */ 155 | 63FEA3CA193E888700400515 /* Sources */ = { 156 | isa = PBXSourcesBuildPhase; 157 | buildActionMask = 2147483647; 158 | files = ( 159 | 63FEA3D6193E888700400515 /* ViewController.swift in Sources */, 160 | 6339480A1941504E00521224 /* Extensions.swift in Sources */, 161 | 6339480C1941581E00521224 /* TweetsTableViewDelegate.swift in Sources */, 162 | 633537F81A3BC82800074E40 /* TweetCell.swift in Sources */, 163 | 635A00F51A34CE2A000C9647 /* Tweet.swift in Sources */, 164 | 63FEA3D4193E888700400515 /* AppDelegate.swift in Sources */, 165 | 63C33D9E1A337DBE004B2495 /* TwitterClient.swift in Sources */, 166 | 63C33D9C1A337B66004B2495 /* TwitterAuthorization.swift in Sources */, 167 | ); 168 | runOnlyForDeploymentPostprocessing = 0; 169 | }; 170 | /* End PBXSourcesBuildPhase section */ 171 | 172 | /* Begin PBXVariantGroup section */ 173 | 63FEA3D7193E888700400515 /* Main.storyboard */ = { 174 | isa = PBXVariantGroup; 175 | children = ( 176 | 63FEA3D8193E888700400515 /* Base */, 177 | ); 178 | name = Main.storyboard; 179 | sourceTree = ""; 180 | }; 181 | /* End PBXVariantGroup section */ 182 | 183 | /* Begin XCBuildConfiguration section */ 184 | 63FEA3E8193E888700400515 /* Debug */ = { 185 | isa = XCBuildConfiguration; 186 | buildSettings = { 187 | ALWAYS_SEARCH_USER_PATHS = NO; 188 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 189 | CLANG_CXX_LIBRARY = "libc++"; 190 | CLANG_ENABLE_MODULES = YES; 191 | CLANG_ENABLE_OBJC_ARC = YES; 192 | CLANG_WARN_BOOL_CONVERSION = YES; 193 | CLANG_WARN_CONSTANT_CONVERSION = YES; 194 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 195 | CLANG_WARN_EMPTY_BODY = YES; 196 | CLANG_WARN_ENUM_CONVERSION = YES; 197 | CLANG_WARN_INT_CONVERSION = YES; 198 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 199 | CLANG_WARN_UNREACHABLE_CODE = YES; 200 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 201 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 202 | COPY_PHASE_STRIP = NO; 203 | ENABLE_STRICT_OBJC_MSGSEND = YES; 204 | GCC_C_LANGUAGE_STANDARD = gnu99; 205 | GCC_DYNAMIC_NO_PIC = NO; 206 | GCC_OPTIMIZATION_LEVEL = 0; 207 | GCC_PREPROCESSOR_DEFINITIONS = ( 208 | "DEBUG=1", 209 | "$(inherited)", 210 | ); 211 | GCC_SYMBOLS_PRIVATE_EXTERN = NO; 212 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 213 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 214 | GCC_WARN_UNDECLARED_SELECTOR = YES; 215 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 216 | GCC_WARN_UNUSED_FUNCTION = YES; 217 | GCC_WARN_UNUSED_VARIABLE = YES; 218 | IPHONEOS_DEPLOYMENT_TARGET = 7.1; 219 | METAL_ENABLE_DEBUG_INFO = YES; 220 | ONLY_ACTIVE_ARCH = YES; 221 | SDKROOT = iphoneos; 222 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 223 | }; 224 | name = Debug; 225 | }; 226 | 63FEA3E9193E888700400515 /* Release */ = { 227 | isa = XCBuildConfiguration; 228 | buildSettings = { 229 | ALWAYS_SEARCH_USER_PATHS = NO; 230 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 231 | CLANG_CXX_LIBRARY = "libc++"; 232 | CLANG_ENABLE_MODULES = YES; 233 | CLANG_ENABLE_OBJC_ARC = YES; 234 | CLANG_WARN_BOOL_CONVERSION = YES; 235 | CLANG_WARN_CONSTANT_CONVERSION = YES; 236 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 237 | CLANG_WARN_EMPTY_BODY = YES; 238 | CLANG_WARN_ENUM_CONVERSION = YES; 239 | CLANG_WARN_INT_CONVERSION = YES; 240 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 241 | CLANG_WARN_UNREACHABLE_CODE = YES; 242 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 243 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 244 | COPY_PHASE_STRIP = YES; 245 | ENABLE_NS_ASSERTIONS = NO; 246 | ENABLE_STRICT_OBJC_MSGSEND = YES; 247 | GCC_C_LANGUAGE_STANDARD = gnu99; 248 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 249 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 250 | GCC_WARN_UNDECLARED_SELECTOR = YES; 251 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 252 | GCC_WARN_UNUSED_FUNCTION = YES; 253 | GCC_WARN_UNUSED_VARIABLE = YES; 254 | IPHONEOS_DEPLOYMENT_TARGET = 7.1; 255 | METAL_ENABLE_DEBUG_INFO = NO; 256 | SDKROOT = iphoneos; 257 | VALIDATE_PRODUCT = YES; 258 | }; 259 | name = Release; 260 | }; 261 | 63FEA3EB193E888700400515 /* Debug */ = { 262 | isa = XCBuildConfiguration; 263 | buildSettings = { 264 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 265 | ASSETCATALOG_COMPILER_LAUNCHIMAGE_NAME = LaunchImage; 266 | INFOPLIST_FILE = SwiftTweetGettr/Info.plist; 267 | IPHONEOS_DEPLOYMENT_TARGET = 7.1; 268 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 269 | PRODUCT_NAME = "$(TARGET_NAME)"; 270 | }; 271 | name = Debug; 272 | }; 273 | 63FEA3EC193E888700400515 /* Release */ = { 274 | isa = XCBuildConfiguration; 275 | buildSettings = { 276 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 277 | ASSETCATALOG_COMPILER_LAUNCHIMAGE_NAME = LaunchImage; 278 | INFOPLIST_FILE = SwiftTweetGettr/Info.plist; 279 | IPHONEOS_DEPLOYMENT_TARGET = 7.1; 280 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 281 | PRODUCT_NAME = "$(TARGET_NAME)"; 282 | }; 283 | name = Release; 284 | }; 285 | /* End XCBuildConfiguration section */ 286 | 287 | /* Begin XCConfigurationList section */ 288 | 63FEA3C9193E888700400515 /* Build configuration list for PBXProject "SwiftTweetGettr" */ = { 289 | isa = XCConfigurationList; 290 | buildConfigurations = ( 291 | 63FEA3E8193E888700400515 /* Debug */, 292 | 63FEA3E9193E888700400515 /* Release */, 293 | ); 294 | defaultConfigurationIsVisible = 0; 295 | defaultConfigurationName = Release; 296 | }; 297 | 63FEA3EA193E888700400515 /* Build configuration list for PBXNativeTarget "SwiftTweetGettr" */ = { 298 | isa = XCConfigurationList; 299 | buildConfigurations = ( 300 | 63FEA3EB193E888700400515 /* Debug */, 301 | 63FEA3EC193E888700400515 /* Release */, 302 | ); 303 | defaultConfigurationIsVisible = 0; 304 | defaultConfigurationName = Release; 305 | }; 306 | /* End XCConfigurationList section */ 307 | }; 308 | rootObject = 63FEA3C6193E888700400515 /* Project object */; 309 | } 310 | -------------------------------------------------------------------------------- /SwiftTweetGettr.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /SwiftTweetGettr.xcodeproj/project.xcworkspace/xcshareddata/SwiftTweetGettr.xccheckout: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDESourceControlProjectFavoriteDictionaryKey 6 | 7 | IDESourceControlProjectIdentifier 8 | 9CE29D9A-3DD9-4C4E-8A03-40BE1DF28E59 9 | IDESourceControlProjectName 10 | SwiftTweetGettr 11 | IDESourceControlProjectOriginsDictionary 12 | 13 | 3FE94A3C848621FE5779988776BE974E4605D262 14 | https://github.com/jmenter/SwiftTweetGettr.git 15 | 16 | IDESourceControlProjectPath 17 | SwiftTweetGettr.xcodeproj 18 | IDESourceControlProjectRelativeInstallPathDictionary 19 | 20 | 3FE94A3C848621FE5779988776BE974E4605D262 21 | ../.. 22 | 23 | IDESourceControlProjectURL 24 | https://github.com/jmenter/SwiftTweetGettr.git 25 | IDESourceControlProjectVersion 26 | 111 27 | IDESourceControlProjectWCCIdentifier 28 | 3FE94A3C848621FE5779988776BE974E4605D262 29 | IDESourceControlProjectWCConfigurations 30 | 31 | 32 | IDESourceControlRepositoryExtensionIdentifierKey 33 | public.vcs.git 34 | IDESourceControlWCCIdentifierKey 35 | 3FE94A3C848621FE5779988776BE974E4605D262 36 | IDESourceControlWCCName 37 | SwiftTweetGettr 38 | 39 | 40 | 41 | 42 | -------------------------------------------------------------------------------- /SwiftTweetGettr.xcodeproj/project.xcworkspace/xcuserdata/jmenter.xcuserdatad/UserInterfaceState.xcuserstate: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jmenter/SwiftTweetGettr/5ab12260bb5227cb596f2e7db0a71e161ab96fbf/SwiftTweetGettr.xcodeproj/project.xcworkspace/xcuserdata/jmenter.xcuserdatad/UserInterfaceState.xcuserstate -------------------------------------------------------------------------------- /SwiftTweetGettr.xcodeproj/xcuserdata/jmenter.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 8 | 14 | 15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /SwiftTweetGettr.xcodeproj/xcuserdata/jmenter.xcuserdatad/xcschemes/SwiftTweetGettr.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 33 | 39 | 40 | 41 | 42 | 43 | 49 | 50 | 51 | 52 | 61 | 62 | 68 | 69 | 70 | 71 | 72 | 73 | 79 | 80 | 86 | 87 | 88 | 89 | 91 | 92 | 95 | 96 | 97 | -------------------------------------------------------------------------------- /SwiftTweetGettr.xcodeproj/xcuserdata/jmenter.xcuserdatad/xcschemes/xcschememanagement.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | SchemeUserState 6 | 7 | SwiftTweetGettr.xcscheme 8 | 9 | orderHint 10 | 0 11 | 12 | 13 | SuppressBuildableAutocreation 14 | 15 | 63FEA3CD193E888700400515 16 | 17 | primary 18 | 19 | 20 | 63FEA3DF193E888700400515 21 | 22 | primary 23 | 24 | 25 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /SwiftTweetGettr/AppDelegate.swift: -------------------------------------------------------------------------------- 1 | 2 | import UIKit 3 | 4 | @UIApplicationMain 5 | class AppDelegate: UIResponder, UIApplicationDelegate { 6 | 7 | var window: UIWindow? 8 | 9 | func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject : AnyObject]?) -> Bool 10 | { 11 | let greenHue:CGFloat = 123.0/360.0 12 | self.window?.tintColor = UIColor(hue: greenHue, saturation: 0.5, brightness: 0.5, alpha: 1.0) 13 | UINavigationBar.appearance().barTintColor = UIColor(hue: greenHue, saturation: 0.05, brightness: 0.95, alpha: 1.0) 14 | return true 15 | } 16 | } -------------------------------------------------------------------------------- /SwiftTweetGettr/Base.lproj/Main.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 47 | 54 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | Lorem ipsum dolor sit er elit lamet, consectetaur cillium adipisicing pecu, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum. Nam liber te conscient to factor tum poen legum odioque civiuda. 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | -------------------------------------------------------------------------------- /SwiftTweetGettr/Extensions.swift: -------------------------------------------------------------------------------- 1 | 2 | import UIKit 3 | 4 | extension NSURLResponse { 5 | 6 | func isHTTPResponseValid() -> Bool 7 | { 8 | if let response = self as? NSHTTPURLResponse { 9 | return (response.statusCode >= 200 && response.statusCode <= 299) 10 | } 11 | return false 12 | } 13 | } 14 | 15 | extension NSData { 16 | 17 | func json() -> AnyObject 18 | { 19 | return NSJSONSerialization.JSONObjectWithData(self, options: nil, error: nil)! 20 | } 21 | } 22 | 23 | extension UITableView { 24 | 25 | func scrollToTop(#animated: Bool) 26 | { 27 | scrollRectToVisible(CGRectMake(0, 0, 1, 0), animated: true) 28 | } 29 | } 30 | 31 | extension UIViewController { 32 | 33 | func showAlertViewWithMessage(message : String) 34 | { 35 | var alertController = UIAlertController(title: "Oops!", message: message, preferredStyle: UIAlertControllerStyle.Alert) 36 | alertController.addAction(UIAlertAction(title: "OK", style: .Default, handler: nil)) 37 | presentViewController(alertController, animated: true, completion: nil) 38 | } 39 | } 40 | 41 | extension String { 42 | 43 | func data() -> NSData 44 | { 45 | return dataUsingEncoding(NSUTF8StringEncoding, allowLossyConversion: false)! 46 | } 47 | 48 | func base64Encoded() -> String 49 | { 50 | return data().base64EncodedStringWithOptions(nil) 51 | } 52 | 53 | func createURL() -> NSURL 54 | { 55 | return NSURL(string: self)! 56 | } 57 | 58 | func stringByRemovingWhitespace() -> String 59 | { 60 | let trimmed = self.stringByTrimmingCharactersInSet(NSCharacterSet.whitespaceAndNewlineCharacterSet()) 61 | return trimmed.stringByReplacingOccurrencesOfString(" ", withString: "", options: nil, range: nil) 62 | } 63 | } 64 | 65 | extension NSMutableURLRequest { 66 | 67 | class func getRequestWithURL(url:NSURL) -> NSMutableURLRequest 68 | { 69 | var request = NSMutableURLRequest(URL: url) 70 | request.HTTPMethod = "GET" 71 | return request 72 | } 73 | 74 | class func postRequestWithURL(url:NSURL, body:String) -> NSMutableURLRequest 75 | { 76 | var request = NSMutableURLRequest(URL: url) 77 | request.HTTPMethod = "POST" 78 | request.HTTPBody = body.data() 79 | return request 80 | } 81 | } 82 | 83 | extension UIImageView { 84 | 85 | func loadURL(url:String) { 86 | NSURLConnection.sendAsynchronousRequest(NSURLRequest(URL: NSURL(string: url)!), queue: NSOperationQueue.mainQueue()) { (response, data, error) -> Void in 87 | if response.isHTTPResponseValid() { 88 | if let image = UIImage(data: data) { 89 | self.image = image 90 | } 91 | } 92 | } 93 | } 94 | } -------------------------------------------------------------------------------- /SwiftTweetGettr/Images.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "iphone", 5 | "size" : "29x29", 6 | "scale" : "2x" 7 | }, 8 | { 9 | "idiom" : "iphone", 10 | "size" : "40x40", 11 | "scale" : "2x" 12 | }, 13 | { 14 | "idiom" : "iphone", 15 | "size" : "60x60", 16 | "scale" : "2x" 17 | }, 18 | { 19 | "idiom" : "iphone", 20 | "size" : "60x60", 21 | "scale" : "3x" 22 | } 23 | ], 24 | "info" : { 25 | "version" : 1, 26 | "author" : "xcode" 27 | } 28 | } -------------------------------------------------------------------------------- /SwiftTweetGettr/Images.xcassets/LaunchImage.launchimage/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "orientation" : "portrait", 5 | "idiom" : "iphone", 6 | "extent" : "full-screen", 7 | "minimum-system-version" : "7.0", 8 | "scale" : "2x" 9 | }, 10 | { 11 | "orientation" : "portrait", 12 | "idiom" : "iphone", 13 | "subtype" : "retina4", 14 | "extent" : "full-screen", 15 | "minimum-system-version" : "7.0", 16 | "scale" : "2x" 17 | } 18 | ], 19 | "info" : { 20 | "version" : 1, 21 | "author" : "xcode" 22 | } 23 | } -------------------------------------------------------------------------------- /SwiftTweetGettr/Images.xcassets/default.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x" 6 | }, 7 | { 8 | "idiom" : "universal", 9 | "scale" : "2x", 10 | "filename" : "default@2x.png" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /SwiftTweetGettr/Images.xcassets/default.imageset/default@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jmenter/SwiftTweetGettr/5ab12260bb5227cb596f2e7db0a71e161ab96fbf/SwiftTweetGettr/Images.xcassets/default.imageset/default@2x.png -------------------------------------------------------------------------------- /SwiftTweetGettr/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | ${EXECUTABLE_NAME} 9 | CFBundleIdentifier 10 | com.yournamehere.${PRODUCT_NAME:rfc1034identifier} 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | ${PRODUCT_NAME} 15 | CFBundlePackageType 16 | APPL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1 23 | LSRequiresIPhoneOS 24 | 25 | UILaunchStoryboardName 26 | Main 27 | UIMainStoryboardFile 28 | Main 29 | UIRequiredDeviceCapabilities 30 | 31 | armv7 32 | 33 | 34 | 35 | -------------------------------------------------------------------------------- /SwiftTweetGettr/Tweet.swift: -------------------------------------------------------------------------------- 1 | 2 | import UIKit 3 | 4 | class Tweet { 5 | 6 | class func tweetsFromArray(from: Array>) -> Array 7 | { 8 | return from.map( { Tweet(tweetDictionary: $0) } ) 9 | } 10 | 11 | private let tweetDictionary:Dictionary 12 | 13 | var text:String { return tweetDictionary["text"] as! String } 14 | var createdAt:String { return tweetDictionary["created_at"] as! String } 15 | var name:String { return user()["name"] as! String } 16 | var screenName:String { return user()["screen_name"] as! String } 17 | var description:String { return tweetDictionary.description } 18 | var userImage = UIImage(named: "default") 19 | var profileImageURL:String? { return user()["profile_image_url"] as? String } 20 | var biggerProfileImageURL:String? { 21 | if let url = user()["profile_image_url"] as? String { 22 | return url.stringByReplacingOccurrencesOfString("_normal", withString: "_bigger") 23 | } 24 | return nil 25 | } 26 | var originalProfileImageURL:String? { 27 | if let url = user()["profile_image_url"] as? String { 28 | return url.stringByReplacingOccurrencesOfString("_normal", withString: "") 29 | } 30 | return nil 31 | } 32 | 33 | init(tweetDictionary:Dictionary) 34 | { 35 | self.tweetDictionary = tweetDictionary 36 | TwitterClient.fetchImageAtURL(biggerProfileImageURL!, success: { (image) -> Void in 37 | self.userImage = image 38 | }) 39 | } 40 | 41 | private func user() -> Dictionary 42 | { 43 | return tweetDictionary["user"] as! Dictionary 44 | } 45 | 46 | } -------------------------------------------------------------------------------- /SwiftTweetGettr/TweetCell.swift: -------------------------------------------------------------------------------- 1 | 2 | import UIKit 3 | 4 | class TweetCell : UITableViewCell { 5 | 6 | @IBOutlet weak var userImageView: UIImageView! 7 | @IBOutlet weak var nameTextField: UILabel! 8 | @IBOutlet weak var handleTextField: UILabel! 9 | @IBOutlet weak var statusTextField: UILabel! 10 | 11 | func applyTweet(tweet:Tweet) -> Void 12 | { 13 | nameTextField.text = tweet.name 14 | handleTextField.text = tweet.screenName 15 | statusTextField.text = tweet.text 16 | userImageView.image = tweet.userImage 17 | userImageView.loadURL(tweet.biggerProfileImageURL!) 18 | } 19 | } -------------------------------------------------------------------------------- /SwiftTweetGettr/TweetsTableViewDelegate.swift: -------------------------------------------------------------------------------- 1 | 2 | import UIKit 3 | 4 | class TweetsTableViewDelegate : NSObject, UITableViewDataSource, UITableViewDelegate { 5 | 6 | var tweets = Array() 7 | 8 | func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int 9 | { 10 | return tweets.count 11 | } 12 | 13 | func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell 14 | { 15 | let cell = tableView.dequeueReusableCellWithIdentifier("tweetCell", forIndexPath: indexPath) as! TweetCell 16 | let tweet = tweets[indexPath.row] 17 | 18 | cell.applyTweet(tweet) 19 | 20 | return cell 21 | } 22 | 23 | } -------------------------------------------------------------------------------- /SwiftTweetGettr/TwitterAuthorization.swift: -------------------------------------------------------------------------------- 1 | 2 | import UIKit 3 | 4 | private let kAuthorizationTokenStorageKey = "authorizationToken" 5 | 6 | class TwitterAuthorization { 7 | 8 | class func token() -> String? 9 | { 10 | return NSUserDefaults.standardUserDefaults().stringForKey(kAuthorizationTokenStorageKey) 11 | } 12 | 13 | class func setToken(token:String?) -> Void 14 | { 15 | if let actuallyToken = token { 16 | NSUserDefaults.standardUserDefaults().setObject(token, forKey: kAuthorizationTokenStorageKey) 17 | } else { 18 | NSUserDefaults.standardUserDefaults().removeObjectForKey(kAuthorizationTokenStorageKey) 19 | } 20 | NSUserDefaults.standardUserDefaults().synchronize() 21 | } 22 | 23 | class func hasToken() -> Bool 24 | { 25 | if let actuallyToken = token() { 26 | return true 27 | } 28 | return false 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /SwiftTweetGettr/TwitterClient.swift: -------------------------------------------------------------------------------- 1 | 2 | import UIKit 3 | 4 | private let kAPIKey = "" 5 | private let kAPISecret = "" 6 | private let kPostMethod = "POST" 7 | private let kGetMethod = "GET" 8 | private let kContentTypeHeader = "Content-Type" 9 | private let kAuthorizationHeaderKey = "Authorization" 10 | private let kOAuthRootURL = "https://api.twitter.com/oauth2/token" 11 | private let kTimelineRootURL = "https://api.twitter.com/1.1/statuses/user_timeline.json?count=30&screen_name=" 12 | private let kAuthorizationBody = "grant_type=client_credentials" 13 | private let kAuthorizationContentType = "application/x-www-form-urlencoded;charset=UTF-8" 14 | 15 | class TwitterClient { 16 | 17 | class func fetchAuthorizationToken(#success:() -> Void, failure:(String) -> Void) 18 | { 19 | var tokenRequest = NSMutableURLRequest.postRequestWithURL(kOAuthRootURL.createURL(), body: kAuthorizationBody) 20 | tokenRequest.addValue(kAuthorizationContentType, forHTTPHeaderField: kContentTypeHeader) 21 | tokenRequest.addValue(headerForAuthorization(), forHTTPHeaderField: kAuthorizationHeaderKey) 22 | 23 | NSURLConnection.sendAsynchronousRequest(tokenRequest, queue: NSOperationQueue.mainQueue(), completionHandler:{ (response: NSURLResponse!, data: NSData!, error: NSError!) -> Void in 24 | if response.isHTTPResponseValid() { 25 | TwitterAuthorization.setToken(data.json()["access_token"] as? String) 26 | if TwitterAuthorization.hasToken() { 27 | success() 28 | } else { 29 | failure("response has no access_token") 30 | } 31 | } else { 32 | self.handleFailure(failure, error: error, response: response) 33 | } 34 | }) 35 | } 36 | 37 | class func fetchTweetsForUser(userName:String, success:(Array) -> Void, failure:(String) -> Void) 38 | { 39 | var tweetRequest = NSMutableURLRequest.getRequestWithURL((kTimelineRootURL + userName.stringByRemovingWhitespace()).createURL()) 40 | tweetRequest.addValue(headerWithAuthorization(), forHTTPHeaderField: kAuthorizationHeaderKey) 41 | 42 | NSURLConnection.sendAsynchronousRequest(tweetRequest, queue: NSOperationQueue.mainQueue(), completionHandler:{ (response: NSURLResponse!, data: NSData!, error: NSError!) -> Void in 43 | if response.isHTTPResponseValid() { 44 | if let results:Array> = data.json() as? Array { 45 | success(Tweet.tweetsFromArray(results)) 46 | } 47 | } else { 48 | self.handleFailure(failure, error: error, response: response) 49 | } 50 | }) 51 | } 52 | 53 | class func fetchImageAtURL(url:String, success:(UIImage?) -> Void) -> Void 54 | { 55 | NSURLConnection.sendAsynchronousRequest(NSURLRequest(URL: NSURL(string: url)!), queue: NSOperationQueue.mainQueue()) { (response, data, error) -> Void in 56 | if response.isHTTPResponseValid() { 57 | success(UIImage(data: data)) 58 | } 59 | } 60 | } 61 | 62 | private class func handleFailure(failure:(String) -> Void, error:NSError!, response: NSURLResponse!) -> Void 63 | { 64 | if let actuallyError = error { 65 | failure(actuallyError.description) 66 | } else if let actuallyResponse = response { 67 | failure(actuallyResponse.description) 68 | } else { 69 | failure("no response or error") 70 | } 71 | } 72 | 73 | private class func headerForAuthorization() -> String 74 | { 75 | return "Basic " + (kAPIKey + ":" + kAPISecret).base64Encoded() 76 | } 77 | 78 | private class func headerWithAuthorization() -> String 79 | { 80 | return "Bearer " + TwitterAuthorization.token()! 81 | } 82 | 83 | } 84 | -------------------------------------------------------------------------------- /SwiftTweetGettr/ViewController.swift: -------------------------------------------------------------------------------- 1 | 2 | import UIKit 3 | 4 | class ViewController : UIViewController, UITextFieldDelegate { 5 | 6 | var spinner = UIActivityIndicatorView(activityIndicatorStyle: .Gray) 7 | var tweetsTableViewDelegate = TweetsTableViewDelegate() 8 | 9 | @IBOutlet var textField : UITextField! 10 | @IBOutlet var button : UIButton! 11 | @IBOutlet var tableView : UITableView! 12 | 13 | override func viewDidLoad() 14 | { 15 | super.viewDidLoad() 16 | 17 | textField.rightView = spinner 18 | textField.rightViewMode = .Always 19 | 20 | button.layer.cornerRadius = 5 21 | button.layer.borderWidth = 1 22 | button.layer.borderColor = button.titleLabel?.textColor.CGColor 23 | 24 | tableView.dataSource = tweetsTableViewDelegate 25 | tableView.delegate = tweetsTableViewDelegate 26 | } 27 | 28 | override func viewDidAppear(animated: Bool) 29 | { 30 | super.viewDidAppear(animated) 31 | 32 | if let selected = tableView.indexPathForSelectedRow() { 33 | tableView.deselectRowAtIndexPath(tableView.indexPathForSelectedRow()!, animated: animated) 34 | } 35 | if textField.text.isEmpty { 36 | textField.becomeFirstResponder() 37 | } 38 | } 39 | 40 | override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) 41 | { 42 | let index = tableView.indexPathForSelectedRow()?.row 43 | let tweet = tweetsTableViewDelegate.tweets[index!] 44 | (segue.destinationViewController.view as! UITextView).text = tweet.description 45 | } 46 | 47 | func textFieldShouldReturn(textField: UITextField) -> Bool 48 | { 49 | if !textField.text.isEmpty { 50 | buttonWasTapped(nil) 51 | } 52 | return textField.resignFirstResponder() 53 | } 54 | 55 | @IBAction func textFieldDidChange(sender : AnyObject) 56 | { 57 | button.enabled = !textField.text.isEmpty 58 | button.layer.borderColor = button.titleLabel?.textColor.CGColor 59 | } 60 | 61 | @IBAction func buttonWasTapped(sender : AnyObject?) 62 | { 63 | textField.resignFirstResponder() 64 | spinner.startAnimating() 65 | 66 | if TwitterAuthorization.hasToken() { 67 | fetchTweets() 68 | } else { 69 | TwitterClient.fetchAuthorizationToken(success: { () -> Void in 70 | self.fetchTweets() 71 | }, failure: { (message) -> Void in 72 | self.showAlertViewWithMessage("Something went wrong getting token. \(message)") 73 | self.spinner.stopAnimating() 74 | }) 75 | } 76 | } 77 | 78 | func fetchTweets() { 79 | TwitterClient.fetchTweetsForUser(textField.text.stringByRemovingWhitespace(), success: { (tweets) -> Void in 80 | self.tweetsTableViewDelegate.tweets = tweets 81 | self.tableView.reloadSections(NSIndexSet(index: 0), withRowAnimation: UITableViewRowAnimation.Fade) 82 | self.tableView.scrollToTop(animated: true) 83 | self.spinner.stopAnimating() 84 | }, failure: { (message) -> Void in 85 | self.showAlertViewWithMessage("Something went wrong getting tweets. \(message)") 86 | self.spinner.stopAnimating() 87 | }) 88 | } 89 | 90 | } 91 | 92 | --------------------------------------------------------------------------------