├── README.md ├── UnitTest-MVVM-C-Example.xcodeproj ├── project.pbxproj ├── project.xcworkspace │ ├── contents.xcworkspacedata │ └── xcshareddata │ │ └── IDEWorkspaceChecks.plist └── xcuserdata │ └── skara.xcuserdatad │ ├── xcdebugger │ └── Breakpoints_v2.xcbkptlist │ └── xcschemes │ └── xcschememanagement.plist ├── UnitTest-MVVM-C-Example ├── Application │ └── AppDelegate.swift ├── Assets.xcassets │ ├── AccentColor.colorset │ │ └── Contents.json │ ├── AppIcon.appiconset │ │ └── Contents.json │ └── Contents.json ├── Base.lproj │ └── LaunchScreen.storyboard ├── Common │ └── Test │ │ ├── MockNewsListService.swift │ │ └── ResourceLoader.swift ├── Coordinator │ └── AppCoordinator.swift ├── Info.plist ├── Manager │ └── NetworkLayer.swift ├── Model │ └── News.swift ├── Scenes │ ├── NewsListVC.swift │ ├── NewsListVC.xib │ └── NewsListVM.swift └── Utilities │ ├── APISettings │ └── APISettings.swift │ └── Base │ └── BaseViewController.swift ├── UnitTest-MVVM-C-ExampleTests └── UnitTest_MVVM_C_ExampleTests.swift └── UnitTest-MVVM-C-ExampleUITests ├── UnitTest_MVVM_C_ExampleUITests.swift └── UnitTest_MVVM_C_ExampleUITestsLaunchTests.swift /README.md: -------------------------------------------------------------------------------- 1 | # MVVM-C Unit Test Example 2 | 3 | This repository contains an iOS application developed using the MVVM-C (Model-View-ViewModel-Coordinator) architecture. Additionally, it includes unit tests for the application. 4 | 5 | ## Project Structure 6 | 7 | The project is organized as follows: 8 | 9 | - **UnitTest-MVVM-C-Example**: Main project folder 10 | - **UnitTest-MVVM-C-Example**: Main application source code 11 | - **UnitTest_MVVM_C_ExampleTests**: Folder containing unit tests 12 | - ... 13 | 14 | ## How to Run 15 | 16 | 1. Clone this repository to your local machine. 17 | 2. Open the `UnitTest-MVVM-C-Example.xcodeproj` file inside the main project folder using Xcode. 18 | 3. Build the project and run the application. 19 | 20 | ## Running Unit Tests 21 | 22 | 1. Open a terminal and navigate to the project folder. 23 | 2. Navigate to the `UnitTest_MVVM_C_ExampleTests` folder. 24 | 3. Run the command `xcodebuild test -scheme UnitTest_MVVM_C_ExampleTests -destination 'platform=iOS Simulator,name=iPhone 13'` to start the unit tests. 25 | 26 | # Update Versions 27 | 1. Xcode Version 14.3 28 | 2. iOS Version 15.0+ 29 | 3. Swift Version 4.0+ 30 | -------------------------------------------------------------------------------- /UnitTest-MVVM-C-Example.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 56; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | DB2A54F42B5501AF003B88D2 /* ResourceLoader.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB2A54F32B5501AF003B88D2 /* ResourceLoader.swift */; }; 11 | DB2A54F62B5501CC003B88D2 /* MockNewsListService.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB2A54F52B5501CC003B88D2 /* MockNewsListService.swift */; }; 12 | DBB6AF832B54357E0022F9D6 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = DBB6AF822B54357E0022F9D6 /* AppDelegate.swift */; }; 13 | DBB6AF8C2B54357F0022F9D6 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = DBB6AF8B2B54357F0022F9D6 /* Assets.xcassets */; }; 14 | DBB6AF8F2B54357F0022F9D6 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = DBB6AF8D2B54357F0022F9D6 /* LaunchScreen.storyboard */; }; 15 | DBB6AF9A2B54357F0022F9D6 /* UnitTest_MVVM_C_ExampleTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = DBB6AF992B54357F0022F9D6 /* UnitTest_MVVM_C_ExampleTests.swift */; }; 16 | DBB6AFA42B54357F0022F9D6 /* UnitTest_MVVM_C_ExampleUITests.swift in Sources */ = {isa = PBXBuildFile; fileRef = DBB6AFA32B54357F0022F9D6 /* UnitTest_MVVM_C_ExampleUITests.swift */; }; 17 | DBB6AFA62B54357F0022F9D6 /* UnitTest_MVVM_C_ExampleUITestsLaunchTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = DBB6AFA52B54357F0022F9D6 /* UnitTest_MVVM_C_ExampleUITestsLaunchTests.swift */; }; 18 | DBB6AFB52B5435F80022F9D6 /* AppCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = DBB6AFB42B5435F80022F9D6 /* AppCoordinator.swift */; }; 19 | DBB6AFB82B5436400022F9D6 /* NetworkLayer.swift in Sources */ = {isa = PBXBuildFile; fileRef = DBB6AFB72B5436400022F9D6 /* NetworkLayer.swift */; }; 20 | DBB6AFBB2B5436A50022F9D6 /* News.swift in Sources */ = {isa = PBXBuildFile; fileRef = DBB6AFBA2B5436A50022F9D6 /* News.swift */; }; 21 | DBB6AFC02B5436DD0022F9D6 /* APISettings.swift in Sources */ = {isa = PBXBuildFile; fileRef = DBB6AFBF2B5436DD0022F9D6 /* APISettings.swift */; }; 22 | DBB6AFC22B5437020022F9D6 /* BaseViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = DBB6AFC12B5437020022F9D6 /* BaseViewController.swift */; }; 23 | DBB6AFC52B5437650022F9D6 /* NewsListVM.swift in Sources */ = {isa = PBXBuildFile; fileRef = DBB6AFC42B5437650022F9D6 /* NewsListVM.swift */; }; 24 | DBB6AFC82B5437700022F9D6 /* NewsListVC.swift in Sources */ = {isa = PBXBuildFile; fileRef = DBB6AFC62B5437700022F9D6 /* NewsListVC.swift */; }; 25 | DBB6AFC92B5437700022F9D6 /* NewsListVC.xib in Resources */ = {isa = PBXBuildFile; fileRef = DBB6AFC72B5437700022F9D6 /* NewsListVC.xib */; }; 26 | /* End PBXBuildFile section */ 27 | 28 | /* Begin PBXContainerItemProxy section */ 29 | DBB6AF962B54357F0022F9D6 /* PBXContainerItemProxy */ = { 30 | isa = PBXContainerItemProxy; 31 | containerPortal = DBB6AF772B54357E0022F9D6 /* Project object */; 32 | proxyType = 1; 33 | remoteGlobalIDString = DBB6AF7E2B54357E0022F9D6; 34 | remoteInfo = "UnitTest-MVVM-C-Example"; 35 | }; 36 | DBB6AFA02B54357F0022F9D6 /* PBXContainerItemProxy */ = { 37 | isa = PBXContainerItemProxy; 38 | containerPortal = DBB6AF772B54357E0022F9D6 /* Project object */; 39 | proxyType = 1; 40 | remoteGlobalIDString = DBB6AF7E2B54357E0022F9D6; 41 | remoteInfo = "UnitTest-MVVM-C-Example"; 42 | }; 43 | /* End PBXContainerItemProxy section */ 44 | 45 | /* Begin PBXFileReference section */ 46 | DB2A54F32B5501AF003B88D2 /* ResourceLoader.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ResourceLoader.swift; sourceTree = ""; }; 47 | DB2A54F52B5501CC003B88D2 /* MockNewsListService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MockNewsListService.swift; sourceTree = ""; }; 48 | DBB6AF7F2B54357E0022F9D6 /* UnitTest-MVVM-C-Example.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "UnitTest-MVVM-C-Example.app"; sourceTree = BUILT_PRODUCTS_DIR; }; 49 | DBB6AF822B54357E0022F9D6 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; 50 | DBB6AF8B2B54357F0022F9D6 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 51 | DBB6AF8E2B54357F0022F9D6 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; 52 | DBB6AF902B54357F0022F9D6 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 53 | DBB6AF952B54357F0022F9D6 /* UnitTest-MVVM-C-ExampleTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = "UnitTest-MVVM-C-ExampleTests.xctest"; sourceTree = BUILT_PRODUCTS_DIR; }; 54 | DBB6AF992B54357F0022F9D6 /* UnitTest_MVVM_C_ExampleTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UnitTest_MVVM_C_ExampleTests.swift; sourceTree = ""; }; 55 | DBB6AF9F2B54357F0022F9D6 /* UnitTest-MVVM-C-ExampleUITests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = "UnitTest-MVVM-C-ExampleUITests.xctest"; sourceTree = BUILT_PRODUCTS_DIR; }; 56 | DBB6AFA32B54357F0022F9D6 /* UnitTest_MVVM_C_ExampleUITests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UnitTest_MVVM_C_ExampleUITests.swift; sourceTree = ""; }; 57 | DBB6AFA52B54357F0022F9D6 /* UnitTest_MVVM_C_ExampleUITestsLaunchTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UnitTest_MVVM_C_ExampleUITestsLaunchTests.swift; sourceTree = ""; }; 58 | DBB6AFB42B5435F80022F9D6 /* AppCoordinator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppCoordinator.swift; sourceTree = ""; }; 59 | DBB6AFB72B5436400022F9D6 /* NetworkLayer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NetworkLayer.swift; sourceTree = ""; }; 60 | DBB6AFBA2B5436A50022F9D6 /* News.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = News.swift; sourceTree = ""; }; 61 | DBB6AFBF2B5436DD0022F9D6 /* APISettings.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = APISettings.swift; sourceTree = ""; }; 62 | DBB6AFC12B5437020022F9D6 /* BaseViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BaseViewController.swift; sourceTree = ""; }; 63 | DBB6AFC42B5437650022F9D6 /* NewsListVM.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NewsListVM.swift; sourceTree = ""; }; 64 | DBB6AFC62B5437700022F9D6 /* NewsListVC.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NewsListVC.swift; sourceTree = ""; }; 65 | DBB6AFC72B5437700022F9D6 /* NewsListVC.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = NewsListVC.xib; sourceTree = ""; }; 66 | /* End PBXFileReference section */ 67 | 68 | /* Begin PBXFrameworksBuildPhase section */ 69 | DBB6AF7C2B54357E0022F9D6 /* Frameworks */ = { 70 | isa = PBXFrameworksBuildPhase; 71 | buildActionMask = 2147483647; 72 | files = ( 73 | ); 74 | runOnlyForDeploymentPostprocessing = 0; 75 | }; 76 | DBB6AF922B54357F0022F9D6 /* Frameworks */ = { 77 | isa = PBXFrameworksBuildPhase; 78 | buildActionMask = 2147483647; 79 | files = ( 80 | ); 81 | runOnlyForDeploymentPostprocessing = 0; 82 | }; 83 | DBB6AF9C2B54357F0022F9D6 /* Frameworks */ = { 84 | isa = PBXFrameworksBuildPhase; 85 | buildActionMask = 2147483647; 86 | files = ( 87 | ); 88 | runOnlyForDeploymentPostprocessing = 0; 89 | }; 90 | /* End PBXFrameworksBuildPhase section */ 91 | 92 | /* Begin PBXGroup section */ 93 | DB2A54F12B550196003B88D2 /* Common */ = { 94 | isa = PBXGroup; 95 | children = ( 96 | DB2A54F22B5501A2003B88D2 /* Test */, 97 | ); 98 | path = Common; 99 | sourceTree = ""; 100 | }; 101 | DB2A54F22B5501A2003B88D2 /* Test */ = { 102 | isa = PBXGroup; 103 | children = ( 104 | DB2A54F32B5501AF003B88D2 /* ResourceLoader.swift */, 105 | DB2A54F52B5501CC003B88D2 /* MockNewsListService.swift */, 106 | ); 107 | path = Test; 108 | sourceTree = ""; 109 | }; 110 | DBB6AF762B54357E0022F9D6 = { 111 | isa = PBXGroup; 112 | children = ( 113 | DBB6AF812B54357E0022F9D6 /* UnitTest-MVVM-C-Example */, 114 | DBB6AF982B54357F0022F9D6 /* UnitTest-MVVM-C-ExampleTests */, 115 | DBB6AFA22B54357F0022F9D6 /* UnitTest-MVVM-C-ExampleUITests */, 116 | DBB6AF802B54357E0022F9D6 /* Products */, 117 | ); 118 | sourceTree = ""; 119 | }; 120 | DBB6AF802B54357E0022F9D6 /* Products */ = { 121 | isa = PBXGroup; 122 | children = ( 123 | DBB6AF7F2B54357E0022F9D6 /* UnitTest-MVVM-C-Example.app */, 124 | DBB6AF952B54357F0022F9D6 /* UnitTest-MVVM-C-ExampleTests.xctest */, 125 | DBB6AF9F2B54357F0022F9D6 /* UnitTest-MVVM-C-ExampleUITests.xctest */, 126 | ); 127 | name = Products; 128 | sourceTree = ""; 129 | }; 130 | DBB6AF812B54357E0022F9D6 /* UnitTest-MVVM-C-Example */ = { 131 | isa = PBXGroup; 132 | children = ( 133 | DB2A54F12B550196003B88D2 /* Common */, 134 | DBB6AFBC2B5436B30022F9D6 /* Utilities */, 135 | DBB6AFB92B54365C0022F9D6 /* Model */, 136 | DBB6AFB62B5436310022F9D6 /* Manager */, 137 | DBB6AFC32B5437170022F9D6 /* Scenes */, 138 | DBB6AFB32B5435DD0022F9D6 /* Coordinator */, 139 | DBB6AFB22B5435C20022F9D6 /* Application */, 140 | DBB6AF8B2B54357F0022F9D6 /* Assets.xcassets */, 141 | DBB6AF8D2B54357F0022F9D6 /* LaunchScreen.storyboard */, 142 | DBB6AF902B54357F0022F9D6 /* Info.plist */, 143 | ); 144 | path = "UnitTest-MVVM-C-Example"; 145 | sourceTree = ""; 146 | }; 147 | DBB6AF982B54357F0022F9D6 /* UnitTest-MVVM-C-ExampleTests */ = { 148 | isa = PBXGroup; 149 | children = ( 150 | DBB6AF992B54357F0022F9D6 /* UnitTest_MVVM_C_ExampleTests.swift */, 151 | ); 152 | path = "UnitTest-MVVM-C-ExampleTests"; 153 | sourceTree = ""; 154 | }; 155 | DBB6AFA22B54357F0022F9D6 /* UnitTest-MVVM-C-ExampleUITests */ = { 156 | isa = PBXGroup; 157 | children = ( 158 | DBB6AFA32B54357F0022F9D6 /* UnitTest_MVVM_C_ExampleUITests.swift */, 159 | DBB6AFA52B54357F0022F9D6 /* UnitTest_MVVM_C_ExampleUITestsLaunchTests.swift */, 160 | ); 161 | path = "UnitTest-MVVM-C-ExampleUITests"; 162 | sourceTree = ""; 163 | }; 164 | DBB6AFB22B5435C20022F9D6 /* Application */ = { 165 | isa = PBXGroup; 166 | children = ( 167 | DBB6AF822B54357E0022F9D6 /* AppDelegate.swift */, 168 | ); 169 | path = Application; 170 | sourceTree = ""; 171 | }; 172 | DBB6AFB32B5435DD0022F9D6 /* Coordinator */ = { 173 | isa = PBXGroup; 174 | children = ( 175 | DBB6AFB42B5435F80022F9D6 /* AppCoordinator.swift */, 176 | ); 177 | path = Coordinator; 178 | sourceTree = ""; 179 | }; 180 | DBB6AFB62B5436310022F9D6 /* Manager */ = { 181 | isa = PBXGroup; 182 | children = ( 183 | DBB6AFB72B5436400022F9D6 /* NetworkLayer.swift */, 184 | ); 185 | path = Manager; 186 | sourceTree = ""; 187 | }; 188 | DBB6AFB92B54365C0022F9D6 /* Model */ = { 189 | isa = PBXGroup; 190 | children = ( 191 | DBB6AFBA2B5436A50022F9D6 /* News.swift */, 192 | ); 193 | path = Model; 194 | sourceTree = ""; 195 | }; 196 | DBB6AFBC2B5436B30022F9D6 /* Utilities */ = { 197 | isa = PBXGroup; 198 | children = ( 199 | DBB6AFBE2B5436CB0022F9D6 /* Base */, 200 | DBB6AFBD2B5436C00022F9D6 /* APISettings */, 201 | ); 202 | path = Utilities; 203 | sourceTree = ""; 204 | }; 205 | DBB6AFBD2B5436C00022F9D6 /* APISettings */ = { 206 | isa = PBXGroup; 207 | children = ( 208 | DBB6AFBF2B5436DD0022F9D6 /* APISettings.swift */, 209 | ); 210 | path = APISettings; 211 | sourceTree = ""; 212 | }; 213 | DBB6AFBE2B5436CB0022F9D6 /* Base */ = { 214 | isa = PBXGroup; 215 | children = ( 216 | DBB6AFC12B5437020022F9D6 /* BaseViewController.swift */, 217 | ); 218 | path = Base; 219 | sourceTree = ""; 220 | }; 221 | DBB6AFC32B5437170022F9D6 /* Scenes */ = { 222 | isa = PBXGroup; 223 | children = ( 224 | DBB6AFC72B5437700022F9D6 /* NewsListVC.xib */, 225 | DBB6AFC62B5437700022F9D6 /* NewsListVC.swift */, 226 | DBB6AFC42B5437650022F9D6 /* NewsListVM.swift */, 227 | ); 228 | path = Scenes; 229 | sourceTree = ""; 230 | }; 231 | /* End PBXGroup section */ 232 | 233 | /* Begin PBXNativeTarget section */ 234 | DBB6AF7E2B54357E0022F9D6 /* UnitTest-MVVM-C-Example */ = { 235 | isa = PBXNativeTarget; 236 | buildConfigurationList = DBB6AFA92B54357F0022F9D6 /* Build configuration list for PBXNativeTarget "UnitTest-MVVM-C-Example" */; 237 | buildPhases = ( 238 | DBB6AF7B2B54357E0022F9D6 /* Sources */, 239 | DBB6AF7C2B54357E0022F9D6 /* Frameworks */, 240 | DBB6AF7D2B54357E0022F9D6 /* Resources */, 241 | ); 242 | buildRules = ( 243 | ); 244 | dependencies = ( 245 | ); 246 | name = "UnitTest-MVVM-C-Example"; 247 | productName = "UnitTest-MVVM-C-Example"; 248 | productReference = DBB6AF7F2B54357E0022F9D6 /* UnitTest-MVVM-C-Example.app */; 249 | productType = "com.apple.product-type.application"; 250 | }; 251 | DBB6AF942B54357F0022F9D6 /* UnitTest-MVVM-C-ExampleTests */ = { 252 | isa = PBXNativeTarget; 253 | buildConfigurationList = DBB6AFAC2B54357F0022F9D6 /* Build configuration list for PBXNativeTarget "UnitTest-MVVM-C-ExampleTests" */; 254 | buildPhases = ( 255 | DBB6AF912B54357F0022F9D6 /* Sources */, 256 | DBB6AF922B54357F0022F9D6 /* Frameworks */, 257 | DBB6AF932B54357F0022F9D6 /* Resources */, 258 | ); 259 | buildRules = ( 260 | ); 261 | dependencies = ( 262 | DBB6AF972B54357F0022F9D6 /* PBXTargetDependency */, 263 | ); 264 | name = "UnitTest-MVVM-C-ExampleTests"; 265 | productName = "UnitTest-MVVM-C-ExampleTests"; 266 | productReference = DBB6AF952B54357F0022F9D6 /* UnitTest-MVVM-C-ExampleTests.xctest */; 267 | productType = "com.apple.product-type.bundle.unit-test"; 268 | }; 269 | DBB6AF9E2B54357F0022F9D6 /* UnitTest-MVVM-C-ExampleUITests */ = { 270 | isa = PBXNativeTarget; 271 | buildConfigurationList = DBB6AFAF2B54357F0022F9D6 /* Build configuration list for PBXNativeTarget "UnitTest-MVVM-C-ExampleUITests" */; 272 | buildPhases = ( 273 | DBB6AF9B2B54357F0022F9D6 /* Sources */, 274 | DBB6AF9C2B54357F0022F9D6 /* Frameworks */, 275 | DBB6AF9D2B54357F0022F9D6 /* Resources */, 276 | ); 277 | buildRules = ( 278 | ); 279 | dependencies = ( 280 | DBB6AFA12B54357F0022F9D6 /* PBXTargetDependency */, 281 | ); 282 | name = "UnitTest-MVVM-C-ExampleUITests"; 283 | productName = "UnitTest-MVVM-C-ExampleUITests"; 284 | productReference = DBB6AF9F2B54357F0022F9D6 /* UnitTest-MVVM-C-ExampleUITests.xctest */; 285 | productType = "com.apple.product-type.bundle.ui-testing"; 286 | }; 287 | /* End PBXNativeTarget section */ 288 | 289 | /* Begin PBXProject section */ 290 | DBB6AF772B54357E0022F9D6 /* Project object */ = { 291 | isa = PBXProject; 292 | attributes = { 293 | BuildIndependentTargetsInParallel = 1; 294 | LastSwiftUpdateCheck = 1430; 295 | LastUpgradeCheck = 1430; 296 | TargetAttributes = { 297 | DBB6AF7E2B54357E0022F9D6 = { 298 | CreatedOnToolsVersion = 14.3; 299 | }; 300 | DBB6AF942B54357F0022F9D6 = { 301 | CreatedOnToolsVersion = 14.3; 302 | TestTargetID = DBB6AF7E2B54357E0022F9D6; 303 | }; 304 | DBB6AF9E2B54357F0022F9D6 = { 305 | CreatedOnToolsVersion = 14.3; 306 | TestTargetID = DBB6AF7E2B54357E0022F9D6; 307 | }; 308 | }; 309 | }; 310 | buildConfigurationList = DBB6AF7A2B54357E0022F9D6 /* Build configuration list for PBXProject "UnitTest-MVVM-C-Example" */; 311 | compatibilityVersion = "Xcode 14.0"; 312 | developmentRegion = en; 313 | hasScannedForEncodings = 0; 314 | knownRegions = ( 315 | en, 316 | Base, 317 | ); 318 | mainGroup = DBB6AF762B54357E0022F9D6; 319 | productRefGroup = DBB6AF802B54357E0022F9D6 /* Products */; 320 | projectDirPath = ""; 321 | projectRoot = ""; 322 | targets = ( 323 | DBB6AF7E2B54357E0022F9D6 /* UnitTest-MVVM-C-Example */, 324 | DBB6AF942B54357F0022F9D6 /* UnitTest-MVVM-C-ExampleTests */, 325 | DBB6AF9E2B54357F0022F9D6 /* UnitTest-MVVM-C-ExampleUITests */, 326 | ); 327 | }; 328 | /* End PBXProject section */ 329 | 330 | /* Begin PBXResourcesBuildPhase section */ 331 | DBB6AF7D2B54357E0022F9D6 /* Resources */ = { 332 | isa = PBXResourcesBuildPhase; 333 | buildActionMask = 2147483647; 334 | files = ( 335 | DBB6AFC92B5437700022F9D6 /* NewsListVC.xib in Resources */, 336 | DBB6AF8F2B54357F0022F9D6 /* LaunchScreen.storyboard in Resources */, 337 | DBB6AF8C2B54357F0022F9D6 /* Assets.xcassets in Resources */, 338 | ); 339 | runOnlyForDeploymentPostprocessing = 0; 340 | }; 341 | DBB6AF932B54357F0022F9D6 /* Resources */ = { 342 | isa = PBXResourcesBuildPhase; 343 | buildActionMask = 2147483647; 344 | files = ( 345 | ); 346 | runOnlyForDeploymentPostprocessing = 0; 347 | }; 348 | DBB6AF9D2B54357F0022F9D6 /* Resources */ = { 349 | isa = PBXResourcesBuildPhase; 350 | buildActionMask = 2147483647; 351 | files = ( 352 | ); 353 | runOnlyForDeploymentPostprocessing = 0; 354 | }; 355 | /* End PBXResourcesBuildPhase section */ 356 | 357 | /* Begin PBXSourcesBuildPhase section */ 358 | DBB6AF7B2B54357E0022F9D6 /* Sources */ = { 359 | isa = PBXSourcesBuildPhase; 360 | buildActionMask = 2147483647; 361 | files = ( 362 | DB2A54F62B5501CC003B88D2 /* MockNewsListService.swift in Sources */, 363 | DBB6AFB82B5436400022F9D6 /* NetworkLayer.swift in Sources */, 364 | DBB6AFC52B5437650022F9D6 /* NewsListVM.swift in Sources */, 365 | DBB6AFC02B5436DD0022F9D6 /* APISettings.swift in Sources */, 366 | DBB6AFB52B5435F80022F9D6 /* AppCoordinator.swift in Sources */, 367 | DBB6AFBB2B5436A50022F9D6 /* News.swift in Sources */, 368 | DBB6AFC82B5437700022F9D6 /* NewsListVC.swift in Sources */, 369 | DBB6AF832B54357E0022F9D6 /* AppDelegate.swift in Sources */, 370 | DBB6AFC22B5437020022F9D6 /* BaseViewController.swift in Sources */, 371 | DB2A54F42B5501AF003B88D2 /* ResourceLoader.swift in Sources */, 372 | ); 373 | runOnlyForDeploymentPostprocessing = 0; 374 | }; 375 | DBB6AF912B54357F0022F9D6 /* Sources */ = { 376 | isa = PBXSourcesBuildPhase; 377 | buildActionMask = 2147483647; 378 | files = ( 379 | DBB6AF9A2B54357F0022F9D6 /* UnitTest_MVVM_C_ExampleTests.swift in Sources */, 380 | ); 381 | runOnlyForDeploymentPostprocessing = 0; 382 | }; 383 | DBB6AF9B2B54357F0022F9D6 /* Sources */ = { 384 | isa = PBXSourcesBuildPhase; 385 | buildActionMask = 2147483647; 386 | files = ( 387 | DBB6AFA42B54357F0022F9D6 /* UnitTest_MVVM_C_ExampleUITests.swift in Sources */, 388 | DBB6AFA62B54357F0022F9D6 /* UnitTest_MVVM_C_ExampleUITestsLaunchTests.swift in Sources */, 389 | ); 390 | runOnlyForDeploymentPostprocessing = 0; 391 | }; 392 | /* End PBXSourcesBuildPhase section */ 393 | 394 | /* Begin PBXTargetDependency section */ 395 | DBB6AF972B54357F0022F9D6 /* PBXTargetDependency */ = { 396 | isa = PBXTargetDependency; 397 | target = DBB6AF7E2B54357E0022F9D6 /* UnitTest-MVVM-C-Example */; 398 | targetProxy = DBB6AF962B54357F0022F9D6 /* PBXContainerItemProxy */; 399 | }; 400 | DBB6AFA12B54357F0022F9D6 /* PBXTargetDependency */ = { 401 | isa = PBXTargetDependency; 402 | target = DBB6AF7E2B54357E0022F9D6 /* UnitTest-MVVM-C-Example */; 403 | targetProxy = DBB6AFA02B54357F0022F9D6 /* PBXContainerItemProxy */; 404 | }; 405 | /* End PBXTargetDependency section */ 406 | 407 | /* Begin PBXVariantGroup section */ 408 | DBB6AF8D2B54357F0022F9D6 /* LaunchScreen.storyboard */ = { 409 | isa = PBXVariantGroup; 410 | children = ( 411 | DBB6AF8E2B54357F0022F9D6 /* Base */, 412 | ); 413 | name = LaunchScreen.storyboard; 414 | sourceTree = ""; 415 | }; 416 | /* End PBXVariantGroup section */ 417 | 418 | /* Begin XCBuildConfiguration section */ 419 | DBB6AFA72B54357F0022F9D6 /* Debug */ = { 420 | isa = XCBuildConfiguration; 421 | buildSettings = { 422 | ALWAYS_SEARCH_USER_PATHS = NO; 423 | CLANG_ANALYZER_NONNULL = YES; 424 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 425 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; 426 | CLANG_ENABLE_MODULES = YES; 427 | CLANG_ENABLE_OBJC_ARC = YES; 428 | CLANG_ENABLE_OBJC_WEAK = YES; 429 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 430 | CLANG_WARN_BOOL_CONVERSION = YES; 431 | CLANG_WARN_COMMA = YES; 432 | CLANG_WARN_CONSTANT_CONVERSION = YES; 433 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 434 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 435 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 436 | CLANG_WARN_EMPTY_BODY = YES; 437 | CLANG_WARN_ENUM_CONVERSION = YES; 438 | CLANG_WARN_INFINITE_RECURSION = YES; 439 | CLANG_WARN_INT_CONVERSION = YES; 440 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 441 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 442 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 443 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 444 | CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; 445 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 446 | CLANG_WARN_STRICT_PROTOTYPES = YES; 447 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 448 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; 449 | CLANG_WARN_UNREACHABLE_CODE = YES; 450 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 451 | COPY_PHASE_STRIP = NO; 452 | DEBUG_INFORMATION_FORMAT = dwarf; 453 | ENABLE_STRICT_OBJC_MSGSEND = YES; 454 | ENABLE_TESTABILITY = YES; 455 | GCC_C_LANGUAGE_STANDARD = gnu11; 456 | GCC_DYNAMIC_NO_PIC = NO; 457 | GCC_NO_COMMON_BLOCKS = YES; 458 | GCC_OPTIMIZATION_LEVEL = 0; 459 | GCC_PREPROCESSOR_DEFINITIONS = ( 460 | "DEBUG=1", 461 | "$(inherited)", 462 | ); 463 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 464 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 465 | GCC_WARN_UNDECLARED_SELECTOR = YES; 466 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 467 | GCC_WARN_UNUSED_FUNCTION = YES; 468 | GCC_WARN_UNUSED_VARIABLE = YES; 469 | IPHONEOS_DEPLOYMENT_TARGET = 15.0; 470 | MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; 471 | MTL_FAST_MATH = YES; 472 | ONLY_ACTIVE_ARCH = YES; 473 | SDKROOT = iphoneos; 474 | SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; 475 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 476 | }; 477 | name = Debug; 478 | }; 479 | DBB6AFA82B54357F0022F9D6 /* Release */ = { 480 | isa = XCBuildConfiguration; 481 | buildSettings = { 482 | ALWAYS_SEARCH_USER_PATHS = NO; 483 | CLANG_ANALYZER_NONNULL = YES; 484 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 485 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; 486 | CLANG_ENABLE_MODULES = YES; 487 | CLANG_ENABLE_OBJC_ARC = YES; 488 | CLANG_ENABLE_OBJC_WEAK = YES; 489 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 490 | CLANG_WARN_BOOL_CONVERSION = YES; 491 | CLANG_WARN_COMMA = YES; 492 | CLANG_WARN_CONSTANT_CONVERSION = YES; 493 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 494 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 495 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 496 | CLANG_WARN_EMPTY_BODY = YES; 497 | CLANG_WARN_ENUM_CONVERSION = YES; 498 | CLANG_WARN_INFINITE_RECURSION = YES; 499 | CLANG_WARN_INT_CONVERSION = YES; 500 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 501 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 502 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 503 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 504 | CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; 505 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 506 | CLANG_WARN_STRICT_PROTOTYPES = YES; 507 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 508 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; 509 | CLANG_WARN_UNREACHABLE_CODE = YES; 510 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 511 | COPY_PHASE_STRIP = NO; 512 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 513 | ENABLE_NS_ASSERTIONS = NO; 514 | ENABLE_STRICT_OBJC_MSGSEND = YES; 515 | GCC_C_LANGUAGE_STANDARD = gnu11; 516 | GCC_NO_COMMON_BLOCKS = YES; 517 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 518 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 519 | GCC_WARN_UNDECLARED_SELECTOR = YES; 520 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 521 | GCC_WARN_UNUSED_FUNCTION = YES; 522 | GCC_WARN_UNUSED_VARIABLE = YES; 523 | IPHONEOS_DEPLOYMENT_TARGET = 15.0; 524 | MTL_ENABLE_DEBUG_INFO = NO; 525 | MTL_FAST_MATH = YES; 526 | SDKROOT = iphoneos; 527 | SWIFT_COMPILATION_MODE = wholemodule; 528 | SWIFT_OPTIMIZATION_LEVEL = "-O"; 529 | VALIDATE_PRODUCT = YES; 530 | }; 531 | name = Release; 532 | }; 533 | DBB6AFAA2B54357F0022F9D6 /* Debug */ = { 534 | isa = XCBuildConfiguration; 535 | buildSettings = { 536 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 537 | ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; 538 | CODE_SIGN_STYLE = Automatic; 539 | CURRENT_PROJECT_VERSION = 1; 540 | DEVELOPMENT_TEAM = 9ZR69YFKY4; 541 | GENERATE_INFOPLIST_FILE = YES; 542 | INFOPLIST_FILE = "UnitTest-MVVM-C-Example/Info.plist"; 543 | INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES; 544 | INFOPLIST_KEY_UILaunchStoryboardName = LaunchScreen; 545 | INFOPLIST_KEY_UISupportedInterfaceOrientations = UIInterfaceOrientationPortrait; 546 | INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown"; 547 | IPHONEOS_DEPLOYMENT_TARGET = 15.0; 548 | LD_RUNPATH_SEARCH_PATHS = ( 549 | "$(inherited)", 550 | "@executable_path/Frameworks", 551 | ); 552 | MARKETING_VERSION = 1.0; 553 | PRODUCT_BUNDLE_IDENTIFIER = "com.sktech.UnitTest-MVVM-C-Example"; 554 | PRODUCT_NAME = "$(TARGET_NAME)"; 555 | SWIFT_EMIT_LOC_STRINGS = YES; 556 | SWIFT_VERSION = 5.0; 557 | TARGETED_DEVICE_FAMILY = "1,2"; 558 | }; 559 | name = Debug; 560 | }; 561 | DBB6AFAB2B54357F0022F9D6 /* Release */ = { 562 | isa = XCBuildConfiguration; 563 | buildSettings = { 564 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 565 | ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; 566 | CODE_SIGN_STYLE = Automatic; 567 | CURRENT_PROJECT_VERSION = 1; 568 | DEVELOPMENT_TEAM = 9ZR69YFKY4; 569 | GENERATE_INFOPLIST_FILE = YES; 570 | INFOPLIST_FILE = "UnitTest-MVVM-C-Example/Info.plist"; 571 | INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES; 572 | INFOPLIST_KEY_UILaunchStoryboardName = LaunchScreen; 573 | INFOPLIST_KEY_UISupportedInterfaceOrientations = UIInterfaceOrientationPortrait; 574 | INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown"; 575 | IPHONEOS_DEPLOYMENT_TARGET = 15.0; 576 | LD_RUNPATH_SEARCH_PATHS = ( 577 | "$(inherited)", 578 | "@executable_path/Frameworks", 579 | ); 580 | MARKETING_VERSION = 1.0; 581 | PRODUCT_BUNDLE_IDENTIFIER = "com.sktech.UnitTest-MVVM-C-Example"; 582 | PRODUCT_NAME = "$(TARGET_NAME)"; 583 | SWIFT_EMIT_LOC_STRINGS = YES; 584 | SWIFT_VERSION = 5.0; 585 | TARGETED_DEVICE_FAMILY = "1,2"; 586 | }; 587 | name = Release; 588 | }; 589 | DBB6AFAD2B54357F0022F9D6 /* Debug */ = { 590 | isa = XCBuildConfiguration; 591 | buildSettings = { 592 | ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; 593 | BUNDLE_LOADER = "$(TEST_HOST)"; 594 | CODE_SIGN_STYLE = Automatic; 595 | CURRENT_PROJECT_VERSION = 1; 596 | DEVELOPMENT_TEAM = 9ZR69YFKY4; 597 | GENERATE_INFOPLIST_FILE = YES; 598 | IPHONEOS_DEPLOYMENT_TARGET = 16.4; 599 | MARKETING_VERSION = 1.0; 600 | PRODUCT_BUNDLE_IDENTIFIER = "com.sktech.UnitTest-MVVM-C-ExampleTests"; 601 | PRODUCT_NAME = "$(TARGET_NAME)"; 602 | SWIFT_EMIT_LOC_STRINGS = NO; 603 | SWIFT_VERSION = 5.0; 604 | TARGETED_DEVICE_FAMILY = "1,2"; 605 | TEST_HOST = "$(BUILT_PRODUCTS_DIR)/UnitTest-MVVM-C-Example.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/UnitTest-MVVM-C-Example"; 606 | }; 607 | name = Debug; 608 | }; 609 | DBB6AFAE2B54357F0022F9D6 /* Release */ = { 610 | isa = XCBuildConfiguration; 611 | buildSettings = { 612 | ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; 613 | BUNDLE_LOADER = "$(TEST_HOST)"; 614 | CODE_SIGN_STYLE = Automatic; 615 | CURRENT_PROJECT_VERSION = 1; 616 | DEVELOPMENT_TEAM = 9ZR69YFKY4; 617 | GENERATE_INFOPLIST_FILE = YES; 618 | IPHONEOS_DEPLOYMENT_TARGET = 16.4; 619 | MARKETING_VERSION = 1.0; 620 | PRODUCT_BUNDLE_IDENTIFIER = "com.sktech.UnitTest-MVVM-C-ExampleTests"; 621 | PRODUCT_NAME = "$(TARGET_NAME)"; 622 | SWIFT_EMIT_LOC_STRINGS = NO; 623 | SWIFT_VERSION = 5.0; 624 | TARGETED_DEVICE_FAMILY = "1,2"; 625 | TEST_HOST = "$(BUILT_PRODUCTS_DIR)/UnitTest-MVVM-C-Example.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/UnitTest-MVVM-C-Example"; 626 | }; 627 | name = Release; 628 | }; 629 | DBB6AFB02B54357F0022F9D6 /* Debug */ = { 630 | isa = XCBuildConfiguration; 631 | buildSettings = { 632 | ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; 633 | CODE_SIGN_STYLE = Automatic; 634 | CURRENT_PROJECT_VERSION = 1; 635 | DEVELOPMENT_TEAM = 9ZR69YFKY4; 636 | GENERATE_INFOPLIST_FILE = YES; 637 | MARKETING_VERSION = 1.0; 638 | PRODUCT_BUNDLE_IDENTIFIER = "com.sktech.UnitTest-MVVM-C-ExampleUITests"; 639 | PRODUCT_NAME = "$(TARGET_NAME)"; 640 | SWIFT_EMIT_LOC_STRINGS = NO; 641 | SWIFT_VERSION = 5.0; 642 | TARGETED_DEVICE_FAMILY = "1,2"; 643 | TEST_TARGET_NAME = "UnitTest-MVVM-C-Example"; 644 | }; 645 | name = Debug; 646 | }; 647 | DBB6AFB12B54357F0022F9D6 /* Release */ = { 648 | isa = XCBuildConfiguration; 649 | buildSettings = { 650 | ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; 651 | CODE_SIGN_STYLE = Automatic; 652 | CURRENT_PROJECT_VERSION = 1; 653 | DEVELOPMENT_TEAM = 9ZR69YFKY4; 654 | GENERATE_INFOPLIST_FILE = YES; 655 | MARKETING_VERSION = 1.0; 656 | PRODUCT_BUNDLE_IDENTIFIER = "com.sktech.UnitTest-MVVM-C-ExampleUITests"; 657 | PRODUCT_NAME = "$(TARGET_NAME)"; 658 | SWIFT_EMIT_LOC_STRINGS = NO; 659 | SWIFT_VERSION = 5.0; 660 | TARGETED_DEVICE_FAMILY = "1,2"; 661 | TEST_TARGET_NAME = "UnitTest-MVVM-C-Example"; 662 | }; 663 | name = Release; 664 | }; 665 | /* End XCBuildConfiguration section */ 666 | 667 | /* Begin XCConfigurationList section */ 668 | DBB6AF7A2B54357E0022F9D6 /* Build configuration list for PBXProject "UnitTest-MVVM-C-Example" */ = { 669 | isa = XCConfigurationList; 670 | buildConfigurations = ( 671 | DBB6AFA72B54357F0022F9D6 /* Debug */, 672 | DBB6AFA82B54357F0022F9D6 /* Release */, 673 | ); 674 | defaultConfigurationIsVisible = 0; 675 | defaultConfigurationName = Release; 676 | }; 677 | DBB6AFA92B54357F0022F9D6 /* Build configuration list for PBXNativeTarget "UnitTest-MVVM-C-Example" */ = { 678 | isa = XCConfigurationList; 679 | buildConfigurations = ( 680 | DBB6AFAA2B54357F0022F9D6 /* Debug */, 681 | DBB6AFAB2B54357F0022F9D6 /* Release */, 682 | ); 683 | defaultConfigurationIsVisible = 0; 684 | defaultConfigurationName = Release; 685 | }; 686 | DBB6AFAC2B54357F0022F9D6 /* Build configuration list for PBXNativeTarget "UnitTest-MVVM-C-ExampleTests" */ = { 687 | isa = XCConfigurationList; 688 | buildConfigurations = ( 689 | DBB6AFAD2B54357F0022F9D6 /* Debug */, 690 | DBB6AFAE2B54357F0022F9D6 /* Release */, 691 | ); 692 | defaultConfigurationIsVisible = 0; 693 | defaultConfigurationName = Release; 694 | }; 695 | DBB6AFAF2B54357F0022F9D6 /* Build configuration list for PBXNativeTarget "UnitTest-MVVM-C-ExampleUITests" */ = { 696 | isa = XCConfigurationList; 697 | buildConfigurations = ( 698 | DBB6AFB02B54357F0022F9D6 /* Debug */, 699 | DBB6AFB12B54357F0022F9D6 /* Release */, 700 | ); 701 | defaultConfigurationIsVisible = 0; 702 | defaultConfigurationName = Release; 703 | }; 704 | /* End XCConfigurationList section */ 705 | }; 706 | rootObject = DBB6AF772B54357E0022F9D6 /* Project object */; 707 | } 708 | -------------------------------------------------------------------------------- /UnitTest-MVVM-C-Example.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /UnitTest-MVVM-C-Example.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /UnitTest-MVVM-C-Example.xcodeproj/xcuserdata/skara.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | -------------------------------------------------------------------------------- /UnitTest-MVVM-C-Example.xcodeproj/xcuserdata/skara.xcuserdatad/xcschemes/xcschememanagement.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | SchemeUserState 6 | 7 | UnitTest-MVVM-C-Example.xcscheme_^#shared#^_ 8 | 9 | orderHint 10 | 0 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /UnitTest-MVVM-C-Example/Application/AppDelegate.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.swift 3 | // UnitTest-MVVM-C-Example 4 | // 5 | // Created by Serkan Kara on 14.01.2024. 6 | // 7 | 8 | import UIKit 9 | 10 | @main 11 | class AppDelegate: UIResponder, UIApplicationDelegate { 12 | 13 | var window: UIWindow? 14 | var coordinator: AppCoordinatoring? 15 | 16 | func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { 17 | let nav = UINavigationController() 18 | self.window = setupWindow(nav) 19 | setupCoordinator(navController: nav) 20 | return true 21 | } 22 | 23 | } 24 | 25 | extension AppDelegate { 26 | private func setupWindow(_ nav: UINavigationController, makeKeyAndVisible: Bool = true) -> UIWindow { 27 | let window = UIWindow(frame: UIScreen.main.bounds) 28 | window.overrideUserInterfaceStyle = .light 29 | window.rootViewController = nav 30 | window.makeKeyAndVisible() 31 | return window 32 | } 33 | 34 | private func setupCoordinator(navController: UINavigationController) { 35 | coordinator = AppCoordinator(navigation: navController) 36 | coordinator?.start() 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /UnitTest-MVVM-C-Example/Assets.xcassets/AccentColor.colorset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "colors" : [ 3 | { 4 | "idiom" : "universal" 5 | } 6 | ], 7 | "info" : { 8 | "author" : "xcode", 9 | "version" : 1 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /UnitTest-MVVM-C-Example/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "platform" : "ios", 6 | "size" : "1024x1024" 7 | } 8 | ], 9 | "info" : { 10 | "author" : "xcode", 11 | "version" : 1 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /UnitTest-MVVM-C-Example/Assets.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "author" : "xcode", 4 | "version" : 1 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /UnitTest-MVVM-C-Example/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 | -------------------------------------------------------------------------------- /UnitTest-MVVM-C-Example/Common/Test/MockNewsListService.swift: -------------------------------------------------------------------------------- 1 | // 2 | // MockNewsListService.swift 3 | // UnitTest-MVVM-C-Example 4 | // 5 | // Created by Serkan Kara on 15.01.2024. 6 | // 7 | 8 | import Foundation 9 | 10 | final class MockNewsListService: NetworkService { 11 | var news = News(articles: [Articles(title: "NASA wants you to help track gamma ray bursts - The Washington Post", 12 | description: "Volunteers will help researchers classify gammy ray bursts’ pulses and shapes, contributing to future research on the huge explosions.")]) 13 | func fetchNews(completion: @escaping (Result) -> Void) { 14 | completion(.success(news)) 15 | } 16 | } 17 | 18 | 19 | -------------------------------------------------------------------------------- /UnitTest-MVVM-C-Example/Common/Test/ResourceLoader.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ResourceLoader.swift 3 | // UnitTest-MVVM-C-Example 4 | // 5 | // Created by Serkan Kara on 15.01.2024. 6 | // 7 | 8 | import Foundation 9 | 10 | class ResourceLoader { 11 | 12 | enum Resource: String { 13 | case news 14 | } 15 | 16 | static func loadNews(resource: Resource) throws -> News { 17 | let jsonString = """ 18 | { 19 | "articles": [ 20 | { 21 | "title": "NASA wants you to help track gamma ray bursts - The Washington Post", 22 | "description": "Volunteers will help researchers classify gammy ray bursts’ pulses and shapes, contributing to future research on the huge explosions." 23 | } 24 | ] 25 | } 26 | """ 27 | 28 | let jsonData = jsonString.data(using: .utf8) ?? Data() 29 | let decoder = JSONDecoder() 30 | let newsResponse = try decoder.decode(News.self, from: jsonData) 31 | return newsResponse 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /UnitTest-MVVM-C-Example/Coordinator/AppCoordinator.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AppCoordinator.swift 3 | // UnitTest-MVVM-C-Example 4 | // 5 | // Created by Serkan Kara on 14.01.2024. 6 | // 7 | 8 | import UIKit 9 | 10 | protocol AppCoordinatoring { 11 | init(navigation: UINavigationController) 12 | func start() 13 | } 14 | 15 | class AppCoordinator: NSObject, AppCoordinatoring { 16 | 17 | var navigationController: UINavigationController 18 | 19 | required init(navigation: UINavigationController) { 20 | self.navigationController = navigation 21 | } 22 | 23 | func start() { 24 | let service = NetworkServiceImpl() 25 | let vm = NewsListVMImpl(service: service) 26 | let vc = NewsListVC() 27 | vc.inject(vm: vm) 28 | self.navigationController.viewControllers = [vc] 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /UnitTest-MVVM-C-Example/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /UnitTest-MVVM-C-Example/Manager/NetworkLayer.swift: -------------------------------------------------------------------------------- 1 | // 2 | // NetworkLayer.swift 3 | // UnitTest-MVVM-C-Example 4 | // 5 | // Created by Serkan Kara on 14.01.2024. 6 | // 7 | 8 | import Foundation 9 | 10 | protocol NetworkService { 11 | func fetchNews(completion: @escaping (Result) -> Void) 12 | } 13 | 14 | 15 | final class NetworkServiceImpl: NetworkService { 16 | func fetchNews(completion: @escaping (Result) -> Void) { 17 | let urlString = "\(Constants.baseURL.rawValue)\(Constants.urlPath.rawValue)\(Constants.apiKey.rawValue)" 18 | guard let url = URL(string: urlString) else { 19 | completion(.failure(.invalidComplete)) 20 | return 21 | } 22 | URLSession.shared.dataTask(with: url) { data, response, error in 23 | if let _ = error { 24 | completion(.failure(.invalidComplete)) 25 | return 26 | } 27 | 28 | guard let response = response as? HTTPURLResponse, response.statusCode == 200 else { 29 | completion(.failure(.invalidResponse)) 30 | return 31 | } 32 | 33 | guard let data = data else { 34 | completion(.failure(.invalidData)) 35 | return 36 | } 37 | do{ 38 | let news = try JSONDecoder().decode(News.self, from: data) 39 | completion(.success(news)) 40 | }catch{ 41 | completion(.failure(.invalidComplete)) 42 | } 43 | 44 | }.resume() 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /UnitTest-MVVM-C-Example/Model/News.swift: -------------------------------------------------------------------------------- 1 | // 2 | // News.swift 3 | // UnitTest-MVVM-C-Example 4 | // 5 | // Created by Serkan Kara on 14.01.2024. 6 | // 7 | 8 | import Foundation 9 | 10 | struct News: Codable, Hashable { 11 | let articles: [Articles] 12 | } 13 | 14 | struct Articles: Codable, Hashable{ 15 | let title: String? 16 | let description: String? 17 | } 18 | -------------------------------------------------------------------------------- /UnitTest-MVVM-C-Example/Scenes/NewsListVC.swift: -------------------------------------------------------------------------------- 1 | // 2 | // NewsListVC.swift 3 | // UnitTest-MVVM-C-Example 4 | // 5 | // Created by Serkan Kara on 14.01.2024. 6 | // 7 | 8 | import UIKit 9 | 10 | class NewsListVC: BaseViewController { 11 | 12 | @IBOutlet weak var tableView: UITableView! 13 | 14 | private var viewModel: NewsListVM? 15 | var news: News? 16 | 17 | override func viewDidLoad() { 18 | super.viewDidLoad() 19 | viewModel?.delegate = self 20 | viewModel?.load() 21 | setupTableView() 22 | } 23 | 24 | func setupTableView() { 25 | tableView.delegate = self 26 | tableView.dataSource = self 27 | } 28 | 29 | func inject(vm: NewsListVM) { 30 | self.viewModel = vm 31 | } 32 | } 33 | 34 | extension NewsListVC: NewsListVMDelegate { 35 | func handleOutput(output: UserInteraction) { 36 | switch output { 37 | case .newsList(let news): 38 | DispatchQueue.main.async { [weak self] in 39 | self?.news = news 40 | self?.tableView.reloadData() 41 | } 42 | case .showLoading(let isLoading): 43 | isLoading ? startLoading() : stopLoading() 44 | } 45 | } 46 | } 47 | 48 | extension NewsListVC: UITableViewDataSource { 49 | func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { 50 | return news?.articles.count ?? 0 51 | } 52 | 53 | func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { 54 | let cell = UITableViewCell() 55 | cell.textLabel?.text = news?.articles[indexPath.row].title 56 | return cell 57 | } 58 | } 59 | 60 | extension NewsListVC: UITableViewDelegate { } 61 | -------------------------------------------------------------------------------- /UnitTest-MVVM-C-Example/Scenes/NewsListVC.xib: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | -------------------------------------------------------------------------------- /UnitTest-MVVM-C-Example/Scenes/NewsListVM.swift: -------------------------------------------------------------------------------- 1 | // 2 | // NewsListVM.swift 3 | // UnitTest-MVVM-C-Example 4 | // 5 | // Created by Serkan Kara on 14.01.2024. 6 | // 7 | 8 | import Foundation 9 | 10 | protocol NewsListVM { 11 | func load() 12 | var delegate: NewsListVMDelegate? { get set } 13 | } 14 | 15 | protocol NewsListVMDelegate: AnyObject { 16 | func handleOutput(output: UserInteraction) 17 | } 18 | 19 | final class NewsListVMImpl: NewsListVM { 20 | 21 | private var service: NetworkService 22 | weak var delegate: NewsListVMDelegate? 23 | private var news: News? 24 | 25 | init(service: NetworkService) { 26 | self.service = service 27 | } 28 | 29 | func load() { 30 | notify(.showLoading(true)) 31 | service.fetchNews { [weak self] result in 32 | self?.notify(.showLoading(false)) 33 | switch result { 34 | case .success(let news): 35 | self?.news = news 36 | self?.notify(.newsList(news)) 37 | case .failure: 38 | break 39 | } 40 | } 41 | } 42 | 43 | private func notify(_ output: UserInteraction) { 44 | delegate?.handleOutput(output: output) 45 | } 46 | } 47 | 48 | enum UserInteraction: Equatable { 49 | case newsList(News) 50 | case showLoading(Bool) 51 | } 52 | -------------------------------------------------------------------------------- /UnitTest-MVVM-C-Example/Utilities/APISettings/APISettings.swift: -------------------------------------------------------------------------------- 1 | // 2 | // APISettings.swift 3 | // UnitTest-MVVM-C-Example 4 | // 5 | // Created by Serkan Kara on 14.01.2024. 6 | // 7 | 8 | import Foundation 9 | 10 | enum Constants: String { 11 | case baseURL = "https://newsapi.org" 12 | case urlPath = "/v2/top-headlines?country=us&apiKey=" 13 | case apiKey = "64e79656aaf345428ffdaed1015a2dd9" 14 | } 15 | 16 | enum NError: String, Error{ 17 | case invalidData = "The data received from the server was invalid. Please try again" 18 | case invalidResponse = "iInvalid response from the server. Please try again." 19 | case invalidComplete = "Unable to complete your request. Please check your internet connection" 20 | } 21 | 22 | -------------------------------------------------------------------------------- /UnitTest-MVVM-C-Example/Utilities/Base/BaseViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // BaseViewController.swift 3 | // UnitTest-MVVM-C-Example 4 | // 5 | // Created by Serkan Kara on 14.01.2024. 6 | // 7 | 8 | import UIKit 9 | 10 | 11 | class BaseViewController: UIViewController { 12 | 13 | let indicator = UIActivityIndicatorView() 14 | let backgroundView = UIView() 15 | 16 | func setupLoading() { 17 | backgroundView.backgroundColor = .clear 18 | backgroundView.frame = self.view.frame 19 | self.view.addSubview(backgroundView) 20 | backgroundView.addSubview(indicator) 21 | indicator.translatesAutoresizingMaskIntoConstraints = false 22 | NSLayoutConstraint.activate([ 23 | indicator.centerXAnchor.constraint(equalTo: self.view.centerXAnchor), 24 | indicator.centerYAnchor.constraint(equalTo: self.view.centerYAnchor), 25 | indicator.heightAnchor.constraint(equalToConstant: 100), 26 | indicator.widthAnchor.constraint(equalToConstant: 100) 27 | ]) 28 | } 29 | 30 | func startLoading() { 31 | DispatchQueue.main.async { [weak self] in 32 | self?.setupLoading() 33 | self?.indicator.hidesWhenStopped = true 34 | self?.indicator.startAnimating() 35 | self?.indicator.style = .large 36 | self?.indicator.color = .black 37 | self?.indicator.tintColor = .gray 38 | self?.indicator.backgroundColor = .white.withAlphaComponent(0.6) 39 | self?.indicator.layer.cornerRadius = 10 40 | } 41 | } 42 | 43 | func stopLoading() { 44 | DispatchQueue.main.async { [weak self] in 45 | self?.indicator.stopAnimating() 46 | self?.indicator.removeFromSuperview() 47 | self?.backgroundView.removeFromSuperview() 48 | } 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /UnitTest-MVVM-C-ExampleTests/UnitTest_MVVM_C_ExampleTests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // UnitTest_MVVM_C_ExampleTests.swift 3 | // UnitTest-MVVM-C-ExampleTests 4 | // 5 | // Created by Serkan Kara on 14.01.2024. 6 | // 7 | 8 | import XCTest 9 | @testable import UnitTest_MVVM_C_Example 10 | 11 | final class UnitTest_MVVM_C_ExampleTests: XCTestCase { 12 | 13 | private var mockService: MockNewsListService! 14 | private var viewModel: NewsListVM! 15 | private var view: MockView! 16 | 17 | override func setUp() { 18 | super.setUp() 19 | mockService = MockNewsListService() 20 | viewModel = NewsListVMImpl(service: mockService) 21 | view = MockView() 22 | viewModel.delegate = view 23 | } 24 | 25 | func testLoad() throws { 26 | //Given 27 | let news = try ResourceLoader.loadNews(resource: .news) 28 | 29 | //When 30 | viewModel.delegate = view 31 | viewModel.load() 32 | 33 | 34 | //Then 35 | XCTAssertEqual(view.outputs.count, 3) 36 | XCTAssertEqual(view.outputs[0], .showLoading(true)) 37 | XCTAssertEqual(view.outputs[1], .showLoading(false)) 38 | XCTAssertEqual(view.outputs[2], .newsList(news)) 39 | } 40 | } 41 | 42 | private class MockView: NewsListVMDelegate { 43 | 44 | var outputs: [UserInteraction] = [] 45 | 46 | func handleOutput(output: UnitTest_MVVM_C_Example.UserInteraction) { 47 | outputs.append(output) 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /UnitTest-MVVM-C-ExampleUITests/UnitTest_MVVM_C_ExampleUITests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // UnitTest_MVVM_C_ExampleUITests.swift 3 | // UnitTest-MVVM-C-ExampleUITests 4 | // 5 | // Created by Serkan Kara on 14.01.2024. 6 | // 7 | 8 | import XCTest 9 | 10 | final class UnitTest_MVVM_C_ExampleUITests: XCTestCase { 11 | 12 | override func setUpWithError() throws { 13 | // Put setup code here. This method is called before the invocation of each test method in the class. 14 | 15 | // In UI tests it is usually best to stop immediately when a failure occurs. 16 | continueAfterFailure = false 17 | 18 | // 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. 19 | } 20 | 21 | override func tearDownWithError() throws { 22 | // Put teardown code here. This method is called after the invocation of each test method in the class. 23 | } 24 | 25 | func testExample() throws { 26 | // UI tests must launch the application that they test. 27 | let app = XCUIApplication() 28 | app.launch() 29 | 30 | // Use XCTAssert and related functions to verify your tests produce the correct results. 31 | } 32 | 33 | func testLaunchPerformance() throws { 34 | if #available(macOS 10.15, iOS 13.0, tvOS 13.0, watchOS 7.0, *) { 35 | // This measures how long it takes to launch your application. 36 | measure(metrics: [XCTApplicationLaunchMetric()]) { 37 | XCUIApplication().launch() 38 | } 39 | } 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /UnitTest-MVVM-C-ExampleUITests/UnitTest_MVVM_C_ExampleUITestsLaunchTests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // UnitTest_MVVM_C_ExampleUITestsLaunchTests.swift 3 | // UnitTest-MVVM-C-ExampleUITests 4 | // 5 | // Created by Serkan Kara on 14.01.2024. 6 | // 7 | 8 | import XCTest 9 | 10 | final class UnitTest_MVVM_C_ExampleUITestsLaunchTests: XCTestCase { 11 | 12 | override class var runsForEachTargetApplicationUIConfiguration: Bool { 13 | true 14 | } 15 | 16 | override func setUpWithError() throws { 17 | continueAfterFailure = false 18 | } 19 | 20 | func testLaunch() throws { 21 | let app = XCUIApplication() 22 | app.launch() 23 | 24 | // Insert steps here to perform after app launch but before taking a screenshot, 25 | // such as logging into a test account or navigating somewhere in the app 26 | 27 | let attachment = XCTAttachment(screenshot: app.screenshot()) 28 | attachment.name = "Launch Screen" 29 | attachment.lifetime = .keepAlways 30 | add(attachment) 31 | } 32 | } 33 | --------------------------------------------------------------------------------