├── .gitignore ├── CleanArchitecture.xcodeproj ├── project.pbxproj └── project.xcworkspace │ ├── contents.xcworkspacedata │ └── xcshareddata │ └── IDEWorkspaceChecks.plist ├── CleanArchitecture ├── API │ ├── JLTimelineAPI.h │ ├── JLTimelineAPI.m │ └── Request │ │ ├── JLTimelineRequest.h │ │ └── JLTimelineRequest.m ├── AppDelegate.h ├── AppDelegate.m ├── Assets.xcassets │ └── AppIcon.appiconset │ │ └── Contents.json ├── Base.lproj │ └── LaunchScreen.storyboard ├── Features │ └── Timeline │ │ ├── JLTimelineViewController.h │ │ └── JLTimelineViewController.m ├── Info.plist ├── Service │ ├── Authentication │ │ ├── JLAuthenticationInfo.h │ │ └── JLAuthenticationInfo.m │ ├── JLTimelineService.h │ ├── JLTimelineService.m │ └── Model │ │ ├── JLTweet.h │ │ └── JLTweet.m └── main.m ├── CleanArchitectureTests ├── CleanArchitectureTests.m └── Info.plist ├── CleanArchitectureUITests ├── CleanArchitectureUITests.m └── Info.plist └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | # Xcode 2 | # 3 | # gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore 4 | 5 | ## User settings 6 | xcuserdata/ 7 | 8 | ## compatibility with Xcode 8 and earlier (ignoring not required starting Xcode 9) 9 | *.xcscmblueprint 10 | *.xccheckout 11 | 12 | ## compatibility with Xcode 3 and earlier (ignoring not required starting Xcode 4) 13 | build/ 14 | DerivedData/ 15 | *.moved-aside 16 | *.pbxuser 17 | !default.pbxuser 18 | *.mode1v3 19 | !default.mode1v3 20 | *.mode2v3 21 | !default.mode2v3 22 | *.perspectivev3 23 | !default.perspectivev3 24 | 25 | ## Obj-C/Swift specific 26 | *.hmap 27 | 28 | ## App packaging 29 | *.ipa 30 | *.dSYM.zip 31 | *.dSYM 32 | 33 | # CocoaPods 34 | # 35 | # We recommend against adding the Pods directory to your .gitignore. However 36 | # you should judge for yourself, the pros and cons are mentioned at: 37 | # https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control 38 | # 39 | # Pods/ 40 | # 41 | # Add this line if you want to avoid checking in source code from the Xcode workspace 42 | # *.xcworkspace 43 | 44 | # Carthage 45 | # 46 | # Add this line if you want to avoid checking in source code from Carthage dependencies. 47 | # Carthage/Checkouts 48 | 49 | Carthage/Build/ 50 | 51 | # fastlane 52 | # 53 | # It is recommended to not store the screenshots in the git repo. 54 | # Instead, use fastlane to re-generate the screenshots whenever they are needed. 55 | # For more information about the recommended setup visit: 56 | # https://docs.fastlane.tools/best-practices/source-control/#source-control 57 | 58 | fastlane/report.xml 59 | fastlane/Preview.html 60 | fastlane/screenshots/**/*.png 61 | fastlane/test_output 62 | 63 | # Code Injection 64 | # 65 | # After new code Injection tools there's a generated folder /iOSInjectionProject 66 | # https://github.com/johnno1962/injectionforxcode 67 | 68 | iOSInjectionProject/ 69 | 70 | # General 71 | .DS_Store 72 | .AppleDouble 73 | .LSOverride 74 | 75 | # Icon must end with two \r 76 | Icon 77 | 78 | 79 | # Thumbnails 80 | ._* 81 | 82 | # Files that might appear in the root of a volume 83 | .DocumentRevisions-V100 84 | .fseventsd 85 | .Spotlight-V100 86 | .TemporaryItems 87 | .Trashes 88 | .VolumeIcon.icns 89 | .com.apple.timemachine.donotpresent 90 | 91 | # Directories potentially created on remote AFP share 92 | .AppleDB 93 | .AppleDesktop 94 | Network Trash Folder 95 | Temporary Items 96 | .apdisk -------------------------------------------------------------------------------- /CleanArchitecture.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 46; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | B31043C61C8F353F001F6E00 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = B31043C51C8F353F001F6E00 /* main.m */; }; 11 | B31043C91C8F353F001F6E00 /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = B31043C81C8F353F001F6E00 /* AppDelegate.m */; }; 12 | B31043D11C8F353F001F6E00 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = B31043D01C8F353F001F6E00 /* Assets.xcassets */; }; 13 | B31043D41C8F353F001F6E00 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = B31043D21C8F353F001F6E00 /* LaunchScreen.storyboard */; }; 14 | B31043DF1C8F353F001F6E00 /* CleanArchitectureTests.m in Sources */ = {isa = PBXBuildFile; fileRef = B31043DE1C8F353F001F6E00 /* CleanArchitectureTests.m */; }; 15 | B31043EA1C8F353F001F6E00 /* CleanArchitectureUITests.m in Sources */ = {isa = PBXBuildFile; fileRef = B31043E91C8F353F001F6E00 /* CleanArchitectureUITests.m */; }; 16 | B31044001C8F3661001F6E00 /* JLTimelineViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = B31043FF1C8F3661001F6E00 /* JLTimelineViewController.m */; }; 17 | B31044011C8F3661001F6E00 /* JLTimelineViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = B31043FF1C8F3661001F6E00 /* JLTimelineViewController.m */; }; 18 | B31044021C8F3661001F6E00 /* JLTimelineViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = B31043FF1C8F3661001F6E00 /* JLTimelineViewController.m */; }; 19 | B31044051C8F368C001F6E00 /* JLTimelineService.m in Sources */ = {isa = PBXBuildFile; fileRef = B31044041C8F368C001F6E00 /* JLTimelineService.m */; }; 20 | B31044061C8F368C001F6E00 /* JLTimelineService.m in Sources */ = {isa = PBXBuildFile; fileRef = B31044041C8F368C001F6E00 /* JLTimelineService.m */; }; 21 | B31044071C8F368C001F6E00 /* JLTimelineService.m in Sources */ = {isa = PBXBuildFile; fileRef = B31044041C8F368C001F6E00 /* JLTimelineService.m */; }; 22 | B310440B1C8F36FF001F6E00 /* JLTweet.m in Sources */ = {isa = PBXBuildFile; fileRef = B310440A1C8F36FF001F6E00 /* JLTweet.m */; }; 23 | B310440C1C8F36FF001F6E00 /* JLTweet.m in Sources */ = {isa = PBXBuildFile; fileRef = B310440A1C8F36FF001F6E00 /* JLTweet.m */; }; 24 | B310440D1C8F36FF001F6E00 /* JLTweet.m in Sources */ = {isa = PBXBuildFile; fileRef = B310440A1C8F36FF001F6E00 /* JLTweet.m */; }; 25 | B31044101C8F3776001F6E00 /* JLTimelineAPI.m in Sources */ = {isa = PBXBuildFile; fileRef = B310440F1C8F3776001F6E00 /* JLTimelineAPI.m */; }; 26 | B31044111C8F3776001F6E00 /* JLTimelineAPI.m in Sources */ = {isa = PBXBuildFile; fileRef = B310440F1C8F3776001F6E00 /* JLTimelineAPI.m */; }; 27 | B31044121C8F3776001F6E00 /* JLTimelineAPI.m in Sources */ = {isa = PBXBuildFile; fileRef = B310440F1C8F3776001F6E00 /* JLTimelineAPI.m */; }; 28 | B31044181C8F37AE001F6E00 /* JLTimelineRequest.m in Sources */ = {isa = PBXBuildFile; fileRef = B31044171C8F37AE001F6E00 /* JLTimelineRequest.m */; }; 29 | B31044191C8F37AE001F6E00 /* JLTimelineRequest.m in Sources */ = {isa = PBXBuildFile; fileRef = B31044171C8F37AE001F6E00 /* JLTimelineRequest.m */; }; 30 | B310441A1C8F37AE001F6E00 /* JLTimelineRequest.m in Sources */ = {isa = PBXBuildFile; fileRef = B31044171C8F37AE001F6E00 /* JLTimelineRequest.m */; }; 31 | B310441E1C8F38A2001F6E00 /* JLAuthenticationInfo.m in Sources */ = {isa = PBXBuildFile; fileRef = B310441D1C8F38A2001F6E00 /* JLAuthenticationInfo.m */; }; 32 | B310441F1C8F38A2001F6E00 /* JLAuthenticationInfo.m in Sources */ = {isa = PBXBuildFile; fileRef = B310441D1C8F38A2001F6E00 /* JLAuthenticationInfo.m */; }; 33 | B31044201C8F38A2001F6E00 /* JLAuthenticationInfo.m in Sources */ = {isa = PBXBuildFile; fileRef = B310441D1C8F38A2001F6E00 /* JLAuthenticationInfo.m */; }; 34 | /* End PBXBuildFile section */ 35 | 36 | /* Begin PBXContainerItemProxy section */ 37 | B31043DB1C8F353F001F6E00 /* PBXContainerItemProxy */ = { 38 | isa = PBXContainerItemProxy; 39 | containerPortal = B31043B91C8F353F001F6E00 /* Project object */; 40 | proxyType = 1; 41 | remoteGlobalIDString = B31043C01C8F353F001F6E00; 42 | remoteInfo = CleanArchitecture; 43 | }; 44 | B31043E61C8F353F001F6E00 /* PBXContainerItemProxy */ = { 45 | isa = PBXContainerItemProxy; 46 | containerPortal = B31043B91C8F353F001F6E00 /* Project object */; 47 | proxyType = 1; 48 | remoteGlobalIDString = B31043C01C8F353F001F6E00; 49 | remoteInfo = CleanArchitecture; 50 | }; 51 | /* End PBXContainerItemProxy section */ 52 | 53 | /* Begin PBXFileReference section */ 54 | B31043C11C8F353F001F6E00 /* CleanArchitecture.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = CleanArchitecture.app; sourceTree = BUILT_PRODUCTS_DIR; }; 55 | B31043C51C8F353F001F6E00 /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; 56 | B31043C71C8F353F001F6E00 /* AppDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = ""; }; 57 | B31043C81C8F353F001F6E00 /* AppDelegate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = ""; }; 58 | B31043D01C8F353F001F6E00 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 59 | B31043D31C8F353F001F6E00 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; 60 | B31043D51C8F353F001F6E00 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 61 | B31043DA1C8F353F001F6E00 /* CleanArchitectureTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = CleanArchitectureTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 62 | B31043DE1C8F353F001F6E00 /* CleanArchitectureTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = CleanArchitectureTests.m; sourceTree = ""; }; 63 | B31043E01C8F353F001F6E00 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 64 | B31043E51C8F353F001F6E00 /* CleanArchitectureUITests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = CleanArchitectureUITests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 65 | B31043E91C8F353F001F6E00 /* CleanArchitectureUITests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = CleanArchitectureUITests.m; sourceTree = ""; }; 66 | B31043EB1C8F353F001F6E00 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 67 | B31043FE1C8F3661001F6E00 /* JLTimelineViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = JLTimelineViewController.h; sourceTree = ""; }; 68 | B31043FF1C8F3661001F6E00 /* JLTimelineViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = JLTimelineViewController.m; sourceTree = ""; }; 69 | B31044031C8F368C001F6E00 /* JLTimelineService.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = JLTimelineService.h; sourceTree = ""; }; 70 | B31044041C8F368C001F6E00 /* JLTimelineService.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = JLTimelineService.m; sourceTree = ""; }; 71 | B31044091C8F36FF001F6E00 /* JLTweet.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = JLTweet.h; sourceTree = ""; }; 72 | B310440A1C8F36FF001F6E00 /* JLTweet.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = JLTweet.m; sourceTree = ""; }; 73 | B310440E1C8F3776001F6E00 /* JLTimelineAPI.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = JLTimelineAPI.h; sourceTree = ""; }; 74 | B310440F1C8F3776001F6E00 /* JLTimelineAPI.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = JLTimelineAPI.m; sourceTree = ""; }; 75 | B31044161C8F37AE001F6E00 /* JLTimelineRequest.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = JLTimelineRequest.h; sourceTree = ""; }; 76 | B31044171C8F37AE001F6E00 /* JLTimelineRequest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = JLTimelineRequest.m; sourceTree = ""; }; 77 | B310441C1C8F38A2001F6E00 /* JLAuthenticationInfo.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = JLAuthenticationInfo.h; sourceTree = ""; }; 78 | B310441D1C8F38A2001F6E00 /* JLAuthenticationInfo.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = JLAuthenticationInfo.m; sourceTree = ""; }; 79 | /* End PBXFileReference section */ 80 | 81 | /* Begin PBXFrameworksBuildPhase section */ 82 | B31043BE1C8F353F001F6E00 /* Frameworks */ = { 83 | isa = PBXFrameworksBuildPhase; 84 | buildActionMask = 2147483647; 85 | files = ( 86 | ); 87 | runOnlyForDeploymentPostprocessing = 0; 88 | }; 89 | B31043D71C8F353F001F6E00 /* Frameworks */ = { 90 | isa = PBXFrameworksBuildPhase; 91 | buildActionMask = 2147483647; 92 | files = ( 93 | ); 94 | runOnlyForDeploymentPostprocessing = 0; 95 | }; 96 | B31043E21C8F353F001F6E00 /* Frameworks */ = { 97 | isa = PBXFrameworksBuildPhase; 98 | buildActionMask = 2147483647; 99 | files = ( 100 | ); 101 | runOnlyForDeploymentPostprocessing = 0; 102 | }; 103 | /* End PBXFrameworksBuildPhase section */ 104 | 105 | /* Begin PBXGroup section */ 106 | B31043B81C8F353F001F6E00 = { 107 | isa = PBXGroup; 108 | children = ( 109 | B31043C31C8F353F001F6E00 /* CleanArchitecture */, 110 | B31043DD1C8F353F001F6E00 /* CleanArchitectureTests */, 111 | B31043E81C8F353F001F6E00 /* CleanArchitectureUITests */, 112 | B31043C21C8F353F001F6E00 /* Products */, 113 | ); 114 | sourceTree = ""; 115 | }; 116 | B31043C21C8F353F001F6E00 /* Products */ = { 117 | isa = PBXGroup; 118 | children = ( 119 | B31043C11C8F353F001F6E00 /* CleanArchitecture.app */, 120 | B31043DA1C8F353F001F6E00 /* CleanArchitectureTests.xctest */, 121 | B31043E51C8F353F001F6E00 /* CleanArchitectureUITests.xctest */, 122 | ); 123 | name = Products; 124 | sourceTree = ""; 125 | }; 126 | B31043C31C8F353F001F6E00 /* CleanArchitecture */ = { 127 | isa = PBXGroup; 128 | children = ( 129 | B31043F71C8F35D5001F6E00 /* API */, 130 | B31043F81C8F35D5001F6E00 /* Features */, 131 | B31043FD1C8F35D5001F6E00 /* Service */, 132 | B31043D01C8F353F001F6E00 /* Assets.xcassets */, 133 | B31043C41C8F353F001F6E00 /* Supporting Files */, 134 | ); 135 | path = CleanArchitecture; 136 | sourceTree = ""; 137 | }; 138 | B31043C41C8F353F001F6E00 /* Supporting Files */ = { 139 | isa = PBXGroup; 140 | children = ( 141 | B31043C71C8F353F001F6E00 /* AppDelegate.h */, 142 | B31043C81C8F353F001F6E00 /* AppDelegate.m */, 143 | B31043D21C8F353F001F6E00 /* LaunchScreen.storyboard */, 144 | B31043D51C8F353F001F6E00 /* Info.plist */, 145 | B31043C51C8F353F001F6E00 /* main.m */, 146 | ); 147 | name = "Supporting Files"; 148 | sourceTree = ""; 149 | }; 150 | B31043DD1C8F353F001F6E00 /* CleanArchitectureTests */ = { 151 | isa = PBXGroup; 152 | children = ( 153 | B31043DE1C8F353F001F6E00 /* CleanArchitectureTests.m */, 154 | B31043E01C8F353F001F6E00 /* Info.plist */, 155 | ); 156 | path = CleanArchitectureTests; 157 | sourceTree = ""; 158 | }; 159 | B31043E81C8F353F001F6E00 /* CleanArchitectureUITests */ = { 160 | isa = PBXGroup; 161 | children = ( 162 | B31043E91C8F353F001F6E00 /* CleanArchitectureUITests.m */, 163 | B31043EB1C8F353F001F6E00 /* Info.plist */, 164 | ); 165 | path = CleanArchitectureUITests; 166 | sourceTree = ""; 167 | }; 168 | B31043F71C8F35D5001F6E00 /* API */ = { 169 | isa = PBXGroup; 170 | children = ( 171 | B310440E1C8F3776001F6E00 /* JLTimelineAPI.h */, 172 | B310440F1C8F3776001F6E00 /* JLTimelineAPI.m */, 173 | B31044141C8F379C001F6E00 /* Request */, 174 | ); 175 | path = API; 176 | sourceTree = ""; 177 | }; 178 | B31043F81C8F35D5001F6E00 /* Features */ = { 179 | isa = PBXGroup; 180 | children = ( 181 | B31043F91C8F35D5001F6E00 /* Timeline */, 182 | ); 183 | path = Features; 184 | sourceTree = ""; 185 | }; 186 | B31043F91C8F35D5001F6E00 /* Timeline */ = { 187 | isa = PBXGroup; 188 | children = ( 189 | B31043FE1C8F3661001F6E00 /* JLTimelineViewController.h */, 190 | B31043FF1C8F3661001F6E00 /* JLTimelineViewController.m */, 191 | ); 192 | path = Timeline; 193 | sourceTree = ""; 194 | }; 195 | B31043FD1C8F35D5001F6E00 /* Service */ = { 196 | isa = PBXGroup; 197 | children = ( 198 | B310441B1C8F3896001F6E00 /* Authentication */, 199 | B31044081C8F36EF001F6E00 /* Model */, 200 | B31044031C8F368C001F6E00 /* JLTimelineService.h */, 201 | B31044041C8F368C001F6E00 /* JLTimelineService.m */, 202 | ); 203 | path = Service; 204 | sourceTree = ""; 205 | }; 206 | B31044081C8F36EF001F6E00 /* Model */ = { 207 | isa = PBXGroup; 208 | children = ( 209 | B31044091C8F36FF001F6E00 /* JLTweet.h */, 210 | B310440A1C8F36FF001F6E00 /* JLTweet.m */, 211 | ); 212 | path = Model; 213 | sourceTree = ""; 214 | }; 215 | B31044141C8F379C001F6E00 /* Request */ = { 216 | isa = PBXGroup; 217 | children = ( 218 | B31044161C8F37AE001F6E00 /* JLTimelineRequest.h */, 219 | B31044171C8F37AE001F6E00 /* JLTimelineRequest.m */, 220 | ); 221 | path = Request; 222 | sourceTree = ""; 223 | }; 224 | B310441B1C8F3896001F6E00 /* Authentication */ = { 225 | isa = PBXGroup; 226 | children = ( 227 | B310441C1C8F38A2001F6E00 /* JLAuthenticationInfo.h */, 228 | B310441D1C8F38A2001F6E00 /* JLAuthenticationInfo.m */, 229 | ); 230 | path = Authentication; 231 | sourceTree = ""; 232 | }; 233 | /* End PBXGroup section */ 234 | 235 | /* Begin PBXNativeTarget section */ 236 | B31043C01C8F353F001F6E00 /* CleanArchitecture */ = { 237 | isa = PBXNativeTarget; 238 | buildConfigurationList = B31043EE1C8F353F001F6E00 /* Build configuration list for PBXNativeTarget "CleanArchitecture" */; 239 | buildPhases = ( 240 | B31043BD1C8F353F001F6E00 /* Sources */, 241 | B31043BE1C8F353F001F6E00 /* Frameworks */, 242 | B31043BF1C8F353F001F6E00 /* Resources */, 243 | ); 244 | buildRules = ( 245 | ); 246 | dependencies = ( 247 | ); 248 | name = CleanArchitecture; 249 | productName = CleanArchitecture; 250 | productReference = B31043C11C8F353F001F6E00 /* CleanArchitecture.app */; 251 | productType = "com.apple.product-type.application"; 252 | }; 253 | B31043D91C8F353F001F6E00 /* CleanArchitectureTests */ = { 254 | isa = PBXNativeTarget; 255 | buildConfigurationList = B31043F11C8F353F001F6E00 /* Build configuration list for PBXNativeTarget "CleanArchitectureTests" */; 256 | buildPhases = ( 257 | B31043D61C8F353F001F6E00 /* Sources */, 258 | B31043D71C8F353F001F6E00 /* Frameworks */, 259 | B31043D81C8F353F001F6E00 /* Resources */, 260 | ); 261 | buildRules = ( 262 | ); 263 | dependencies = ( 264 | B31043DC1C8F353F001F6E00 /* PBXTargetDependency */, 265 | ); 266 | name = CleanArchitectureTests; 267 | productName = CleanArchitectureTests; 268 | productReference = B31043DA1C8F353F001F6E00 /* CleanArchitectureTests.xctest */; 269 | productType = "com.apple.product-type.bundle.unit-test"; 270 | }; 271 | B31043E41C8F353F001F6E00 /* CleanArchitectureUITests */ = { 272 | isa = PBXNativeTarget; 273 | buildConfigurationList = B31043F41C8F353F001F6E00 /* Build configuration list for PBXNativeTarget "CleanArchitectureUITests" */; 274 | buildPhases = ( 275 | B31043E11C8F353F001F6E00 /* Sources */, 276 | B31043E21C8F353F001F6E00 /* Frameworks */, 277 | B31043E31C8F353F001F6E00 /* Resources */, 278 | ); 279 | buildRules = ( 280 | ); 281 | dependencies = ( 282 | B31043E71C8F353F001F6E00 /* PBXTargetDependency */, 283 | ); 284 | name = CleanArchitectureUITests; 285 | productName = CleanArchitectureUITests; 286 | productReference = B31043E51C8F353F001F6E00 /* CleanArchitectureUITests.xctest */; 287 | productType = "com.apple.product-type.bundle.ui-testing"; 288 | }; 289 | /* End PBXNativeTarget section */ 290 | 291 | /* Begin PBXProject section */ 292 | B31043B91C8F353F001F6E00 /* Project object */ = { 293 | isa = PBXProject; 294 | attributes = { 295 | LastUpgradeCheck = 1230; 296 | ORGANIZATIONNAME = "Joshua Liebowitz"; 297 | TargetAttributes = { 298 | B31043C01C8F353F001F6E00 = { 299 | CreatedOnToolsVersion = 7.2.1; 300 | }; 301 | B31043D91C8F353F001F6E00 = { 302 | CreatedOnToolsVersion = 7.2.1; 303 | TestTargetID = B31043C01C8F353F001F6E00; 304 | }; 305 | B31043E41C8F353F001F6E00 = { 306 | CreatedOnToolsVersion = 7.2.1; 307 | TestTargetID = B31043C01C8F353F001F6E00; 308 | }; 309 | }; 310 | }; 311 | buildConfigurationList = B31043BC1C8F353F001F6E00 /* Build configuration list for PBXProject "CleanArchitecture" */; 312 | compatibilityVersion = "Xcode 3.2"; 313 | developmentRegion = en; 314 | hasScannedForEncodings = 0; 315 | knownRegions = ( 316 | en, 317 | Base, 318 | ); 319 | mainGroup = B31043B81C8F353F001F6E00; 320 | productRefGroup = B31043C21C8F353F001F6E00 /* Products */; 321 | projectDirPath = ""; 322 | projectRoot = ""; 323 | targets = ( 324 | B31043C01C8F353F001F6E00 /* CleanArchitecture */, 325 | B31043D91C8F353F001F6E00 /* CleanArchitectureTests */, 326 | B31043E41C8F353F001F6E00 /* CleanArchitectureUITests */, 327 | ); 328 | }; 329 | /* End PBXProject section */ 330 | 331 | /* Begin PBXResourcesBuildPhase section */ 332 | B31043BF1C8F353F001F6E00 /* Resources */ = { 333 | isa = PBXResourcesBuildPhase; 334 | buildActionMask = 2147483647; 335 | files = ( 336 | B31043D41C8F353F001F6E00 /* LaunchScreen.storyboard in Resources */, 337 | B31043D11C8F353F001F6E00 /* Assets.xcassets in Resources */, 338 | ); 339 | runOnlyForDeploymentPostprocessing = 0; 340 | }; 341 | B31043D81C8F353F001F6E00 /* Resources */ = { 342 | isa = PBXResourcesBuildPhase; 343 | buildActionMask = 2147483647; 344 | files = ( 345 | ); 346 | runOnlyForDeploymentPostprocessing = 0; 347 | }; 348 | B31043E31C8F353F001F6E00 /* Resources */ = { 349 | isa = PBXResourcesBuildPhase; 350 | buildActionMask = 2147483647; 351 | files = ( 352 | ); 353 | runOnlyForDeploymentPostprocessing = 0; 354 | }; 355 | /* End PBXResourcesBuildPhase section */ 356 | 357 | /* Begin PBXSourcesBuildPhase section */ 358 | B31043BD1C8F353F001F6E00 /* Sources */ = { 359 | isa = PBXSourcesBuildPhase; 360 | buildActionMask = 2147483647; 361 | files = ( 362 | B31044051C8F368C001F6E00 /* JLTimelineService.m in Sources */, 363 | B31044181C8F37AE001F6E00 /* JLTimelineRequest.m in Sources */, 364 | B310440B1C8F36FF001F6E00 /* JLTweet.m in Sources */, 365 | B310441E1C8F38A2001F6E00 /* JLAuthenticationInfo.m in Sources */, 366 | B31043C91C8F353F001F6E00 /* AppDelegate.m in Sources */, 367 | B31043C61C8F353F001F6E00 /* main.m in Sources */, 368 | B31044001C8F3661001F6E00 /* JLTimelineViewController.m in Sources */, 369 | B31044101C8F3776001F6E00 /* JLTimelineAPI.m in Sources */, 370 | ); 371 | runOnlyForDeploymentPostprocessing = 0; 372 | }; 373 | B31043D61C8F353F001F6E00 /* Sources */ = { 374 | isa = PBXSourcesBuildPhase; 375 | buildActionMask = 2147483647; 376 | files = ( 377 | B31044061C8F368C001F6E00 /* JLTimelineService.m in Sources */, 378 | B31043DF1C8F353F001F6E00 /* CleanArchitectureTests.m in Sources */, 379 | B31044191C8F37AE001F6E00 /* JLTimelineRequest.m in Sources */, 380 | B31044011C8F3661001F6E00 /* JLTimelineViewController.m in Sources */, 381 | B31044111C8F3776001F6E00 /* JLTimelineAPI.m in Sources */, 382 | B310441F1C8F38A2001F6E00 /* JLAuthenticationInfo.m in Sources */, 383 | B310440C1C8F36FF001F6E00 /* JLTweet.m in Sources */, 384 | ); 385 | runOnlyForDeploymentPostprocessing = 0; 386 | }; 387 | B31043E11C8F353F001F6E00 /* Sources */ = { 388 | isa = PBXSourcesBuildPhase; 389 | buildActionMask = 2147483647; 390 | files = ( 391 | B31044071C8F368C001F6E00 /* JLTimelineService.m in Sources */, 392 | B31043EA1C8F353F001F6E00 /* CleanArchitectureUITests.m in Sources */, 393 | B310441A1C8F37AE001F6E00 /* JLTimelineRequest.m in Sources */, 394 | B31044021C8F3661001F6E00 /* JLTimelineViewController.m in Sources */, 395 | B31044121C8F3776001F6E00 /* JLTimelineAPI.m in Sources */, 396 | B31044201C8F38A2001F6E00 /* JLAuthenticationInfo.m in Sources */, 397 | B310440D1C8F36FF001F6E00 /* JLTweet.m in Sources */, 398 | ); 399 | runOnlyForDeploymentPostprocessing = 0; 400 | }; 401 | /* End PBXSourcesBuildPhase section */ 402 | 403 | /* Begin PBXTargetDependency section */ 404 | B31043DC1C8F353F001F6E00 /* PBXTargetDependency */ = { 405 | isa = PBXTargetDependency; 406 | target = B31043C01C8F353F001F6E00 /* CleanArchitecture */; 407 | targetProxy = B31043DB1C8F353F001F6E00 /* PBXContainerItemProxy */; 408 | }; 409 | B31043E71C8F353F001F6E00 /* PBXTargetDependency */ = { 410 | isa = PBXTargetDependency; 411 | target = B31043C01C8F353F001F6E00 /* CleanArchitecture */; 412 | targetProxy = B31043E61C8F353F001F6E00 /* PBXContainerItemProxy */; 413 | }; 414 | /* End PBXTargetDependency section */ 415 | 416 | /* Begin PBXVariantGroup section */ 417 | B31043D21C8F353F001F6E00 /* LaunchScreen.storyboard */ = { 418 | isa = PBXVariantGroup; 419 | children = ( 420 | B31043D31C8F353F001F6E00 /* Base */, 421 | ); 422 | name = LaunchScreen.storyboard; 423 | sourceTree = ""; 424 | }; 425 | /* End PBXVariantGroup section */ 426 | 427 | /* Begin XCBuildConfiguration section */ 428 | B31043EC1C8F353F001F6E00 /* Debug */ = { 429 | isa = XCBuildConfiguration; 430 | buildSettings = { 431 | ALWAYS_SEARCH_USER_PATHS = NO; 432 | CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES; 433 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 434 | CLANG_CXX_LIBRARY = "libc++"; 435 | CLANG_ENABLE_MODULES = YES; 436 | CLANG_ENABLE_OBJC_ARC = YES; 437 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 438 | CLANG_WARN_BOOL_CONVERSION = YES; 439 | CLANG_WARN_COMMA = YES; 440 | CLANG_WARN_CONSTANT_CONVERSION = YES; 441 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 442 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 443 | CLANG_WARN_EMPTY_BODY = YES; 444 | CLANG_WARN_ENUM_CONVERSION = YES; 445 | CLANG_WARN_INFINITE_RECURSION = YES; 446 | CLANG_WARN_INT_CONVERSION = YES; 447 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 448 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 449 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 450 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 451 | CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; 452 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 453 | CLANG_WARN_STRICT_PROTOTYPES = YES; 454 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 455 | CLANG_WARN_UNREACHABLE_CODE = YES; 456 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 457 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 458 | COPY_PHASE_STRIP = NO; 459 | DEBUG_INFORMATION_FORMAT = dwarf; 460 | ENABLE_STRICT_OBJC_MSGSEND = YES; 461 | ENABLE_TESTABILITY = YES; 462 | GCC_C_LANGUAGE_STANDARD = gnu99; 463 | GCC_DYNAMIC_NO_PIC = NO; 464 | GCC_NO_COMMON_BLOCKS = YES; 465 | GCC_OPTIMIZATION_LEVEL = 0; 466 | GCC_PREPROCESSOR_DEFINITIONS = ( 467 | "DEBUG=1", 468 | "$(inherited)", 469 | ); 470 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 471 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 472 | GCC_WARN_UNDECLARED_SELECTOR = YES; 473 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 474 | GCC_WARN_UNUSED_FUNCTION = YES; 475 | GCC_WARN_UNUSED_VARIABLE = YES; 476 | IPHONEOS_DEPLOYMENT_TARGET = 12.0; 477 | MTL_ENABLE_DEBUG_INFO = YES; 478 | ONLY_ACTIVE_ARCH = YES; 479 | SDKROOT = iphoneos; 480 | }; 481 | name = Debug; 482 | }; 483 | B31043ED1C8F353F001F6E00 /* Release */ = { 484 | isa = XCBuildConfiguration; 485 | buildSettings = { 486 | ALWAYS_SEARCH_USER_PATHS = NO; 487 | CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES; 488 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 489 | CLANG_CXX_LIBRARY = "libc++"; 490 | CLANG_ENABLE_MODULES = YES; 491 | CLANG_ENABLE_OBJC_ARC = YES; 492 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 493 | CLANG_WARN_BOOL_CONVERSION = YES; 494 | CLANG_WARN_COMMA = YES; 495 | CLANG_WARN_CONSTANT_CONVERSION = YES; 496 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 497 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 498 | CLANG_WARN_EMPTY_BODY = YES; 499 | CLANG_WARN_ENUM_CONVERSION = YES; 500 | CLANG_WARN_INFINITE_RECURSION = YES; 501 | CLANG_WARN_INT_CONVERSION = YES; 502 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 503 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 504 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 505 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 506 | CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; 507 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 508 | CLANG_WARN_STRICT_PROTOTYPES = YES; 509 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 510 | CLANG_WARN_UNREACHABLE_CODE = YES; 511 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 512 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 513 | COPY_PHASE_STRIP = NO; 514 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 515 | ENABLE_NS_ASSERTIONS = NO; 516 | ENABLE_STRICT_OBJC_MSGSEND = YES; 517 | GCC_C_LANGUAGE_STANDARD = gnu99; 518 | GCC_NO_COMMON_BLOCKS = YES; 519 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 520 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 521 | GCC_WARN_UNDECLARED_SELECTOR = YES; 522 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 523 | GCC_WARN_UNUSED_FUNCTION = YES; 524 | GCC_WARN_UNUSED_VARIABLE = YES; 525 | IPHONEOS_DEPLOYMENT_TARGET = 12.0; 526 | MTL_ENABLE_DEBUG_INFO = NO; 527 | SDKROOT = iphoneos; 528 | VALIDATE_PRODUCT = YES; 529 | }; 530 | name = Release; 531 | }; 532 | B31043EF1C8F353F001F6E00 /* Debug */ = { 533 | isa = XCBuildConfiguration; 534 | buildSettings = { 535 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 536 | INFOPLIST_FILE = CleanArchitecture/Info.plist; 537 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 538 | PRODUCT_BUNDLE_IDENTIFIER = com.mydoghatestechnology.CleanArchitecture; 539 | PRODUCT_NAME = "$(TARGET_NAME)"; 540 | }; 541 | name = Debug; 542 | }; 543 | B31043F01C8F353F001F6E00 /* Release */ = { 544 | isa = XCBuildConfiguration; 545 | buildSettings = { 546 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 547 | INFOPLIST_FILE = CleanArchitecture/Info.plist; 548 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 549 | PRODUCT_BUNDLE_IDENTIFIER = com.mydoghatestechnology.CleanArchitecture; 550 | PRODUCT_NAME = "$(TARGET_NAME)"; 551 | }; 552 | name = Release; 553 | }; 554 | B31043F21C8F353F001F6E00 /* Debug */ = { 555 | isa = XCBuildConfiguration; 556 | buildSettings = { 557 | BUNDLE_LOADER = "$(TEST_HOST)"; 558 | INFOPLIST_FILE = CleanArchitectureTests/Info.plist; 559 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 560 | PRODUCT_BUNDLE_IDENTIFIER = com.mydoghatestechnology.CleanArchitectureTests; 561 | PRODUCT_NAME = "$(TARGET_NAME)"; 562 | TEST_HOST = "$(BUILT_PRODUCTS_DIR)/CleanArchitecture.app/CleanArchitecture"; 563 | }; 564 | name = Debug; 565 | }; 566 | B31043F31C8F353F001F6E00 /* Release */ = { 567 | isa = XCBuildConfiguration; 568 | buildSettings = { 569 | BUNDLE_LOADER = "$(TEST_HOST)"; 570 | INFOPLIST_FILE = CleanArchitectureTests/Info.plist; 571 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 572 | PRODUCT_BUNDLE_IDENTIFIER = com.mydoghatestechnology.CleanArchitectureTests; 573 | PRODUCT_NAME = "$(TARGET_NAME)"; 574 | TEST_HOST = "$(BUILT_PRODUCTS_DIR)/CleanArchitecture.app/CleanArchitecture"; 575 | }; 576 | name = Release; 577 | }; 578 | B31043F51C8F353F001F6E00 /* Debug */ = { 579 | isa = XCBuildConfiguration; 580 | buildSettings = { 581 | INFOPLIST_FILE = CleanArchitectureUITests/Info.plist; 582 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 583 | PRODUCT_BUNDLE_IDENTIFIER = com.mydoghatestechnology.CleanArchitectureUITests; 584 | PRODUCT_NAME = "$(TARGET_NAME)"; 585 | TEST_TARGET_NAME = CleanArchitecture; 586 | USES_XCTRUNNER = YES; 587 | }; 588 | name = Debug; 589 | }; 590 | B31043F61C8F353F001F6E00 /* Release */ = { 591 | isa = XCBuildConfiguration; 592 | buildSettings = { 593 | INFOPLIST_FILE = CleanArchitectureUITests/Info.plist; 594 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 595 | PRODUCT_BUNDLE_IDENTIFIER = com.mydoghatestechnology.CleanArchitectureUITests; 596 | PRODUCT_NAME = "$(TARGET_NAME)"; 597 | TEST_TARGET_NAME = CleanArchitecture; 598 | USES_XCTRUNNER = YES; 599 | }; 600 | name = Release; 601 | }; 602 | /* End XCBuildConfiguration section */ 603 | 604 | /* Begin XCConfigurationList section */ 605 | B31043BC1C8F353F001F6E00 /* Build configuration list for PBXProject "CleanArchitecture" */ = { 606 | isa = XCConfigurationList; 607 | buildConfigurations = ( 608 | B31043EC1C8F353F001F6E00 /* Debug */, 609 | B31043ED1C8F353F001F6E00 /* Release */, 610 | ); 611 | defaultConfigurationIsVisible = 0; 612 | defaultConfigurationName = Release; 613 | }; 614 | B31043EE1C8F353F001F6E00 /* Build configuration list for PBXNativeTarget "CleanArchitecture" */ = { 615 | isa = XCConfigurationList; 616 | buildConfigurations = ( 617 | B31043EF1C8F353F001F6E00 /* Debug */, 618 | B31043F01C8F353F001F6E00 /* Release */, 619 | ); 620 | defaultConfigurationIsVisible = 0; 621 | defaultConfigurationName = Release; 622 | }; 623 | B31043F11C8F353F001F6E00 /* Build configuration list for PBXNativeTarget "CleanArchitectureTests" */ = { 624 | isa = XCConfigurationList; 625 | buildConfigurations = ( 626 | B31043F21C8F353F001F6E00 /* Debug */, 627 | B31043F31C8F353F001F6E00 /* Release */, 628 | ); 629 | defaultConfigurationIsVisible = 0; 630 | defaultConfigurationName = Release; 631 | }; 632 | B31043F41C8F353F001F6E00 /* Build configuration list for PBXNativeTarget "CleanArchitectureUITests" */ = { 633 | isa = XCConfigurationList; 634 | buildConfigurations = ( 635 | B31043F51C8F353F001F6E00 /* Debug */, 636 | B31043F61C8F353F001F6E00 /* Release */, 637 | ); 638 | defaultConfigurationIsVisible = 0; 639 | defaultConfigurationName = Release; 640 | }; 641 | /* End XCConfigurationList section */ 642 | }; 643 | rootObject = B31043B91C8F353F001F6E00 /* Project object */; 644 | } 645 | -------------------------------------------------------------------------------- /CleanArchitecture.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /CleanArchitecture.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /CleanArchitecture/API/JLTimelineAPI.h: -------------------------------------------------------------------------------- 1 | // 2 | // JLTimelineAPI.h 3 | // CleanArchitecture 4 | // 5 | // Created by Joshua Liebowitz on 3/8/16. 6 | // Copyright © 2016 Joshua Liebowitz. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @class JLTimelineRequest; 12 | @class JLTweet; 13 | 14 | typedef void(^TimelineAPICompletion)(NSArray * __nullable tweets, NSError * __nullable error); 15 | 16 | @interface JLTimelineAPI : NSObject 17 | NS_ASSUME_NONNULL_BEGIN 18 | 19 | - (instancetype)initWithAuthenticationInfo:(JLAuthenticationInfo *)authenticationInfo NS_DESIGNATED_INITIALIZER; 20 | - (instancetype)init NS_UNAVAILABLE; 21 | - (void)fetchTimelineWithRequest:(JLTimelineRequest *)request completion:(TimelineAPICompletion)completion; 22 | 23 | NS_ASSUME_NONNULL_END 24 | 25 | @end 26 | -------------------------------------------------------------------------------- /CleanArchitecture/API/JLTimelineAPI.m: -------------------------------------------------------------------------------- 1 | // 2 | // JLTimelineAPI.m 3 | // CleanArchitecture 4 | // 5 | // Created by Joshua Liebowitz on 3/8/16. 6 | // Copyright © 2016 Joshua Liebowitz. All rights reserved. 7 | // 8 | 9 | #import "JLAuthenticationInfo.h" 10 | #import "JLTimelineAPI.h" 11 | #import "JLTweet.h" 12 | 13 | @interface JLTimelineAPI () 14 | 15 | @property (nonatomic, copy, readonly) JLAuthenticationInfo *authInfo; 16 | 17 | @end 18 | 19 | @implementation JLTimelineAPI 20 | 21 | - (instancetype)initWithAuthenticationInfo:(JLAuthenticationInfo *)authenticationInfo 22 | { 23 | if (self = [super init]) { 24 | _authInfo = [authenticationInfo copy]; 25 | } 26 | return self; 27 | 28 | } 29 | 30 | - (void)fetchTimelineWithRequest:(JLTimelineRequest *)request completion:(TimelineAPICompletion)completion 31 | { 32 | dispatch_queue_t userInteractiveQueue = dispatch_get_global_queue(QOS_CLASS_USER_INTERACTIVE, 0); 33 | dispatch_async(userInteractiveQueue, ^{ 34 | if (completion) { 35 | // networking code, json parsing, etc 36 | // pretend it takes 2 seconds 37 | [NSThread sleepForTimeInterval:2.0f]; 38 | JLTweet *tweet = [[JLTweet alloc] init]; 39 | completion(@[tweet], nil); 40 | } 41 | }); 42 | } 43 | 44 | @end 45 | -------------------------------------------------------------------------------- /CleanArchitecture/API/Request/JLTimelineRequest.h: -------------------------------------------------------------------------------- 1 | // 2 | // JLTimelineRequest.h 3 | // CleanArchitecture 4 | // 5 | // Created by Joshua Liebowitz on 3/8/16. 6 | // Copyright © 2016 Joshua Liebowitz. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface JLTimelineRequest : NSObject 12 | 13 | NS_ASSUME_NONNULL_BEGIN 14 | 15 | @property (nonatomic, copy, readonly) NSString *userID; 16 | @property (nonatomic, copy, readonly) NSDate *startDate; 17 | @property (nonatomic, copy, readonly) NSDate *endDate; 18 | 19 | - (instancetype)initWithUserID:(NSString *)userID startDate:(NSDate *)startDate endDate:(NSDate *)endDate NS_DESIGNATED_INITIALIZER; 20 | - (instancetype)init NS_UNAVAILABLE; 21 | 22 | NS_ASSUME_NONNULL_END 23 | 24 | 25 | @end 26 | -------------------------------------------------------------------------------- /CleanArchitecture/API/Request/JLTimelineRequest.m: -------------------------------------------------------------------------------- 1 | // 2 | // JLTimelineRequest.m 3 | // CleanArchitecture 4 | // 5 | // Created by Joshua Liebowitz on 3/8/16. 6 | // Copyright © 2016 Joshua Liebowitz. All rights reserved. 7 | // 8 | 9 | #import "JLTimelineRequest.h" 10 | 11 | @implementation JLTimelineRequest 12 | 13 | - (instancetype)initWithUserID:(NSString *)userID startDate:(NSDate *)startDate endDate:(NSDate *)endDate 14 | { 15 | if (self = [super init]) { 16 | _userID = [userID copy]; 17 | _startDate = [startDate copy]; 18 | _endDate = [endDate copy]; 19 | } 20 | return self; 21 | } 22 | 23 | @end 24 | -------------------------------------------------------------------------------- /CleanArchitecture/AppDelegate.h: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.h 3 | // CleanArchitecture 4 | // 5 | // Created by Joshua Liebowitz on 3/8/16. 6 | // Copyright © 2016 Joshua Liebowitz. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface AppDelegate : UIResponder 12 | 13 | @property (strong, nonatomic) UIWindow *window; 14 | 15 | 16 | @end 17 | 18 | -------------------------------------------------------------------------------- /CleanArchitecture/AppDelegate.m: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.m 3 | // CleanArchitecture 4 | // 5 | // Created by Joshua Liebowitz on 3/8/16. 6 | // Copyright © 2016 Joshua Liebowitz. All rights reserved. 7 | // 8 | 9 | #import "AppDelegate.h" 10 | #import "JLAuthenticationInfo.h" 11 | #import "JLTimelineViewController.h" 12 | 13 | @interface AppDelegate () 14 | 15 | @end 16 | 17 | @implementation AppDelegate 18 | 19 | 20 | - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions 21 | { 22 | _window = [[UIWindow alloc] init]; 23 | JLAuthenticationInfo *authInfo = [[JLAuthenticationInfo alloc] init]; 24 | 25 | self.window.rootViewController = [[JLTimelineViewController alloc] initWithAuthenticationInfo:authInfo]; 26 | [self.window makeKeyAndVisible]; 27 | return YES; 28 | } 29 | @end 30 | -------------------------------------------------------------------------------- /CleanArchitecture/Assets.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" : "29x29", 11 | "scale" : "3x" 12 | }, 13 | { 14 | "idiom" : "iphone", 15 | "size" : "40x40", 16 | "scale" : "2x" 17 | }, 18 | { 19 | "idiom" : "iphone", 20 | "size" : "40x40", 21 | "scale" : "3x" 22 | }, 23 | { 24 | "idiom" : "iphone", 25 | "size" : "60x60", 26 | "scale" : "2x" 27 | }, 28 | { 29 | "idiom" : "iphone", 30 | "size" : "60x60", 31 | "scale" : "3x" 32 | } 33 | ], 34 | "info" : { 35 | "version" : 1, 36 | "author" : "xcode" 37 | } 38 | } -------------------------------------------------------------------------------- /CleanArchitecture/Base.lproj/LaunchScreen.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 | -------------------------------------------------------------------------------- /CleanArchitecture/Features/Timeline/JLTimelineViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // ViewController.h 3 | // CleanArchitecture 4 | // 5 | // Created by Joshua Liebowitz on 3/8/16. 6 | // Copyright © 2016 Joshua Liebowitz. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @class JLAuthenticationInfo; 12 | 13 | @interface JLTimelineViewController : UIViewController 14 | 15 | NS_ASSUME_NONNULL_BEGIN 16 | 17 | - (instancetype)initWithAuthenticationInfo:(JLAuthenticationInfo *)authenticationInfo; 18 | - (instancetype)init NS_UNAVAILABLE; 19 | - (instancetype)initWithNibName:(NSString * _Nullable)nibNameOrNil bundle:(NSBundle * _Nullable)nibBundleOrNil NS_UNAVAILABLE; 20 | - (instancetype)initWithCoder:(NSCoder *)aDecoder NS_UNAVAILABLE; 21 | 22 | NS_ASSUME_NONNULL_END 23 | 24 | 25 | @end 26 | 27 | -------------------------------------------------------------------------------- /CleanArchitecture/Features/Timeline/JLTimelineViewController.m: -------------------------------------------------------------------------------- 1 | // 2 | // ViewController.m 3 | // CleanArchitecture 4 | // 5 | // Created by Joshua Liebowitz on 3/8/16. 6 | // Copyright © 2016 Joshua Liebowitz. All rights reserved. 7 | // 8 | 9 | #import "JLAuthenticationInfo.h" 10 | #import "JLTimelineService.h" 11 | #import "JLTimelineViewController.h" 12 | 13 | @interface JLTimelineViewController () 14 | 15 | @property (nonatomic, readonly) JLTimelineService *timelineService; 16 | 17 | @end 18 | 19 | 20 | @implementation JLTimelineViewController 21 | 22 | - (instancetype)initWithAuthenticationInfo:(JLAuthenticationInfo *)authenticationInfo 23 | { 24 | if (self = [super initWithNibName:nil bundle:nil]) { 25 | _timelineService = [[JLTimelineService alloc] initWithAuthenticationInfo:authenticationInfo]; 26 | } 27 | return self; 28 | } 29 | 30 | - (void)viewDidLoad { 31 | [super viewDidLoad]; 32 | [self.timelineService fetchTimelineWithCompletion:^(NSArray * _Nonnull tweets, NSError * _Nullable error) { 33 | dispatch_async(dispatch_get_main_queue(), ^{ 34 | NSLog(@"fetched: %li tweet(s)", tweets.count); 35 | }); 36 | 37 | }]; 38 | } 39 | 40 | @end 41 | -------------------------------------------------------------------------------- /CleanArchitecture/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 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 | LaunchScreen 27 | UIRequiredDeviceCapabilities 28 | 29 | armv7 30 | 31 | UISupportedInterfaceOrientations 32 | 33 | UIInterfaceOrientationPortrait 34 | UIInterfaceOrientationLandscapeLeft 35 | UIInterfaceOrientationLandscapeRight 36 | 37 | 38 | 39 | -------------------------------------------------------------------------------- /CleanArchitecture/Service/Authentication/JLAuthenticationInfo.h: -------------------------------------------------------------------------------- 1 | // 2 | // JLAuthenticationInfo.h 3 | // CleanArchitecture 4 | // 5 | // Created by Joshua Liebowitz on 3/8/16. 6 | // Copyright © 2016 Joshua Liebowitz. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface JLAuthenticationInfo : NSObject 12 | NS_ASSUME_NONNULL_BEGIN 13 | 14 | @property (nonatomic, copy, readonly) NSString *userID; 15 | 16 | // maybe init with API key + secret, or the object might know how to get its own data from keychain 17 | // have properties that contain that data 18 | 19 | NS_ASSUME_NONNULL_END 20 | 21 | @end 22 | -------------------------------------------------------------------------------- /CleanArchitecture/Service/Authentication/JLAuthenticationInfo.m: -------------------------------------------------------------------------------- 1 | // 2 | // JLAuthenticationInfo.m 3 | // CleanArchitecture 4 | // 5 | // Created by Joshua Liebowitz on 3/8/16. 6 | // Copyright © 2016 Joshua Liebowitz. All rights reserved. 7 | // 8 | 9 | #import "JLAuthenticationInfo.h" 10 | 11 | @interface JLAuthenticationInfo () 12 | 13 | @end 14 | @implementation JLAuthenticationInfo 15 | 16 | - (id)copyWithZone:(NSZone *)zone 17 | { 18 | return self; 19 | } 20 | 21 | @end 22 | -------------------------------------------------------------------------------- /CleanArchitecture/Service/JLTimelineService.h: -------------------------------------------------------------------------------- 1 | // 2 | // JLTimelineService.h 3 | // CleanArchitecture 4 | // 5 | // Created by Joshua Liebowitz on 3/8/16. 6 | // Copyright © 2016 Joshua Liebowitz. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @class JLAuthenticationInfo; 12 | @class JLTweet; 13 | 14 | typedef void(^TimelineCompletion)(NSArray * __nonnull tweets, NSError * __nullable error); 15 | 16 | @interface JLTimelineService : NSObject 17 | 18 | NS_ASSUME_NONNULL_BEGIN 19 | 20 | - (instancetype)initWithAuthenticationInfo:(JLAuthenticationInfo *)authenticationInfo NS_DESIGNATED_INITIALIZER; 21 | - (instancetype)init NS_UNAVAILABLE; 22 | - (void)fetchTimelineWithCompletion:(TimelineCompletion)completion; 23 | 24 | NS_ASSUME_NONNULL_END 25 | 26 | @end 27 | -------------------------------------------------------------------------------- /CleanArchitecture/Service/JLTimelineService.m: -------------------------------------------------------------------------------- 1 | // 2 | // JLTimelineService.m 3 | // CleanArchitecture 4 | // 5 | // Created by Joshua Liebowitz on 3/8/16. 6 | // Copyright © 2016 Joshua Liebowitz. All rights reserved. 7 | // 8 | 9 | #import "JLAuthenticationInfo.h" 10 | #import "JLTimelineAPI.h" 11 | #import "JLTimelineRequest.h" 12 | #import "JLTimelineService.h" 13 | 14 | @interface JLTimelineService() 15 | 16 | @property (nonatomic, copy, readonly) JLAuthenticationInfo *authInfo; 17 | @property (nonatomic, readonly) JLTimelineAPI *timelineAPI; 18 | 19 | @end 20 | 21 | @implementation JLTimelineService 22 | 23 | - (instancetype)initWithAuthenticationInfo:(JLAuthenticationInfo *)authenticationInfo 24 | { 25 | if (self = [super init]) { 26 | _authInfo = [authenticationInfo copy]; 27 | _timelineAPI = [[JLTimelineAPI alloc] initWithAuthenticationInfo:authenticationInfo]; 28 | } 29 | return self; 30 | } 31 | 32 | - (void)fetchTimelineWithCompletion:(TimelineCompletion)completion 33 | { 34 | JLTimelineRequest *request = [[JLTimelineRequest alloc] initWithUserID:self.authInfo.userID startDate:[[[NSDate alloc] init] dateByAddingTimeInterval:-86400] endDate:[[NSDate alloc] init]]; 35 | [self.timelineAPI fetchTimelineWithRequest:request completion:^(NSArray * _Nullable tweets, NSError * _Nullable error) { 36 | if (completion) { 37 | if (!tweets) { 38 | tweets = [[NSArray alloc] init]; 39 | } 40 | completion(tweets, nil); 41 | } 42 | }]; 43 | } 44 | 45 | @end 46 | -------------------------------------------------------------------------------- /CleanArchitecture/Service/Model/JLTweet.h: -------------------------------------------------------------------------------- 1 | // 2 | // JLTweet.h 3 | // CleanArchitecture 4 | // 5 | // Created by Joshua Liebowitz on 3/8/16. 6 | // Copyright © 2016 Joshua Liebowitz. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface JLTweet : NSObject 12 | 13 | @end 14 | -------------------------------------------------------------------------------- /CleanArchitecture/Service/Model/JLTweet.m: -------------------------------------------------------------------------------- 1 | // 2 | // JLTweet.m 3 | // CleanArchitecture 4 | // 5 | // Created by Joshua Liebowitz on 3/8/16. 6 | // Copyright © 2016 Joshua Liebowitz. All rights reserved. 7 | // 8 | 9 | #import "JLTweet.h" 10 | 11 | @implementation JLTweet 12 | 13 | @end 14 | -------------------------------------------------------------------------------- /CleanArchitecture/main.m: -------------------------------------------------------------------------------- 1 | // 2 | // main.m 3 | // CleanArchitecture 4 | // 5 | // Created by Joshua Liebowitz on 3/8/16. 6 | // Copyright © 2016 Joshua Liebowitz. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "AppDelegate.h" 11 | 12 | int main(int argc, char * argv[]) { 13 | @autoreleasepool { 14 | return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class])); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /CleanArchitectureTests/CleanArchitectureTests.m: -------------------------------------------------------------------------------- 1 | // 2 | // CleanArchitectureTests.m 3 | // CleanArchitectureTests 4 | // 5 | // Created by Joshua Liebowitz on 3/8/16. 6 | // Copyright © 2016 Joshua Liebowitz. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface CleanArchitectureTests : XCTestCase 12 | 13 | @end 14 | 15 | @implementation CleanArchitectureTests 16 | 17 | - (void)setUp { 18 | [super setUp]; 19 | // Put setup code here. This method is called before the invocation of each test method in the class. 20 | } 21 | 22 | - (void)tearDown { 23 | // Put teardown code here. This method is called after the invocation of each test method in the class. 24 | [super tearDown]; 25 | } 26 | 27 | - (void)testExample { 28 | // This is an example of a functional test case. 29 | // Use XCTAssert and related functions to verify your tests produce the correct results. 30 | } 31 | 32 | - (void)testPerformanceExample { 33 | // This is an example of a performance test case. 34 | [self measureBlock:^{ 35 | // Put the code you want to measure the time of here. 36 | }]; 37 | } 38 | 39 | @end 40 | -------------------------------------------------------------------------------- /CleanArchitectureTests/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | BNDL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1 23 | 24 | 25 | -------------------------------------------------------------------------------- /CleanArchitectureUITests/CleanArchitectureUITests.m: -------------------------------------------------------------------------------- 1 | // 2 | // CleanArchitectureUITests.m 3 | // CleanArchitectureUITests 4 | // 5 | // Created by Joshua Liebowitz on 3/8/16. 6 | // Copyright © 2016 Joshua Liebowitz. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface CleanArchitectureUITests : XCTestCase 12 | 13 | @end 14 | 15 | @implementation CleanArchitectureUITests 16 | 17 | - (void)setUp { 18 | [super setUp]; 19 | 20 | // Put setup code here. This method is called before the invocation of each test method in the class. 21 | 22 | // In UI tests it is usually best to stop immediately when a failure occurs. 23 | self.continueAfterFailure = NO; 24 | // UI tests must launch the application that they test. Doing this in setup will make sure it happens for each test method. 25 | [[[XCUIApplication alloc] init] launch]; 26 | 27 | // In UI tests it’s important to set the initial state - such as interface orientation - required for your tests before they run. The setUp method is a good place to do this. 28 | } 29 | 30 | - (void)tearDown { 31 | // Put teardown code here. This method is called after the invocation of each test method in the class. 32 | [super tearDown]; 33 | } 34 | 35 | - (void)testExample { 36 | // Use recording to get started writing UI tests. 37 | // Use XCTAssert and related functions to verify your tests produce the correct results. 38 | } 39 | 40 | @end 41 | -------------------------------------------------------------------------------- /CleanArchitectureUITests/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | BNDL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1 23 | 24 | 25 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # CleanArchitectureiOS 2 | 3 | Boilerplate ObjC iOS App demonstrating multiple layers with a clean separation of concerns. 4 | --------------------------------------------------------------------------------