├── .gitignore ├── Podfile ├── Podfile.lock ├── README.md ├── TestNetworkLayer.xcodeproj ├── project.pbxproj └── project.xcworkspace │ └── contents.xcworkspacedata ├── TestNetworkLayer.xcworkspace └── contents.xcworkspacedata ├── TestNetworkLayer ├── AFNetworkingApiClient.swift ├── AlamofireApiClient.swift ├── AppDelegate.swift ├── Assets.xcassets │ └── AppIcon.appiconset │ │ └── Contents.json ├── Base.lproj │ ├── LaunchScreen.storyboard │ └── Main.storyboard ├── GitHubApiClient.swift ├── GitHubUserData.swift ├── Info.plist ├── NativeApiClient.swift └── ViewController.swift └── TestNetworkLayerTests ├── Fixtures └── GetUserSuccess.json ├── Info.plist ├── NativeApiClientSpec.swift └── XCTest+Extensions.swift /.gitignore: -------------------------------------------------------------------------------- 1 | # OS X 2 | .DS_Store 3 | 4 | # Xcode 5 | build/ 6 | *.pbxuser 7 | !default.pbxuser 8 | *.mode1v3 9 | !default.mode1v3 10 | *.mode2v3 11 | !default.mode2v3 12 | *.perspectivev3 13 | !default.perspectivev3 14 | xcuserdata/ 15 | *.xccheckout 16 | profile 17 | *.moved-aside 18 | DerivedData 19 | *.hmap 20 | *.ipa 21 | 22 | # Bundler 23 | .bundle 24 | 25 | Carthage 26 | Pods/ 27 | -------------------------------------------------------------------------------- /Podfile: -------------------------------------------------------------------------------- 1 | platform :ios, '9.0' 2 | 3 | target 'TestNetworkLayer' do 4 | use_frameworks! 5 | pod 'SwiftyJSON' 6 | pod 'AFNetworking' 7 | pod 'Alamofire' 8 | 9 | target 'TestNetworkLayerTests' do 10 | inherit! :search_paths 11 | pod 'Quick' 12 | pod 'Nimble' 13 | pod 'Mockingjay' 14 | end 15 | end 16 | -------------------------------------------------------------------------------- /Podfile.lock: -------------------------------------------------------------------------------- 1 | PODS: 2 | - AFNetworking (3.1.0): 3 | - AFNetworking/NSURLSession (= 3.1.0) 4 | - AFNetworking/Reachability (= 3.1.0) 5 | - AFNetworking/Security (= 3.1.0) 6 | - AFNetworking/Serialization (= 3.1.0) 7 | - AFNetworking/UIKit (= 3.1.0) 8 | - AFNetworking/NSURLSession (3.1.0): 9 | - AFNetworking/Reachability 10 | - AFNetworking/Security 11 | - AFNetworking/Serialization 12 | - AFNetworking/Reachability (3.1.0) 13 | - AFNetworking/Security (3.1.0) 14 | - AFNetworking/Serialization (3.1.0) 15 | - AFNetworking/UIKit (3.1.0): 16 | - AFNetworking/NSURLSession 17 | - Alamofire (3.4.1) 18 | - Mockingjay (1.2.2): 19 | - Mockingjay/Core (= 1.2.2) 20 | - Mockingjay/XCTest (= 1.2.2) 21 | - Mockingjay/Core (1.2.2): 22 | - URITemplate (~> 1.3) 23 | - Mockingjay/XCTest (1.2.2): 24 | - Mockingjay/Core 25 | - Nimble (4.1.0) 26 | - Quick (0.9.2) 27 | - SwiftyJSON (2.3.2) 28 | - URITemplate (1.3.1) 29 | 30 | DEPENDENCIES: 31 | - AFNetworking 32 | - Alamofire 33 | - Mockingjay 34 | - Nimble 35 | - Quick 36 | - SwiftyJSON 37 | 38 | SPEC CHECKSUMS: 39 | AFNetworking: 5e0e199f73d8626b11e79750991f5d173d1f8b67 40 | Alamofire: 01a82e2f6c0f860ade35534c8dd88be61bdef40c 41 | Mockingjay: 5de65f14108473afc6727e05df9a3ecc9cef8562 42 | Nimble: 97a0a4cae5124c117115634b2d055d8c97d0af19 43 | Quick: 18d057bc66451eedd5d1c8dc99ba2a5db6e60226 44 | SwiftyJSON: 04ccea08915aa0109039157c7974cf0298da292a 45 | URITemplate: 398a48d6559e23b77bd41176b675b017ad1fc959 46 | 47 | PODFILE CHECKSUM: dbddf8a169983f9ec87f6f29cb7c67145348019b 48 | 49 | COCOAPODS: 1.0.1 50 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # This is a demo project for this blog post: 2 | http://hoangtran.me/ios/testing/2016/09/12/unit-test-network-layer-in-ios/ 3 | -------------------------------------------------------------------------------- /TestNetworkLayer.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 46; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 39E6E0E31D82A4490051DD4E /* GitHubUserData.swift in Sources */ = {isa = PBXBuildFile; fileRef = 39E6E0E21D82A4490051DD4E /* GitHubUserData.swift */; }; 11 | 4AF30917F544A203B0B1A7FC /* Pods_TestNetworkLayer.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 63E539D6E742766CD8A40F7E /* Pods_TestNetworkLayer.framework */; }; 12 | 536FCCF61DAD8CC89C409AE1 /* Pods_TestNetworkLayerTests.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 1624BDE3EE53CE5BAD4EBC65 /* Pods_TestNetworkLayerTests.framework */; }; 13 | F317D3251D81A4C200B871E3 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = F317D3241D81A4C200B871E3 /* AppDelegate.swift */; }; 14 | F317D3271D81A4C200B871E3 /* ViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = F317D3261D81A4C200B871E3 /* ViewController.swift */; }; 15 | F317D32A1D81A4C200B871E3 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = F317D3281D81A4C200B871E3 /* Main.storyboard */; }; 16 | F317D32C1D81A4C200B871E3 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = F317D32B1D81A4C200B871E3 /* Assets.xcassets */; }; 17 | F317D32F1D81A4C200B871E3 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = F317D32D1D81A4C200B871E3 /* LaunchScreen.storyboard */; }; 18 | F317D3451D81A78E00B871E3 /* NativeApiClientSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = F317D3441D81A78E00B871E3 /* NativeApiClientSpec.swift */; }; 19 | F3516FCA1D81AE4500C6D7E8 /* NativeApiClient.swift in Sources */ = {isa = PBXBuildFile; fileRef = F3516FC91D81AE4500C6D7E8 /* NativeApiClient.swift */; }; 20 | F3516FCC1D81AF7300C6D7E8 /* GitHubApiClient.swift in Sources */ = {isa = PBXBuildFile; fileRef = F3516FCB1D81AF7300C6D7E8 /* GitHubApiClient.swift */; }; 21 | F3516FCE1D81BB3300C6D7E8 /* AFNetworkingApiClient.swift in Sources */ = {isa = PBXBuildFile; fileRef = F3516FCD1D81BB3300C6D7E8 /* AFNetworkingApiClient.swift */; }; 22 | F3516FD01D81C00300C6D7E8 /* AlamofireApiClient.swift in Sources */ = {isa = PBXBuildFile; fileRef = F3516FCF1D81C00300C6D7E8 /* AlamofireApiClient.swift */; }; 23 | F38AE80F1D83AAA3008D2DAC /* XCTest+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = F38AE80E1D83AAA3008D2DAC /* XCTest+Extensions.swift */; }; 24 | F38AE8181D83BB5F008D2DAC /* GetUserSuccess.json in Resources */ = {isa = PBXBuildFile; fileRef = F38AE8171D83BB5F008D2DAC /* GetUserSuccess.json */; }; 25 | /* End PBXBuildFile section */ 26 | 27 | /* Begin PBXContainerItemProxy section */ 28 | F317D3361D81A4C200B871E3 /* PBXContainerItemProxy */ = { 29 | isa = PBXContainerItemProxy; 30 | containerPortal = F317D3191D81A4C200B871E3 /* Project object */; 31 | proxyType = 1; 32 | remoteGlobalIDString = F317D3201D81A4C200B871E3; 33 | remoteInfo = TestNetworkLayer; 34 | }; 35 | /* End PBXContainerItemProxy section */ 36 | 37 | /* Begin PBXFileReference section */ 38 | 0C0B8106BA1F308843201A06 /* Pods-TestNetworkLayerTests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-TestNetworkLayerTests.debug.xcconfig"; path = "Pods/Target Support Files/Pods-TestNetworkLayerTests/Pods-TestNetworkLayerTests.debug.xcconfig"; sourceTree = ""; }; 39 | 0F6242A829B40ADC3B08DDB5 /* Pods-TestNetworkLayer.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-TestNetworkLayer.debug.xcconfig"; path = "Pods/Target Support Files/Pods-TestNetworkLayer/Pods-TestNetworkLayer.debug.xcconfig"; sourceTree = ""; }; 40 | 1624BDE3EE53CE5BAD4EBC65 /* Pods_TestNetworkLayerTests.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_TestNetworkLayerTests.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 41 | 39E6E0E21D82A4490051DD4E /* GitHubUserData.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GitHubUserData.swift; sourceTree = ""; }; 42 | 3D9B85F54C9DFDF5E313208B /* Pods-TestNetworkLayerTests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-TestNetworkLayerTests.release.xcconfig"; path = "Pods/Target Support Files/Pods-TestNetworkLayerTests/Pods-TestNetworkLayerTests.release.xcconfig"; sourceTree = ""; }; 43 | 63E539D6E742766CD8A40F7E /* Pods_TestNetworkLayer.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_TestNetworkLayer.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 44 | 8FD14A8F8D7D9674E0510BCD /* Pods-TestNetworkLayer.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-TestNetworkLayer.release.xcconfig"; path = "Pods/Target Support Files/Pods-TestNetworkLayer/Pods-TestNetworkLayer.release.xcconfig"; sourceTree = ""; }; 45 | F317D3211D81A4C200B871E3 /* TestNetworkLayer.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = TestNetworkLayer.app; sourceTree = BUILT_PRODUCTS_DIR; }; 46 | F317D3241D81A4C200B871E3 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; 47 | F317D3261D81A4C200B871E3 /* ViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewController.swift; sourceTree = ""; }; 48 | F317D3291D81A4C200B871E3 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; 49 | F317D32B1D81A4C200B871E3 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 50 | F317D32E1D81A4C200B871E3 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; 51 | F317D3301D81A4C200B871E3 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 52 | F317D3351D81A4C200B871E3 /* TestNetworkLayerTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = TestNetworkLayerTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 53 | F317D33B1D81A4C200B871E3 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 54 | F317D3441D81A78E00B871E3 /* NativeApiClientSpec.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NativeApiClientSpec.swift; sourceTree = ""; }; 55 | F3516FC91D81AE4500C6D7E8 /* NativeApiClient.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NativeApiClient.swift; sourceTree = ""; }; 56 | F3516FCB1D81AF7300C6D7E8 /* GitHubApiClient.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GitHubApiClient.swift; sourceTree = ""; }; 57 | F3516FCD1D81BB3300C6D7E8 /* AFNetworkingApiClient.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AFNetworkingApiClient.swift; sourceTree = ""; }; 58 | F3516FCF1D81C00300C6D7E8 /* AlamofireApiClient.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AlamofireApiClient.swift; sourceTree = ""; }; 59 | F38AE80E1D83AAA3008D2DAC /* XCTest+Extensions.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "XCTest+Extensions.swift"; sourceTree = ""; }; 60 | F38AE8171D83BB5F008D2DAC /* GetUserSuccess.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = GetUserSuccess.json; sourceTree = ""; }; 61 | /* End PBXFileReference section */ 62 | 63 | /* Begin PBXFrameworksBuildPhase section */ 64 | F317D31E1D81A4C200B871E3 /* Frameworks */ = { 65 | isa = PBXFrameworksBuildPhase; 66 | buildActionMask = 2147483647; 67 | files = ( 68 | 4AF30917F544A203B0B1A7FC /* Pods_TestNetworkLayer.framework in Frameworks */, 69 | ); 70 | runOnlyForDeploymentPostprocessing = 0; 71 | }; 72 | F317D3321D81A4C200B871E3 /* Frameworks */ = { 73 | isa = PBXFrameworksBuildPhase; 74 | buildActionMask = 2147483647; 75 | files = ( 76 | 536FCCF61DAD8CC89C409AE1 /* Pods_TestNetworkLayerTests.framework in Frameworks */, 77 | ); 78 | runOnlyForDeploymentPostprocessing = 0; 79 | }; 80 | /* End PBXFrameworksBuildPhase section */ 81 | 82 | /* Begin PBXGroup section */ 83 | 2EA00A06B45B7471CCF8DB5F /* Frameworks */ = { 84 | isa = PBXGroup; 85 | children = ( 86 | 63E539D6E742766CD8A40F7E /* Pods_TestNetworkLayer.framework */, 87 | 1624BDE3EE53CE5BAD4EBC65 /* Pods_TestNetworkLayerTests.framework */, 88 | ); 89 | name = Frameworks; 90 | sourceTree = ""; 91 | }; 92 | 5B0BEFC72E40FB1963A6DEB5 /* Pods */ = { 93 | isa = PBXGroup; 94 | children = ( 95 | 0F6242A829B40ADC3B08DDB5 /* Pods-TestNetworkLayer.debug.xcconfig */, 96 | 8FD14A8F8D7D9674E0510BCD /* Pods-TestNetworkLayer.release.xcconfig */, 97 | 0C0B8106BA1F308843201A06 /* Pods-TestNetworkLayerTests.debug.xcconfig */, 98 | 3D9B85F54C9DFDF5E313208B /* Pods-TestNetworkLayerTests.release.xcconfig */, 99 | ); 100 | name = Pods; 101 | sourceTree = ""; 102 | }; 103 | F317D3181D81A4C200B871E3 = { 104 | isa = PBXGroup; 105 | children = ( 106 | F317D3231D81A4C200B871E3 /* TestNetworkLayer */, 107 | F317D3381D81A4C200B871E3 /* TestNetworkLayerTests */, 108 | F317D3221D81A4C200B871E3 /* Products */, 109 | 5B0BEFC72E40FB1963A6DEB5 /* Pods */, 110 | 2EA00A06B45B7471CCF8DB5F /* Frameworks */, 111 | ); 112 | sourceTree = ""; 113 | }; 114 | F317D3221D81A4C200B871E3 /* Products */ = { 115 | isa = PBXGroup; 116 | children = ( 117 | F317D3211D81A4C200B871E3 /* TestNetworkLayer.app */, 118 | F317D3351D81A4C200B871E3 /* TestNetworkLayerTests.xctest */, 119 | ); 120 | name = Products; 121 | sourceTree = ""; 122 | }; 123 | F317D3231D81A4C200B871E3 /* TestNetworkLayer */ = { 124 | isa = PBXGroup; 125 | children = ( 126 | F317D3241D81A4C200B871E3 /* AppDelegate.swift */, 127 | F317D3261D81A4C200B871E3 /* ViewController.swift */, 128 | F317D3281D81A4C200B871E3 /* Main.storyboard */, 129 | F317D32B1D81A4C200B871E3 /* Assets.xcassets */, 130 | F317D32D1D81A4C200B871E3 /* LaunchScreen.storyboard */, 131 | F317D3301D81A4C200B871E3 /* Info.plist */, 132 | F3516FC91D81AE4500C6D7E8 /* NativeApiClient.swift */, 133 | F3516FCD1D81BB3300C6D7E8 /* AFNetworkingApiClient.swift */, 134 | F3516FCF1D81C00300C6D7E8 /* AlamofireApiClient.swift */, 135 | F3516FCB1D81AF7300C6D7E8 /* GitHubApiClient.swift */, 136 | 39E6E0E21D82A4490051DD4E /* GitHubUserData.swift */, 137 | ); 138 | path = TestNetworkLayer; 139 | sourceTree = ""; 140 | }; 141 | F317D3381D81A4C200B871E3 /* TestNetworkLayerTests */ = { 142 | isa = PBXGroup; 143 | children = ( 144 | F38AE8161D83BB5F008D2DAC /* Fixtures */, 145 | F317D33B1D81A4C200B871E3 /* Info.plist */, 146 | F317D3441D81A78E00B871E3 /* NativeApiClientSpec.swift */, 147 | F38AE80E1D83AAA3008D2DAC /* XCTest+Extensions.swift */, 148 | ); 149 | path = TestNetworkLayerTests; 150 | sourceTree = ""; 151 | }; 152 | F38AE8161D83BB5F008D2DAC /* Fixtures */ = { 153 | isa = PBXGroup; 154 | children = ( 155 | F38AE8171D83BB5F008D2DAC /* GetUserSuccess.json */, 156 | ); 157 | path = Fixtures; 158 | sourceTree = ""; 159 | }; 160 | /* End PBXGroup section */ 161 | 162 | /* Begin PBXNativeTarget section */ 163 | F317D3201D81A4C200B871E3 /* TestNetworkLayer */ = { 164 | isa = PBXNativeTarget; 165 | buildConfigurationList = F317D33E1D81A4C200B871E3 /* Build configuration list for PBXNativeTarget "TestNetworkLayer" */; 166 | buildPhases = ( 167 | 20F2EF46C218B923640ACEBB /* [CP] Check Pods Manifest.lock */, 168 | F317D31D1D81A4C200B871E3 /* Sources */, 169 | F317D31E1D81A4C200B871E3 /* Frameworks */, 170 | F317D31F1D81A4C200B871E3 /* Resources */, 171 | 46426AFF3E5C7A4AC1F4ED50 /* [CP] Embed Pods Frameworks */, 172 | FD7AE8B7B0EB0856500D6D98 /* [CP] Copy Pods Resources */, 173 | ); 174 | buildRules = ( 175 | ); 176 | dependencies = ( 177 | ); 178 | name = TestNetworkLayer; 179 | productName = TestNetworkLayer; 180 | productReference = F317D3211D81A4C200B871E3 /* TestNetworkLayer.app */; 181 | productType = "com.apple.product-type.application"; 182 | }; 183 | F317D3341D81A4C200B871E3 /* TestNetworkLayerTests */ = { 184 | isa = PBXNativeTarget; 185 | buildConfigurationList = F317D3411D81A4C200B871E3 /* Build configuration list for PBXNativeTarget "TestNetworkLayerTests" */; 186 | buildPhases = ( 187 | 5A15A90B05D324812AC9A3AD /* [CP] Check Pods Manifest.lock */, 188 | F317D3311D81A4C200B871E3 /* Sources */, 189 | F317D3321D81A4C200B871E3 /* Frameworks */, 190 | F317D3331D81A4C200B871E3 /* Resources */, 191 | 55C6313578090E9FF5B57333 /* [CP] Embed Pods Frameworks */, 192 | 97A424BC6FADCE083A9064AC /* [CP] Copy Pods Resources */, 193 | ); 194 | buildRules = ( 195 | ); 196 | dependencies = ( 197 | F317D3371D81A4C200B871E3 /* PBXTargetDependency */, 198 | ); 199 | name = TestNetworkLayerTests; 200 | productName = TestNetworkLayerTests; 201 | productReference = F317D3351D81A4C200B871E3 /* TestNetworkLayerTests.xctest */; 202 | productType = "com.apple.product-type.bundle.unit-test"; 203 | }; 204 | /* End PBXNativeTarget section */ 205 | 206 | /* Begin PBXProject section */ 207 | F317D3191D81A4C200B871E3 /* Project object */ = { 208 | isa = PBXProject; 209 | attributes = { 210 | LastSwiftUpdateCheck = 0730; 211 | LastUpgradeCheck = 0730; 212 | ORGANIZATIONNAME = "Hoang Tran"; 213 | TargetAttributes = { 214 | F317D3201D81A4C200B871E3 = { 215 | CreatedOnToolsVersion = 7.3; 216 | }; 217 | F317D3341D81A4C200B871E3 = { 218 | CreatedOnToolsVersion = 7.3; 219 | TestTargetID = F317D3201D81A4C200B871E3; 220 | }; 221 | }; 222 | }; 223 | buildConfigurationList = F317D31C1D81A4C200B871E3 /* Build configuration list for PBXProject "TestNetworkLayer" */; 224 | compatibilityVersion = "Xcode 3.2"; 225 | developmentRegion = English; 226 | hasScannedForEncodings = 0; 227 | knownRegions = ( 228 | en, 229 | Base, 230 | ); 231 | mainGroup = F317D3181D81A4C200B871E3; 232 | productRefGroup = F317D3221D81A4C200B871E3 /* Products */; 233 | projectDirPath = ""; 234 | projectRoot = ""; 235 | targets = ( 236 | F317D3201D81A4C200B871E3 /* TestNetworkLayer */, 237 | F317D3341D81A4C200B871E3 /* TestNetworkLayerTests */, 238 | ); 239 | }; 240 | /* End PBXProject section */ 241 | 242 | /* Begin PBXResourcesBuildPhase section */ 243 | F317D31F1D81A4C200B871E3 /* Resources */ = { 244 | isa = PBXResourcesBuildPhase; 245 | buildActionMask = 2147483647; 246 | files = ( 247 | F317D32F1D81A4C200B871E3 /* LaunchScreen.storyboard in Resources */, 248 | F317D32C1D81A4C200B871E3 /* Assets.xcassets in Resources */, 249 | F317D32A1D81A4C200B871E3 /* Main.storyboard in Resources */, 250 | ); 251 | runOnlyForDeploymentPostprocessing = 0; 252 | }; 253 | F317D3331D81A4C200B871E3 /* Resources */ = { 254 | isa = PBXResourcesBuildPhase; 255 | buildActionMask = 2147483647; 256 | files = ( 257 | F38AE8181D83BB5F008D2DAC /* GetUserSuccess.json in Resources */, 258 | ); 259 | runOnlyForDeploymentPostprocessing = 0; 260 | }; 261 | /* End PBXResourcesBuildPhase section */ 262 | 263 | /* Begin PBXShellScriptBuildPhase section */ 264 | 20F2EF46C218B923640ACEBB /* [CP] Check Pods Manifest.lock */ = { 265 | isa = PBXShellScriptBuildPhase; 266 | buildActionMask = 2147483647; 267 | files = ( 268 | ); 269 | inputPaths = ( 270 | ); 271 | name = "[CP] Check Pods Manifest.lock"; 272 | outputPaths = ( 273 | ); 274 | runOnlyForDeploymentPostprocessing = 0; 275 | shellPath = /bin/sh; 276 | shellScript = "diff \"${PODS_ROOT}/../Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [[ $? != 0 ]] ; then\n cat << EOM\nerror: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\nEOM\n exit 1\nfi\n"; 277 | showEnvVarsInLog = 0; 278 | }; 279 | 46426AFF3E5C7A4AC1F4ED50 /* [CP] Embed Pods Frameworks */ = { 280 | isa = PBXShellScriptBuildPhase; 281 | buildActionMask = 2147483647; 282 | files = ( 283 | ); 284 | inputPaths = ( 285 | ); 286 | name = "[CP] Embed Pods Frameworks"; 287 | outputPaths = ( 288 | ); 289 | runOnlyForDeploymentPostprocessing = 0; 290 | shellPath = /bin/sh; 291 | shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods-TestNetworkLayer/Pods-TestNetworkLayer-frameworks.sh\"\n"; 292 | showEnvVarsInLog = 0; 293 | }; 294 | 55C6313578090E9FF5B57333 /* [CP] Embed Pods Frameworks */ = { 295 | isa = PBXShellScriptBuildPhase; 296 | buildActionMask = 2147483647; 297 | files = ( 298 | ); 299 | inputPaths = ( 300 | ); 301 | name = "[CP] Embed Pods Frameworks"; 302 | outputPaths = ( 303 | ); 304 | runOnlyForDeploymentPostprocessing = 0; 305 | shellPath = /bin/sh; 306 | shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods-TestNetworkLayerTests/Pods-TestNetworkLayerTests-frameworks.sh\"\n"; 307 | showEnvVarsInLog = 0; 308 | }; 309 | 5A15A90B05D324812AC9A3AD /* [CP] Check Pods Manifest.lock */ = { 310 | isa = PBXShellScriptBuildPhase; 311 | buildActionMask = 2147483647; 312 | files = ( 313 | ); 314 | inputPaths = ( 315 | ); 316 | name = "[CP] Check Pods Manifest.lock"; 317 | outputPaths = ( 318 | ); 319 | runOnlyForDeploymentPostprocessing = 0; 320 | shellPath = /bin/sh; 321 | shellScript = "diff \"${PODS_ROOT}/../Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [[ $? != 0 ]] ; then\n cat << EOM\nerror: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\nEOM\n exit 1\nfi\n"; 322 | showEnvVarsInLog = 0; 323 | }; 324 | 97A424BC6FADCE083A9064AC /* [CP] Copy Pods Resources */ = { 325 | isa = PBXShellScriptBuildPhase; 326 | buildActionMask = 2147483647; 327 | files = ( 328 | ); 329 | inputPaths = ( 330 | ); 331 | name = "[CP] Copy Pods Resources"; 332 | outputPaths = ( 333 | ); 334 | runOnlyForDeploymentPostprocessing = 0; 335 | shellPath = /bin/sh; 336 | shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods-TestNetworkLayerTests/Pods-TestNetworkLayerTests-resources.sh\"\n"; 337 | showEnvVarsInLog = 0; 338 | }; 339 | FD7AE8B7B0EB0856500D6D98 /* [CP] Copy Pods Resources */ = { 340 | isa = PBXShellScriptBuildPhase; 341 | buildActionMask = 2147483647; 342 | files = ( 343 | ); 344 | inputPaths = ( 345 | ); 346 | name = "[CP] Copy Pods Resources"; 347 | outputPaths = ( 348 | ); 349 | runOnlyForDeploymentPostprocessing = 0; 350 | shellPath = /bin/sh; 351 | shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods-TestNetworkLayer/Pods-TestNetworkLayer-resources.sh\"\n"; 352 | showEnvVarsInLog = 0; 353 | }; 354 | /* End PBXShellScriptBuildPhase section */ 355 | 356 | /* Begin PBXSourcesBuildPhase section */ 357 | F317D31D1D81A4C200B871E3 /* Sources */ = { 358 | isa = PBXSourcesBuildPhase; 359 | buildActionMask = 2147483647; 360 | files = ( 361 | F3516FCC1D81AF7300C6D7E8 /* GitHubApiClient.swift in Sources */, 362 | F3516FCA1D81AE4500C6D7E8 /* NativeApiClient.swift in Sources */, 363 | F3516FD01D81C00300C6D7E8 /* AlamofireApiClient.swift in Sources */, 364 | F3516FCE1D81BB3300C6D7E8 /* AFNetworkingApiClient.swift in Sources */, 365 | 39E6E0E31D82A4490051DD4E /* GitHubUserData.swift in Sources */, 366 | F317D3271D81A4C200B871E3 /* ViewController.swift in Sources */, 367 | F317D3251D81A4C200B871E3 /* AppDelegate.swift in Sources */, 368 | ); 369 | runOnlyForDeploymentPostprocessing = 0; 370 | }; 371 | F317D3311D81A4C200B871E3 /* Sources */ = { 372 | isa = PBXSourcesBuildPhase; 373 | buildActionMask = 2147483647; 374 | files = ( 375 | F38AE80F1D83AAA3008D2DAC /* XCTest+Extensions.swift in Sources */, 376 | F317D3451D81A78E00B871E3 /* NativeApiClientSpec.swift in Sources */, 377 | ); 378 | runOnlyForDeploymentPostprocessing = 0; 379 | }; 380 | /* End PBXSourcesBuildPhase section */ 381 | 382 | /* Begin PBXTargetDependency section */ 383 | F317D3371D81A4C200B871E3 /* PBXTargetDependency */ = { 384 | isa = PBXTargetDependency; 385 | target = F317D3201D81A4C200B871E3 /* TestNetworkLayer */; 386 | targetProxy = F317D3361D81A4C200B871E3 /* PBXContainerItemProxy */; 387 | }; 388 | /* End PBXTargetDependency section */ 389 | 390 | /* Begin PBXVariantGroup section */ 391 | F317D3281D81A4C200B871E3 /* Main.storyboard */ = { 392 | isa = PBXVariantGroup; 393 | children = ( 394 | F317D3291D81A4C200B871E3 /* Base */, 395 | ); 396 | name = Main.storyboard; 397 | sourceTree = ""; 398 | }; 399 | F317D32D1D81A4C200B871E3 /* LaunchScreen.storyboard */ = { 400 | isa = PBXVariantGroup; 401 | children = ( 402 | F317D32E1D81A4C200B871E3 /* Base */, 403 | ); 404 | name = LaunchScreen.storyboard; 405 | sourceTree = ""; 406 | }; 407 | /* End PBXVariantGroup section */ 408 | 409 | /* Begin XCBuildConfiguration section */ 410 | F317D33C1D81A4C200B871E3 /* Debug */ = { 411 | isa = XCBuildConfiguration; 412 | buildSettings = { 413 | ALWAYS_SEARCH_USER_PATHS = NO; 414 | CLANG_ANALYZER_NONNULL = YES; 415 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 416 | CLANG_CXX_LIBRARY = "libc++"; 417 | CLANG_ENABLE_MODULES = YES; 418 | CLANG_ENABLE_OBJC_ARC = YES; 419 | CLANG_WARN_BOOL_CONVERSION = YES; 420 | CLANG_WARN_CONSTANT_CONVERSION = YES; 421 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 422 | CLANG_WARN_EMPTY_BODY = YES; 423 | CLANG_WARN_ENUM_CONVERSION = YES; 424 | CLANG_WARN_INT_CONVERSION = YES; 425 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 426 | CLANG_WARN_UNREACHABLE_CODE = YES; 427 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 428 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 429 | COPY_PHASE_STRIP = NO; 430 | DEBUG_INFORMATION_FORMAT = dwarf; 431 | ENABLE_STRICT_OBJC_MSGSEND = YES; 432 | ENABLE_TESTABILITY = YES; 433 | GCC_C_LANGUAGE_STANDARD = gnu99; 434 | GCC_DYNAMIC_NO_PIC = NO; 435 | GCC_NO_COMMON_BLOCKS = YES; 436 | GCC_OPTIMIZATION_LEVEL = 0; 437 | GCC_PREPROCESSOR_DEFINITIONS = ( 438 | "DEBUG=1", 439 | "$(inherited)", 440 | ); 441 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 442 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 443 | GCC_WARN_UNDECLARED_SELECTOR = YES; 444 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 445 | GCC_WARN_UNUSED_FUNCTION = YES; 446 | GCC_WARN_UNUSED_VARIABLE = YES; 447 | IPHONEOS_DEPLOYMENT_TARGET = 9.3; 448 | MTL_ENABLE_DEBUG_INFO = YES; 449 | ONLY_ACTIVE_ARCH = YES; 450 | SDKROOT = iphoneos; 451 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 452 | }; 453 | name = Debug; 454 | }; 455 | F317D33D1D81A4C200B871E3 /* Release */ = { 456 | isa = XCBuildConfiguration; 457 | buildSettings = { 458 | ALWAYS_SEARCH_USER_PATHS = NO; 459 | CLANG_ANALYZER_NONNULL = YES; 460 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 461 | CLANG_CXX_LIBRARY = "libc++"; 462 | CLANG_ENABLE_MODULES = YES; 463 | CLANG_ENABLE_OBJC_ARC = YES; 464 | CLANG_WARN_BOOL_CONVERSION = YES; 465 | CLANG_WARN_CONSTANT_CONVERSION = YES; 466 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 467 | CLANG_WARN_EMPTY_BODY = YES; 468 | CLANG_WARN_ENUM_CONVERSION = YES; 469 | CLANG_WARN_INT_CONVERSION = YES; 470 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 471 | CLANG_WARN_UNREACHABLE_CODE = YES; 472 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 473 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 474 | COPY_PHASE_STRIP = NO; 475 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 476 | ENABLE_NS_ASSERTIONS = NO; 477 | ENABLE_STRICT_OBJC_MSGSEND = YES; 478 | GCC_C_LANGUAGE_STANDARD = gnu99; 479 | GCC_NO_COMMON_BLOCKS = YES; 480 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 481 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 482 | GCC_WARN_UNDECLARED_SELECTOR = YES; 483 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 484 | GCC_WARN_UNUSED_FUNCTION = YES; 485 | GCC_WARN_UNUSED_VARIABLE = YES; 486 | IPHONEOS_DEPLOYMENT_TARGET = 9.3; 487 | MTL_ENABLE_DEBUG_INFO = NO; 488 | SDKROOT = iphoneos; 489 | VALIDATE_PRODUCT = YES; 490 | }; 491 | name = Release; 492 | }; 493 | F317D33F1D81A4C200B871E3 /* Debug */ = { 494 | isa = XCBuildConfiguration; 495 | baseConfigurationReference = 0F6242A829B40ADC3B08DDB5 /* Pods-TestNetworkLayer.debug.xcconfig */; 496 | buildSettings = { 497 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 498 | INFOPLIST_FILE = TestNetworkLayer/Info.plist; 499 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 500 | PRODUCT_BUNDLE_IDENTIFIER = hoang.tran.TestNetworkLayer; 501 | PRODUCT_NAME = "$(TARGET_NAME)"; 502 | }; 503 | name = Debug; 504 | }; 505 | F317D3401D81A4C200B871E3 /* Release */ = { 506 | isa = XCBuildConfiguration; 507 | baseConfigurationReference = 8FD14A8F8D7D9674E0510BCD /* Pods-TestNetworkLayer.release.xcconfig */; 508 | buildSettings = { 509 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 510 | INFOPLIST_FILE = TestNetworkLayer/Info.plist; 511 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 512 | PRODUCT_BUNDLE_IDENTIFIER = hoang.tran.TestNetworkLayer; 513 | PRODUCT_NAME = "$(TARGET_NAME)"; 514 | }; 515 | name = Release; 516 | }; 517 | F317D3421D81A4C200B871E3 /* Debug */ = { 518 | isa = XCBuildConfiguration; 519 | baseConfigurationReference = 0C0B8106BA1F308843201A06 /* Pods-TestNetworkLayerTests.debug.xcconfig */; 520 | buildSettings = { 521 | BUNDLE_LOADER = "$(TEST_HOST)"; 522 | CLANG_ENABLE_MODULES = YES; 523 | INFOPLIST_FILE = TestNetworkLayerTests/Info.plist; 524 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 525 | PRODUCT_BUNDLE_IDENTIFIER = hoang.tran.TestNetworkLayerTests; 526 | PRODUCT_NAME = "$(TARGET_NAME)"; 527 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 528 | TEST_HOST = "$(BUILT_PRODUCTS_DIR)/TestNetworkLayer.app/TestNetworkLayer"; 529 | }; 530 | name = Debug; 531 | }; 532 | F317D3431D81A4C200B871E3 /* Release */ = { 533 | isa = XCBuildConfiguration; 534 | baseConfigurationReference = 3D9B85F54C9DFDF5E313208B /* Pods-TestNetworkLayerTests.release.xcconfig */; 535 | buildSettings = { 536 | BUNDLE_LOADER = "$(TEST_HOST)"; 537 | CLANG_ENABLE_MODULES = YES; 538 | INFOPLIST_FILE = TestNetworkLayerTests/Info.plist; 539 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 540 | PRODUCT_BUNDLE_IDENTIFIER = hoang.tran.TestNetworkLayerTests; 541 | PRODUCT_NAME = "$(TARGET_NAME)"; 542 | TEST_HOST = "$(BUILT_PRODUCTS_DIR)/TestNetworkLayer.app/TestNetworkLayer"; 543 | }; 544 | name = Release; 545 | }; 546 | /* End XCBuildConfiguration section */ 547 | 548 | /* Begin XCConfigurationList section */ 549 | F317D31C1D81A4C200B871E3 /* Build configuration list for PBXProject "TestNetworkLayer" */ = { 550 | isa = XCConfigurationList; 551 | buildConfigurations = ( 552 | F317D33C1D81A4C200B871E3 /* Debug */, 553 | F317D33D1D81A4C200B871E3 /* Release */, 554 | ); 555 | defaultConfigurationIsVisible = 0; 556 | defaultConfigurationName = Release; 557 | }; 558 | F317D33E1D81A4C200B871E3 /* Build configuration list for PBXNativeTarget "TestNetworkLayer" */ = { 559 | isa = XCConfigurationList; 560 | buildConfigurations = ( 561 | F317D33F1D81A4C200B871E3 /* Debug */, 562 | F317D3401D81A4C200B871E3 /* Release */, 563 | ); 564 | defaultConfigurationIsVisible = 0; 565 | defaultConfigurationName = Release; 566 | }; 567 | F317D3411D81A4C200B871E3 /* Build configuration list for PBXNativeTarget "TestNetworkLayerTests" */ = { 568 | isa = XCConfigurationList; 569 | buildConfigurations = ( 570 | F317D3421D81A4C200B871E3 /* Debug */, 571 | F317D3431D81A4C200B871E3 /* Release */, 572 | ); 573 | defaultConfigurationIsVisible = 0; 574 | defaultConfigurationName = Release; 575 | }; 576 | /* End XCConfigurationList section */ 577 | }; 578 | rootObject = F317D3191D81A4C200B871E3 /* Project object */; 579 | } 580 | -------------------------------------------------------------------------------- /TestNetworkLayer.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /TestNetworkLayer.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /TestNetworkLayer/AFNetworkingApiClient.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AFNetworkingApiClient.swift 3 | // TestNetworkLayer 4 | // 5 | // Created by Hoang Tran on 9/8/16. 6 | // Copyright © 2016 Hoang Tran. All rights reserved. 7 | // 8 | 9 | import AFNetworking 10 | import SwiftyJSON 11 | 12 | class AFNetworkingApiClient: GitHubApiClient { 13 | 14 | static func requestUserWithUsername(username: String, onSuccess: GitHubGetUserCallback? = nil, onError: ErrorCallback? = nil) { 15 | let urlString = "\(kGitHubApiBaseUrl)users/\(username)" 16 | let url = NSURL(string: urlString)! 17 | 18 | let manager = AFURLSessionManager(sessionConfiguration: NSURLSessionConfiguration.defaultSessionConfiguration()) 19 | let request = NSURLRequest(URL: url) 20 | let dataTask = manager.dataTaskWithRequest(request) { response, data, error in 21 | if let error = error { 22 | onError?(error) 23 | } else if let data = data { 24 | let json = JSON(data) 25 | onSuccess?(GitHubUserData(json: json)) 26 | } 27 | } 28 | dataTask.resume() 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /TestNetworkLayer/AlamofireApiClient.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AlamofireApiClient.swift 3 | // TestNetworkLayer 4 | // 5 | // Created by Hoang Tran on 9/8/16. 6 | // Copyright © 2016 Hoang Tran. All rights reserved. 7 | // 8 | 9 | import Alamofire 10 | import SwiftyJSON 11 | 12 | class AlamofireApiClient: GitHubApiClient { 13 | 14 | static func requestUserWithUsername(username: String, onSuccess: GitHubGetUserCallback? = nil, onError: ErrorCallback? = nil) { 15 | let urlString = "\(kGitHubApiBaseUrl)users/\(username)" 16 | 17 | Alamofire.request(.GET, urlString) 18 | .validate() 19 | .responseJSON { response in 20 | switch response.result { 21 | case .Success: 22 | if let data = response.result.value { 23 | let json = JSON(data) 24 | onSuccess?(GitHubUserData(json: json)) 25 | } 26 | case .Failure(let error): 27 | onError?(error) 28 | } 29 | } 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /TestNetworkLayer/AppDelegate.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.swift 3 | // TestNetworkLayer 4 | // 5 | // Created by Hoang Tran on 9/8/16. 6 | // Copyright © 2016 Hoang Tran. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | @UIApplicationMain 12 | class AppDelegate: UIResponder, UIApplicationDelegate { 13 | 14 | var window: UIWindow? 15 | 16 | func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool { 17 | return true 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /TestNetworkLayer/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 | } -------------------------------------------------------------------------------- /TestNetworkLayer/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 | -------------------------------------------------------------------------------- /TestNetworkLayer/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 | -------------------------------------------------------------------------------- /TestNetworkLayer/GitHubApiClient.swift: -------------------------------------------------------------------------------- 1 | // 2 | // GitHubApiClient.swift 3 | // TestNetworkLayer 4 | // 5 | // Created by Hoang Tran on 9/8/16. 6 | // Copyright © 2016 Hoang Tran. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | let kGitHubApiBaseUrl = "https://api.github.com/" 12 | 13 | typealias GitHubGetUserCallback = (GitHubUserData) -> Void 14 | typealias ErrorCallback = (NSError) -> Void 15 | 16 | protocol GitHubApiClient { 17 | static func requestUserWithUsername(username: String, 18 | onSuccess: GitHubGetUserCallback?, 19 | onError: ErrorCallback?) 20 | } -------------------------------------------------------------------------------- /TestNetworkLayer/GitHubUserData.swift: -------------------------------------------------------------------------------- 1 | // 2 | // GitHubUserData.swift 3 | // TestNetworkLayer 4 | // 5 | // Created by eastagile-tc on 9/9/16. 6 | // Copyright © 2016 Hoang Tran. All rights reserved. 7 | // 8 | 9 | import SwiftyJSON 10 | 11 | struct GitHubUserData { 12 | var name: String 13 | var bio: String 14 | var email: String 15 | var numberOfFollowers: Int 16 | var numberOfFollowing: Int 17 | 18 | init(json: JSON) { 19 | name = json["name"].stringValue 20 | bio = json["bio"].stringValue 21 | email = json["email"].stringValue 22 | numberOfFollowers = json["followers"].intValue 23 | numberOfFollowing = json["following"].intValue 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /TestNetworkLayer/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 | UIMainStoryboardFile 28 | Main 29 | UIRequiredDeviceCapabilities 30 | 31 | armv7 32 | 33 | UISupportedInterfaceOrientations 34 | 35 | UIInterfaceOrientationPortrait 36 | UIInterfaceOrientationLandscapeLeft 37 | UIInterfaceOrientationLandscapeRight 38 | 39 | 40 | 41 | -------------------------------------------------------------------------------- /TestNetworkLayer/NativeApiClient.swift: -------------------------------------------------------------------------------- 1 | // 2 | // MyApiClient.swift 3 | // TestNetworkLayer 4 | // 5 | // Created by Hoang Tran on 9/8/16. 6 | // Copyright © 2016 Hoang Tran. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import SwiftyJSON 11 | 12 | class NativeApiClient: GitHubApiClient { 13 | 14 | static func requestUserWithUsername(username: String, onSuccess: GitHubGetUserCallback? = nil, onError: ErrorCallback? = nil) { 15 | let urlString = "https://api.github.com/users/\(username)" 16 | let url = NSURL(string: urlString)! 17 | 18 | let defaultSession = NSURLSession(configuration: NSURLSessionConfiguration.defaultSessionConfiguration()) 19 | let dataTask = defaultSession.dataTaskWithURL(url) { data, response, error in 20 | if let error = error { 21 | onError?(error) 22 | } else if let data = data { 23 | let json = JSON(data: data) 24 | onSuccess?(GitHubUserData(json: json)) 25 | } 26 | } 27 | dataTask.resume() 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /TestNetworkLayer/ViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ViewController.swift 3 | // TestNetworkLayer 4 | // 5 | // Created by Hoang Tran on 9/8/16. 6 | // Copyright © 2016 Hoang Tran. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | class ViewController: UIViewController { 12 | override func viewDidLoad() { 13 | super.viewDidLoad() 14 | let onSuccess: (GitHubUserData) -> Void = { userData in 15 | print(userData) 16 | } 17 | NativeApiClient.requestUserWithUsername("hoang-tran", onSuccess: onSuccess) 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /TestNetworkLayerTests/Fixtures/GetUserSuccess.json: -------------------------------------------------------------------------------- 1 | { 2 | "login": "hoang-tran", 3 | "id": 6714157, 4 | "avatar_url": "https://avatars.githubusercontent.com/u/6714157?v=3", 5 | "gravatar_id": "", 6 | "url": "https://api.github.com/users/hoang-tran", 7 | "html_url": "https://github.com/hoang-tran", 8 | "followers_url": "https://api.github.com/users/hoang-tran/followers", 9 | "following_url": "https://api.github.com/users/hoang-tran/following{/other_user}", 10 | "gists_url": "https://api.github.com/users/hoang-tran/gists{/gist_id}", 11 | "starred_url": "https://api.github.com/users/hoang-tran/starred{/owner}{/repo}", 12 | "subscriptions_url": "https://api.github.com/users/hoang-tran/subscriptions", 13 | "organizations_url": "https://api.github.com/users/hoang-tran/orgs", 14 | "repos_url": "https://api.github.com/users/hoang-tran/repos", 15 | "events_url": "https://api.github.com/users/hoang-tran/events{/privacy}", 16 | "received_events_url": "https://api.github.com/users/hoang-tran/received_events", 17 | "type": "User", 18 | "site_admin": false, 19 | "name": "Hoang Tran", 20 | "company": "East Agile Vietnam", 21 | "blog": "http://hoangtran.me/", 22 | "location": "Ho Chi Minh city, VietNam", 23 | "email": "hoangtx.master@gmail.com", 24 | "hireable": null, 25 | "bio": "Hi, I'm Hoang. I'm a Swift lover. I blog weekly about iOS development/testing/deployment at http://hoangtran.me/\r\nAnd I happen to love GitHub so much 😜 ", 26 | "public_repos": 15, 27 | "public_gists": 23, 28 | "followers": 16, 29 | "following": 93, 30 | "created_at": "2014-02-18T09:42:07Z", 31 | "updated_at": "2016-08-30T03:04:04Z" 32 | } 33 | -------------------------------------------------------------------------------- /TestNetworkLayerTests/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 | -------------------------------------------------------------------------------- /TestNetworkLayerTests/NativeApiClientSpec.swift: -------------------------------------------------------------------------------- 1 | // 2 | // MyApiClientSpec.swift 3 | // TestNetworkLayer 4 | // 5 | // Created by Hoang Tran on 9/8/16. 6 | // Copyright © 2016 Hoang Tran. All rights reserved. 7 | // 8 | 9 | import Quick 10 | import Nimble 11 | import Mockingjay 12 | @testable import TestNetworkLayer 13 | 14 | class NativeApiClientSpec : QuickSpec { 15 | override func spec() { 16 | super.spec() 17 | 18 | describe("requestUserWithName") { 19 | var returnedUserData: GitHubUserData? 20 | var returnedError: NSError? 21 | let urlString = "https://api.github.com/users/hoang-tran" 22 | 23 | beforeEach { 24 | returnedUserData = nil 25 | returnedError = nil 26 | } 27 | 28 | context("success") { 29 | beforeEach { 30 | self.stub(urlString, jsonFileName: "GetUserSuccess") 31 | NativeApiClient.requestUserWithUsername("hoang-tran", onSuccess: { userData in 32 | returnedUserData = userData 33 | }) 34 | } 35 | 36 | it("returns GitHubUserData") { 37 | expect(returnedUserData).toEventuallyNot(beNil(), timeout: 20) 38 | expect(returnedUserData?.name) == "Hoang Tran" 39 | expect(returnedUserData?.bio) == "Hi, I'm Hoang. I'm a Swift lover. I blog weekly about iOS development/testing/deployment at http://hoangtran.me/\r\nAnd I happen to love GitHub so much 😜 " 40 | expect(returnedUserData?.email) == "hoangtx.master@gmail.com" 41 | expect(returnedUserData?.numberOfFollowers) == 16 42 | expect(returnedUserData?.numberOfFollowing) == 93 43 | } 44 | } 45 | 46 | context("error") { 47 | let error = NSError(domain: "error", code: 404, userInfo: nil) 48 | 49 | beforeEach { 50 | self.stub(urlString, error: error) 51 | NativeApiClient.requestUserWithUsername("hoang-tran", onError: { error in 52 | returnedError = error 53 | }) 54 | } 55 | 56 | it("returns error") { 57 | expect(returnedError).toEventuallyNot(beNil()) 58 | expect(returnedError?.domain) == error.domain 59 | expect(returnedError?.code) == error.code 60 | } 61 | } 62 | } 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /TestNetworkLayerTests/XCTest+Extensions.swift: -------------------------------------------------------------------------------- 1 | // 2 | // XCTest+Extensions.swift 3 | // TestNetworkLayer 4 | // 5 | // Created by Hoang Tran on 9/10/16. 6 | // Copyright © 2016 Hoang Tran. All rights reserved. 7 | // 8 | 9 | import XCTest 10 | import Mockingjay 11 | 12 | extension XCTest { 13 | public func stub(urlString: String, jsonFileName: String) -> Mockingjay.Stub { 14 | let path = NSBundle(forClass: self.dynamicType).pathForResource(jsonFileName, ofType: "json")! 15 | let data = NSData(contentsOfFile: path)! 16 | return stub(uri(urlString), builder: jsonData(data)) 17 | } 18 | 19 | public func stub(urlString: String, error: NSError) -> Mockingjay.Stub { 20 | return stub(uri(urlString), builder: failure(error)) 21 | } 22 | } 23 | --------------------------------------------------------------------------------