├── Assertly
├── Resources
│ ├── Assets.xcassets
│ │ ├── Contents.json
│ │ ├── AccentColor.colorset
│ │ │ └── Contents.json
│ │ └── AppIcon.appiconset
│ │ │ └── Contents.json
│ └── Base.lproj
│ │ └── LaunchScreen.storyboard
├── Utils
│ ├── AppConstants.swift
│ ├── NetworkError.swift
│ ├── TestConfiguration.swift
│ ├── extension+XCTestCase.swift
│ ├── Extension+Array.swift
│ └── AssertUnitTestable.swift
├── AssertlyMain
│ ├── NetworkingBaseTest
│ │ ├── AssertlyNetworkingProtocol.swift
│ │ ├── AssertlyMockNetworking.swift
│ │ ├── ErrorHandlingTests.swift
│ │ └── AssertlyNetworkingTests.swift
│ └── ViewModelBaseTest
│ │ ├── AssertlyViewModelProtocol.swift
│ │ ├── MockViewModel.swift
│ │ └── AssertlyViewModelTests.swift
├── Info.plist
├── SampleApp
│ ├── Model
│ │ └── Users.swift
│ ├── ViewModel
│ │ └── SampleViewModel.swift
│ └── ViewController
│ │ └── MockSampleViewController.swift
└── App
│ ├── AppDelegate.swift
│ └── SceneDelegate.swift
├── AssertlyInUse.xcodeproj
├── project.xcworkspace
│ ├── contents.xcworkspacedata
│ └── xcshareddata
│ │ ├── IDEWorkspaceChecks.plist
│ │ └── swiftpm
│ │ └── Package.resolved
├── xcuserdata
│ └── sharon-omoyeni.xcuserdatad
│ │ ├── xcschemes
│ │ └── xcschememanagement.plist
│ │ └── xcdebugger
│ │ └── Breakpoints_v2.xcbkptlist
├── xcshareddata
│ └── xcschemes
│ │ └── AssertlyInUse.xcscheme
└── project.pbxproj
├── AssertlyUITests
├── AssertlyUITestsLaunchTests.swift
└── AssertlyUITests.swift
├── AssertlyTests
├── AssertlyTests.swift
├── SampleNetworkingTest
│ └── NetworkingTests.swift
├── ErrorHandlingTests
│ └── ErrorHandlingTests.swift
├── SampleUseViewModelTest
│ └── AssertlySampleUserViewModelTests.swift
└── SampleControllerTest
│ └── AssertlySampleViewControllerTest.swift
└── README.md
/Assertly/Resources/Assets.xcassets/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "info" : {
3 | "author" : "xcode",
4 | "version" : 1
5 | }
6 | }
7 |
--------------------------------------------------------------------------------
/AssertlyInUse.xcodeproj/project.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/Assertly/Resources/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 |
--------------------------------------------------------------------------------
/Assertly/Utils/AppConstants.swift:
--------------------------------------------------------------------------------
1 | //
2 | // AppConstants.swift
3 | // Assertly
4 | //
5 | // Created by Sharon Omoyeni Babatunde on 27/07/2024.
6 | //
7 |
8 | import Foundation
9 |
10 | enum AppConstants {
11 | static let BASE_URL = infoPlistString(key: "BASE_URL")
12 | }
13 |
--------------------------------------------------------------------------------
/Assertly/Resources/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 |
--------------------------------------------------------------------------------
/Assertly/Utils/NetworkError.swift:
--------------------------------------------------------------------------------
1 | //
2 | // NetworkError.swift
3 | // Assertly
4 | //
5 | // Created by Sharon Omoyeni Babatunde on 25/11/2023.
6 | //
7 |
8 | import Foundation
9 |
10 | enum NetworkError: Error {
11 | case invalidURL
12 | case noData
13 | case decodingError
14 | }
15 |
--------------------------------------------------------------------------------
/AssertlyInUse.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | IDEDidComputeMac32BitWarning
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/Assertly/AssertlyMain/NetworkingBaseTest/AssertlyNetworkingProtocol.swift:
--------------------------------------------------------------------------------
1 | //
2 | // AssertlyNetworkingProtocol.swift
3 | // Assertly
4 | //
5 | // Created by Sharon Omoyeni Babatunde on 06/07/2024.
6 | //
7 |
8 | import Foundation
9 |
10 | public protocol NetworkingProtocol {
11 | func request(_ endpoint: String, completion: @escaping (Result) -> Void)
12 | }
13 |
14 |
--------------------------------------------------------------------------------
/Assertly/AssertlyMain/ViewModelBaseTest/AssertlyViewModelProtocol.swift:
--------------------------------------------------------------------------------
1 | //
2 | // AssertlyViewModelProtocol.swift
3 | // Assertly
4 | //
5 | // Created by Sharon Omoyeni Babatunde on 06/07/2024.
6 | //
7 |
8 | import Foundation
9 |
10 | public protocol ViewModelProtocol {
11 |
12 | var title: String { get }
13 | var mockResults: [String: Any] { get }
14 | var mockErrors: [String: Error] { get }
15 |
16 | }
17 |
--------------------------------------------------------------------------------
/Assertly/Utils/TestConfiguration.swift:
--------------------------------------------------------------------------------
1 | //
2 | // TestConfiguration.swift
3 | // Assertly
4 | //
5 | // Created by Sharon Omoyeni Babatunde on 25/11/2023.
6 | //
7 |
8 | import Foundation
9 |
10 | public struct TestConfiguration {
11 | static var baseURL = "https://api.example.com"
12 | static var apiKey = "test_api_key"
13 |
14 | public static func configure(baseURL: String, apiKey: String) {
15 | self.baseURL = baseURL
16 | self.apiKey = apiKey
17 | }
18 |
19 | }
20 |
--------------------------------------------------------------------------------
/Assertly/Utils/extension+XCTestCase.swift:
--------------------------------------------------------------------------------
1 | //
2 | // extension+XCTestCase.swift
3 | // Assertly
4 | //
5 | // Created by Sharon Omoyeni Babatunde on 25/11/2023.
6 | //
7 |
8 | import Foundation
9 | import XCTest
10 |
11 | public extension XCTestCase {
12 | func waitForAsyncOperation(_ timeout: TimeInterval = 5, operation: @escaping (@escaping () -> Void) -> Void) {
13 | let expectation = self.expectation(description: "Async operation")
14 | operation {
15 | expectation.fulfill()
16 | }
17 | waitForExpectations(timeout: timeout, handler: nil)
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/Assertly/AssertlyMain/NetworkingBaseTest/AssertlyMockNetworking.swift:
--------------------------------------------------------------------------------
1 | //
2 | // MockNetworking.swift
3 | // Assertly
4 | //
5 | // Created by Sharon Omoyeni Babatunde on 06/03/2024.
6 | //
7 |
8 | import Foundation
9 |
10 | public class MockNetworking: NetworkingProtocol {
11 | var mockResult: Result?
12 |
13 | public func request(_ endpoint: String, completion: @escaping (Result) -> Void) {
14 | guard let result = mockResult as? Result else {
15 | fatalError("Mock result not set or of incorrect type")
16 | }
17 | completion(result)
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/Assertly/Utils/Extension+Array.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Extension+Array.swift
3 | // Assertly
4 | //
5 | // Created by Sharon Omoyeni Babatunde on 25/11/2023.
6 | //
7 |
8 | import Foundation
9 |
10 | var infoPlistProperty: [String: Any]? {
11 | if let path = Bundle.main.path(forResource: "Info", ofType: "plist"), let xml = FileManager.default.contents(atPath: path) {
12 | return (try? PropertyListSerialization.propertyList(from: xml, options: .mutableContainersAndLeaves, format: nil)) as? [String: Any]
13 | }
14 | return nil
15 | }
16 |
17 | func infoPlistString(key: String) -> String {
18 | infoPlistProperty![key] as! String
19 | }
20 |
--------------------------------------------------------------------------------
/Assertly/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | BASE_URL
6 | $(BASE_URL)
7 | UIApplicationSceneManifest
8 |
9 | UIApplicationSupportsMultipleScenes
10 |
11 | UISceneConfigurations
12 |
13 | UIWindowSceneSessionRoleApplication
14 |
15 |
16 | UISceneConfigurationName
17 | Default Configuration
18 | UISceneDelegateClassName
19 | $(PRODUCT_MODULE_NAME).SceneDelegate
20 |
21 |
22 |
23 |
24 |
25 |
26 |
--------------------------------------------------------------------------------
/AssertlyInUse.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved:
--------------------------------------------------------------------------------
1 | {
2 | "originHash" : "6e6e2584c46b7f7c049c81c278ac6b3aba14d368f4258016463f5b12e22641ee",
3 | "pins" : [
4 | {
5 | "identity" : "assertly",
6 | "kind" : "remoteSourceControl",
7 | "location" : "https://github.com/yeniObabatunde/Assertly.git",
8 | "state" : {
9 | "branch" : "main",
10 | "revision" : "1f3f207da7446cdfc3f98999a7d8979bc8db02cf"
11 | }
12 | },
13 | {
14 | "identity" : "networkhandlerpackage",
15 | "kind" : "remoteSourceControl",
16 | "location" : "https://github.com/yeniObabatunde/NetworkHandlerPackage.git",
17 | "state" : {
18 | "branch" : "main",
19 | "revision" : "d1e111241811be77aa4f6e7010895f8c3c214e66"
20 | }
21 | }
22 | ],
23 | "version" : 3
24 | }
25 |
--------------------------------------------------------------------------------
/Assertly/SampleApp/Model/Users.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Users.swift
3 | // Assertly
4 | //
5 | // Created by Sharon Omoyeni Babatunde on 06/07/2024.
6 | //
7 |
8 | import Foundation
9 |
10 | struct User: Equatable {
11 | let id: Int
12 | let name: String
13 | }
14 |
15 | struct UserList: Codable, Equatable {
16 | var totalPages: Int?
17 | var data: [UserListDatum]?
18 |
19 | enum CodingKeys: String, CodingKey {
20 | case totalPages = "total_pages"
21 | case data
22 | }
23 | }
24 |
25 | struct UserListDatum: Codable, Equatable {
26 | var id: Int?
27 | var email, firstName, lastName: String?
28 | var avatar: String?
29 |
30 | enum CodingKeys: String, CodingKey {
31 | case id, email
32 | case firstName = "first_name"
33 | case lastName = "last_name"
34 | case avatar
35 | }
36 | }
37 |
38 |
--------------------------------------------------------------------------------
/AssertlyInUse.xcodeproj/xcuserdata/sharon-omoyeni.xcuserdatad/xcschemes/xcschememanagement.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | SchemeUserState
6 |
7 | AssertlyInUse.xcscheme_^#shared#^_
8 |
9 | orderHint
10 | 0
11 |
12 |
13 | SuppressBuildableAutocreation
14 |
15 | 5861BB872C38CCC2001382DB
16 |
17 | primary
18 |
19 |
20 | 5861BB9D2C38CCC5001382DB
21 |
22 | primary
23 |
24 |
25 | 5861BBA72C38CCC5001382DB
26 |
27 | primary
28 |
29 |
30 |
31 |
32 |
33 |
--------------------------------------------------------------------------------
/AssertlyUITests/AssertlyUITestsLaunchTests.swift:
--------------------------------------------------------------------------------
1 | //
2 | // AssertlyUITestsLaunchTests.swift
3 | // AssertlyUITests
4 | //
5 | // Created by Sharon Omoyeni Babatunde on 12/10/2022.
6 | //
7 |
8 | import XCTest
9 |
10 | final class AssertlyUITestsLaunchTests: 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 |
--------------------------------------------------------------------------------
/Assertly/SampleApp/ViewModel/SampleViewModel.swift:
--------------------------------------------------------------------------------
1 | //
2 | // SampleViewModel.swift
3 | // Assertly
4 | //
5 | // Created by Sharon Omoyeni Babatunde on 06/07/2023.
6 | //
7 |
8 | import Foundation
9 | import NetworkHandling
10 |
11 | class AssertlySampleUseViewModel {
12 |
13 | private var networkHandler: NetworkHandling
14 | var userList: ((UserList?) -> Void)?
15 | var onError: ((Error) -> Void)?
16 |
17 | init(networkHandler: NetworkHandling) {
18 | self.networkHandler = networkHandler
19 | }
20 |
21 | func getUser() {
22 | networkHandler.request(with: AppConstants.BASE_URL, method: .GET, body: nil, headers: nil) { [weak self] (result: Result) in
23 |
24 | DispatchQueue.main.async {
25 | switch result {
26 | case .success(let user):
27 | self?.userList?(user)
28 | Logger.printIfDebug(data: "\(user): DATA GOTTEN SUCCESSFULLY", logType: .success)
29 | case .failure(let error):
30 | self?.onError?(error)
31 | }
32 | }
33 | }
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/Assertly/Utils/AssertUnitTestable.swift:
--------------------------------------------------------------------------------
1 | //
2 | // UnitTestableHelpers.swift
3 | // Assertly
4 | //
5 | // Created by Sharon Omoyeni Babatunde on 25/11/2023.
6 | //
7 |
8 | import XCTest
9 |
10 | // MARK: Core Framework Structure
11 | /*
12 | This structure provides a foundation for our unit tests. The UnitTestable protocol ensures that all test cases have a consistent interface, while the BaseTestCase class handles setup and teardown.
13 | */
14 |
15 | public protocol UnitTestable {
16 | associatedtype Dependencies
17 | init(dependencies: Dependencies)
18 | func setUp()
19 | func tearDown()
20 | }
21 |
22 |
23 | public class BaseTestCase: XCTestCase {
24 | public var sut: T?
25 |
26 | public override func setUp() {
27 | super.setUp()
28 | sut = T(dependencies: createDependencies())
29 | sut?.setUp()
30 | }
31 |
32 | public override func tearDown() {
33 | sut?.tearDown()
34 | sut = nil
35 | super.tearDown()
36 | }
37 |
38 | public func createDependencies() -> T.Dependencies {
39 | fatalError("Subclasses must implement createDependencies()")
40 | }
41 | }
42 |
--------------------------------------------------------------------------------
/AssertlyTests/AssertlyTests.swift:
--------------------------------------------------------------------------------
1 | //
2 | // AssertlyTests.swift
3 | // AssertlyTests
4 | //
5 | // Created by Sharon Omoyeni Babatunde on 12/10/2022.
6 | //
7 |
8 | import XCTest
9 | @testable import Assertly
10 |
11 | final class AssertlyTests: XCTestCase {
12 |
13 | override func setUpWithError() throws {
14 | // Put setup code here. This method is called before the invocation of each test method in the class.
15 | }
16 |
17 | override func tearDownWithError() throws {
18 | // Put teardown code here. This method is called after the invocation of each test method in the class.
19 | }
20 |
21 | func testExample() throws {
22 | // This is an example of a functional test case.
23 | // Use XCTAssert and related functions to verify your tests produce the correct results.
24 | // Any test you write for XCTest can be annotated as throws and async.
25 | // Mark your test throws to produce an unexpected failure when your test encounters an uncaught error.
26 | // Mark your test async to allow awaiting for asynchronous code to complete. Check the results with assertions afterwards.
27 | }
28 |
29 | func testPerformanceExample() throws {
30 | // This is an example of a performance test case.
31 | self.measure {
32 | // Put the code you want to measure the time of here.
33 | }
34 | }
35 |
36 | }
37 |
--------------------------------------------------------------------------------
/Assertly/AssertlyMain/NetworkingBaseTest/ErrorHandlingTests.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ErrorHandlingTests.swift
3 | // AssertlyTests
4 | //
5 | // Created by Sharon Omoyeni Babatunde on 20/08/2023.
6 | //
7 |
8 | import XCTest
9 | @testable import Assertly
10 |
11 | class ErrorHandlingTests: BaseTestCase, UnitTestable {
12 | var networking: MockNetworking!
13 |
14 | required init(dependencies: MockNetworking) {
15 | super.init()
16 | self.networking = dependencies
17 | }
18 |
19 | override func setUp() {
20 | super.setUp()
21 | // Additional setup if needed
22 | }
23 |
24 | override func tearDown() {
25 | // Additional teardown if needed
26 | super.tearDown()
27 | }
28 |
29 | override func createDependencies() -> MockNetworking {
30 | return MockNetworking()
31 | }
32 |
33 | func testInvalidURLError() {
34 | networking.mockResult = .failure(NetworkError.invalidURL)
35 |
36 | waitForAsyncOperation { done in
37 | self.networking.request("invalidEndpoint") { (result: Result<[String: String], Error>) in
38 | switch result {
39 | case .success:
40 | XCTFail("Expected failure, got success")
41 | case .failure(let error):
42 | XCTAssertEqual(error as? NetworkError, .invalidURL)
43 | }
44 | done()
45 | }
46 | }
47 | }
48 | }
49 |
--------------------------------------------------------------------------------
/Assertly/App/AppDelegate.swift:
--------------------------------------------------------------------------------
1 | //
2 | // AppDelegate.swift
3 | // Assertly
4 | //
5 | // Created by Sharon Omoyeni Babatunde on 06/07/2022.
6 | //
7 |
8 | import UIKit
9 |
10 | @main
11 | class AppDelegate: UIResponder, UIApplicationDelegate {
12 |
13 |
14 | func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
15 | // Override point for customization after application launch.
16 | return true
17 | }
18 |
19 | // MARK: UISceneSession Lifecycle
20 |
21 | func application(_ application: UIApplication, configurationForConnecting connectingSceneSession: UISceneSession, options: UIScene.ConnectionOptions) -> UISceneConfiguration {
22 | // Called when a new scene session is being created.
23 | // Use this method to select a configuration to create the new scene with.
24 | return UISceneConfiguration(name: "Default Configuration", sessionRole: connectingSceneSession.role)
25 | }
26 |
27 | func application(_ application: UIApplication, didDiscardSceneSessions sceneSessions: Set) {
28 | // Called when the user discards a scene session.
29 | // If any sessions were discarded while the application was not running, this will be called shortly after application:didFinishLaunchingWithOptions.
30 | // Use this method to release any resources that were specific to the discarded scenes, as they will not return.
31 | }
32 |
33 |
34 | }
35 |
36 |
--------------------------------------------------------------------------------
/AssertlyTests/SampleNetworkingTest/NetworkingTests.swift:
--------------------------------------------------------------------------------
1 | //
2 | // NetworkingTests.swift
3 | // AssertlyTests
4 | //
5 | // Created by Sharon Omoyeni Babatunde on 29/07/2024.
6 | //
7 |
8 | import XCTest
9 | import Assertly
10 | @testable import AssertlyInUse
11 |
12 | class NetworkingTests: BaseTestCase, UnitTestable {
13 |
14 | var networking: MockNetworking!
15 |
16 | required init(dependencies: MockNetworking) {
17 | super.init()
18 | self.networking = dependencies
19 | }
20 |
21 | override func setUp() {
22 | super.setUp()
23 | }
24 |
25 | override func tearDown() {
26 | super.tearDown()
27 | }
28 |
29 | override func createDependencies() -> MockNetworking {
30 | return MockNetworking()
31 | }
32 |
33 | func testSuccessfulRequest() {
34 | let expectation = self.expectation(description: "Successful request")
35 | let mockData = ["key": "value"]
36 | networking.mockResult = .success(mockData)
37 |
38 | networking.request("testEndpoint") { (result: Result<[String: String], Error>) in
39 | switch result {
40 | case .success(let data):
41 | XCTAssertEqual(data, mockData)
42 | case .failure:
43 | XCTFail("Expected success, got failure")
44 | }
45 | expectation.fulfill()
46 | }
47 |
48 | waitForExpectations(timeout: 1, handler: nil)
49 | }
50 | }
51 |
--------------------------------------------------------------------------------
/AssertlyTests/ErrorHandlingTests/ErrorHandlingTests.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ErrorHandlingTests.swift
3 | // AssertlyTests
4 | //
5 | // Created by Sharon Omoyeni Babatunde on 29/07/2024.
6 | //
7 |
8 | import XCTest
9 | import Assertly
10 | @testable import AssertlyInUse
11 |
12 | class ErrorHandlingTests: BaseTestCase, UnitTestable {
13 | var networking: MockNetworking!
14 |
15 | required init(dependencies: MockNetworking) {
16 | super.init()
17 | self.networking = dependencies
18 | }
19 |
20 | override func setUp() {
21 | super.setUp()
22 | // Additional setup if needed
23 | }
24 |
25 | override func tearDown() {
26 | // Additional teardown if needed
27 | super.tearDown()
28 | }
29 |
30 | override func createDependencies() -> MockNetworking {
31 | return MockNetworking()
32 | }
33 |
34 | func testInvalidURLError() {
35 | networking.mockResult = .failure(NetworkError.invalidURL)
36 |
37 | waitForAsyncOperation { done in
38 | self.networking.request("invalidEndpoint") { (result: Result<[String: String], Error>) in
39 | switch result {
40 | case .success:
41 | XCTFail("Expected failure, got success")
42 | case .failure(let error):
43 | XCTAssertEqual(error as? NetworkError, .invalidURL)
44 | }
45 | done()
46 | }
47 | }
48 | }
49 | }
50 |
--------------------------------------------------------------------------------
/AssertlyUITests/AssertlyUITests.swift:
--------------------------------------------------------------------------------
1 | //
2 | // AssertlyUITests.swift
3 | // AssertlyUITests
4 | //
5 | // Created by Sharon Omoyeni Babatunde on 12/10/2022.
6 | //
7 |
8 | import XCTest
9 |
10 | final class AssertlyUITests: 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 |
--------------------------------------------------------------------------------
/Assertly/AssertlyMain/NetworkingBaseTest/AssertlyNetworkingTests.swift:
--------------------------------------------------------------------------------
1 | //
2 | // NetworkingTests.swift
3 | // AssertlyTests
4 | //
5 | // Created by Sharon Omoyeni Babatunde on 06/01/2024.
6 | //
7 |
8 |
9 | import XCTest
10 | @testable import Assertly
11 |
12 | class NetworkingTests: BaseTestCase, UnitTestable {
13 |
14 | var networking: MockNetworking!
15 |
16 | required init(dependencies: MockNetworking) {
17 | super.init()
18 | self.networking = dependencies
19 | }
20 |
21 | override func setUp() {
22 | super.setUp()
23 | // Additional setup if needed
24 | }
25 |
26 | override func tearDown() {
27 | // Additional teardown if needed
28 | super.tearDown()
29 | }
30 |
31 | override func createDependencies() -> MockNetworking {
32 | return MockNetworking()
33 | }
34 |
35 | func testSuccessfulRequest() {
36 | let expectation = self.expectation(description: "Successful request")
37 | let mockData = ["key": "value"]
38 | networking.mockResult = .success(mockData)
39 |
40 | networking.request("testEndpoint") { (result: Result<[String: String], Error>) in
41 | switch result {
42 | case .success(let data):
43 | XCTAssertEqual(data, mockData)
44 | case .failure:
45 | XCTFail("Expected success, got failure")
46 | }
47 | expectation.fulfill()
48 | }
49 |
50 | waitForExpectations(timeout: 1, handler: nil)
51 | }
52 | }
53 |
--------------------------------------------------------------------------------
/Assertly/Resources/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 |
--------------------------------------------------------------------------------
/AssertlyTests/SampleUseViewModelTest/AssertlySampleUserViewModelTests.swift:
--------------------------------------------------------------------------------
1 | //
2 | // AssertlySampleUserViewModelTests.swift
3 | // AssertlyTests
4 | //
5 | // Created by Sharon Omoyeni Babatunde on 06/02/2023.
6 | //
7 |
8 | import XCTest
9 | import Assertly
10 | @testable import AssertlyInUse
11 |
12 | final class AssertlySampleUserViewModelTests: AssertlyViewModelTests {
13 |
14 | override func createDependencies() -> MockViewModel {
15 | return MockViewModel()
16 | }
17 |
18 | func testFetchUser() {
19 | let mockUser = User(id: 1, name: "John Doe")
20 | sut?.setMockResult(mockUser, for: "fetchUser")
21 |
22 | testAction("fetchUser", expectedResult: mockUser)
23 | }
24 |
25 | func testFetchUsers() {
26 | let mockUsers = [User(id: 1, name: "John"), User(id: 2, name: "Jane")]
27 | (sut)?.setMockArrayResult(mockUsers, for: "fetchUsers")
28 |
29 | testArrayAction("fetchUsers", expectedResult: mockUsers)
30 | }
31 |
32 | func testFetchUserDict() {
33 | let mockUserDict = [1: User(id: 1, name: "John"), 2: User(id: 2, name: "Jane")]
34 | (sut)?.setMockDictionaryResult(mockUserDict, for: "fetchUserDict")
35 |
36 | testDictionaryAction("fetchUserDict", expectedResult: mockUserDict)
37 | }
38 |
39 | func testFetchUserError() {
40 | let mockError = NSError(domain: "UserError", code: 404, userInfo: [NSLocalizedDescriptionKey: "User not found"])
41 | (sut)?.setMockError(mockError, for: "fetchUser")
42 |
43 | testActionError("fetchUser", expectedError: mockError, as: NSError.self)
44 | }
45 |
46 | func testFetchUserFailure() {
47 | //MARK: This should fail because expected result doesn't equal actual result
48 | let expectedUser = User(id: 1, name: "John Doe")
49 | let actualUser = User(id: 2, name: "Jane Smith") // Different user
50 | sut?.setMockResult(actualUser, for: "fetchUser")
51 | assertNotEqual(actualUser, expectedUser)
52 | }
53 | }
54 |
55 |
--------------------------------------------------------------------------------
/Assertly/App/SceneDelegate.swift:
--------------------------------------------------------------------------------
1 | //
2 | // SceneDelegate.swift
3 | // Assertly
4 | //
5 | // Created by Sharon Omoyeni Babatunde on 06/07/2022.
6 | //
7 |
8 | import UIKit
9 |
10 | class SceneDelegate: UIResponder, UIWindowSceneDelegate {
11 |
12 | var window: UIWindow?
13 |
14 | func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
15 | if let windowScene = scene as? UIWindowScene {
16 | let window = UIWindow(windowScene: windowScene)
17 | window.rootViewController = MockSampleViewController()
18 | self.window = window
19 | window.makeKeyAndVisible()
20 | }
21 |
22 | }
23 |
24 | func sceneDidDisconnect(_ scene: UIScene) {
25 | // Called as the scene is being released by the system.
26 | // This occurs shortly after the scene enters the background, or when its session is discarded.
27 | // Release any resources associated with this scene that can be re-created the next time the scene connects.
28 | // The scene may re-connect later, as its session was not necessarily discarded (see `application:didDiscardSceneSessions` instead).
29 | }
30 |
31 | func sceneDidBecomeActive(_ scene: UIScene) {
32 | // Called when the scene has moved from an inactive state to an active state.
33 | // Use this method to restart any tasks that were paused (or not yet started) when the scene was inactive.
34 | }
35 |
36 | func sceneWillResignActive(_ scene: UIScene) {
37 | // Called when the scene will move from an active state to an inactive state.
38 | // This may occur due to temporary interruptions (ex. an incoming phone call).
39 | }
40 |
41 | func sceneWillEnterForeground(_ scene: UIScene) {
42 | // Called as the scene transitions from the background to the foreground.
43 | // Use this method to undo the changes made on entering the background.
44 | }
45 |
46 | func sceneDidEnterBackground(_ scene: UIScene) {
47 | // Called as the scene transitions from the foreground to the background.
48 | // Use this method to save data, release shared resources, and store enough scene-specific state information
49 | // to restore the scene back to its current state.
50 | }
51 |
52 |
53 | }
54 |
55 |
--------------------------------------------------------------------------------
/AssertlyInUse.xcodeproj/xcuserdata/sharon-omoyeni.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
9 |
21 |
22 |
36 |
37 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
--------------------------------------------------------------------------------
/AssertlyTests/SampleControllerTest/AssertlySampleViewControllerTest.swift:
--------------------------------------------------------------------------------
1 | //
2 | // AssertlySampleViewControllerTest.swift
3 | // Assertly
4 | //
5 | // Created by Sharon Omoyeni Babatunde on 06/07/2024.
6 | //
7 |
8 | import XCTest
9 | import Assertly
10 | @testable import AssertlyInUse
11 |
12 | class AssertlySampleViewControllerTest: AssertlyViewModelTests {
13 |
14 | var viewController: MockSampleViewController!
15 |
16 | override func createDependencies() -> MockViewModel {
17 | return MockViewModel()
18 | }
19 |
20 | override func setUp() {
21 | super.setUp()
22 | viewController = MockSampleViewController()
23 | viewController.loadViewIfNeeded()
24 | }
25 |
26 | override func tearDown() {
27 | viewController = nil
28 | super.tearDown()
29 | }
30 |
31 | func testUpdateUI_WithValidUser() {
32 | //MARK: Given
33 | let expectation = self.expectation(description: "Fetch User Success")
34 | let expectedUser = UserListDatum(id: 1, email: "john@example.com", firstName: "John", lastName: "Doe", avatar: "avatar_url")
35 | sut?.setMockResult(expectedUser, for: "getUser")
36 |
37 | //MARK: When
38 | viewController.fetchUser()
39 |
40 | //MARK: Then
41 | self.assertTrue(self.viewController.fetchUserCalled)
42 | assertNotEqual(expectedUser.email, "johnexample@gmail.com")
43 | expectation.fulfill()
44 |
45 | waitForExpectations(timeout: 1, handler: nil)
46 | }
47 |
48 | func testFetchUser_Failure() {
49 | //MARK: Given
50 | let expectedError = NSError(domain: "TestError", code: 404, userInfo: [NSLocalizedDescriptionKey: "User not found"])
51 | sut?.setMockError(expectedError, for: "getUser")
52 |
53 | //MARK: When
54 | viewController.fetchUser()
55 |
56 | //MARK: Then
57 | assertTrue(viewController.fetchUserCalled)
58 | if let alert = viewController.presentedAlert {
59 | assertEqual(alert.title, "Error", "Alert title should be 'Error'")
60 | assertEqual(alert.message, "User not found", "Alert message should match the error description")
61 | }
62 | }
63 |
64 | func testUpdateUI_WithNilID() {
65 | //MARK: Given
66 | let user = UserListDatum(id: nil, email: "no-id@example.com", firstName: "No", lastName: "ID", avatar: "avatar_url")
67 |
68 | //MARK: When
69 | viewController.updateUI(with: user)
70 |
71 | //MARK: Then
72 | assertEqual(viewController.idLabel.text, "ID: 0", "ID label should display default ID when nil")
73 | }
74 |
75 | func testHandleError_DisplaysAlert() {
76 | //MARK: Given
77 | let error = NSError(domain: "TestError", code: 500, userInfo: [NSLocalizedDescriptionKey: "Server error"])
78 |
79 | //MARK: When
80 | viewController.handle(error: error)
81 |
82 | //MARK: Then
83 | if let alert = assertNotNilAndUnwrap(viewController.presentedAlert, "Error alert should be presented") {
84 | assertEqual(alert.title, "Error", "Alert title should be 'Error'")
85 | assertEqual(alert.message, "Server error", "Alert message should match the error description")
86 | }
87 | }
88 |
89 | }
90 |
--------------------------------------------------------------------------------
/Assertly/AssertlyMain/ViewModelBaseTest/MockViewModel.swift:
--------------------------------------------------------------------------------
1 | //
2 | // MockViewModel.swift
3 | // AssertlyTests
4 | //
5 | // Created by Sharon Omoyeni Babatunde on 06/07/2024.
6 | //
7 |
8 | import Foundation
9 |
10 | public class MockViewModel: ViewModelProtocol {
11 |
12 | public var title: String = "Mock Title"
13 | public var mockResults: [String: Any] = [:]
14 | public var mockErrors: [String: Error] = [:]
15 |
16 | public func performAction(_ action: String, completion: @escaping (Result) -> Void) {
17 | if let error = mockErrors[action] {
18 | completion(.failure(error))
19 | } else if let result = mockResults[action] as? T {
20 | completion(.success(result))
21 | } else {
22 | fatalError("No mock result or error set for action: \(action)")
23 | }
24 | }
25 |
26 | public func performAction(_ action: String, completion: @escaping (Result) -> Void) {
27 | if let result = mockResults[action] as? R {
28 | completion(.success(result))
29 | } else if let error = mockErrors[action] {
30 | completion(.failure(error))
31 | } else {
32 | fatalError("No mock result or error set for action: \(action)")
33 | }
34 | }
35 |
36 | public func setMockResult(_ result: R, for action: String) {
37 | mockResults[action] = result
38 | }
39 |
40 | public func setMockResult(_ result: R, for action: String) {
41 | mockResults[action] = result
42 | }
43 |
44 | public func setMockError(_ error: Error, for action: String) {
45 | mockErrors[action] = error
46 | }
47 |
48 | // Helper methods for common types
49 | public func setMockArrayResult(_ result: [R], for action: String) {
50 | mockResults[action] = result
51 | }
52 |
53 | public func setMockArrayResult(_ result: [R], for action: String) {
54 | mockResults[action] = result
55 | }
56 |
57 | public func setMockDictionaryResult(_ result: [U: Any], for action: String) {
58 | mockResults[action] = result
59 | }
60 |
61 | public func setMockDictionaryResult(_ result: [U: Any], for action: String) {
62 | mockResults[action] = result
63 | }
64 | }
65 |
66 | //MARK: Extension to handle array results
67 | public extension MockViewModel {
68 |
69 | func performArrayAction(_ action: String, completion: @escaping (Result<[R], Error>) -> Void) {
70 | if let result = mockResults[action] as? [R] {
71 | completion(.success(result))
72 | } else if let error = mockErrors[action] {
73 | completion(.failure(error))
74 | } else {
75 | fatalError("No mock array result or error set for action: \(action)")
76 | }
77 | }
78 |
79 | func performArrayAction(_ action: String, completion: @escaping (Result<[R], Error>) -> Void) {
80 | if let result = mockResults[action] as? [R] {
81 | completion(.success(result))
82 | } else if let error = mockErrors[action] {
83 | completion(.failure(error))
84 | } else {
85 | fatalError("No mock array result or error set for action: \(action)")
86 | }
87 | }
88 |
89 | }
90 |
91 | //MARK: Extension to handle dictionary results
92 | public extension MockViewModel {
93 |
94 | func performDictionaryAction(_ action: String, completion: @escaping (Result<[Key: Value], Error>) -> Void) {
95 | if let result = mockResults[action] as? [Key: Value] {
96 | completion(.success(result))
97 | } else if let error = mockErrors[action] {
98 | completion(.failure(error))
99 | } else {
100 | fatalError("No mock dictionary result or error set for action: \(action)")
101 | }
102 | }
103 | }
104 |
--------------------------------------------------------------------------------
/Assertly/SampleApp/ViewController/MockSampleViewController.swift:
--------------------------------------------------------------------------------
1 | //
2 | // MockSampleViewController.swift
3 | // Assertly
4 | //
5 | // Created by Sharon Omoyeni Babatunde on 06/07/2024.
6 | //
7 |
8 | import UIKit
9 | import NetworkHandling
10 | //import Assertly
11 |
12 | class MockSampleViewController: UIViewController {
13 |
14 | //MARK: UI ELEMENT
15 | lazy var nameLabel = UILabel()
16 | lazy var idLabel = UILabel()
17 | var presentedAlert: UIAlertController?
18 |
19 | lazy var actionButton: UIButton = {
20 | let button = UIButton(type: .system)
21 | button.backgroundColor = .purple
22 | button.layer.cornerRadius = 12
23 | button.setTitle("Perform any action", for: .normal)
24 | button.translatesAutoresizingMaskIntoConstraints = false
25 | return button
26 | }()
27 |
28 | // private var mockViewModel: MockViewModel
29 | private let networkHandler: NetworkHandler = NetworkHandler()
30 | private var viewModel: AssertlySampleUseViewModel?
31 | var fetchUserCalled: Bool = false
32 | //
33 | // init(mockViewModel: MockViewModel) {
34 | //
35 | // super.init(nibName: nil, bundle: nil)
36 | // }
37 | //
38 | // required init?(coder: NSCoder) {
39 | // fatalError("init(coder:) has not been implemented")
40 | // }
41 | //
42 | override func viewDidLoad() {
43 | super.viewDidLoad()
44 | setupUI()
45 | onCompletion()
46 | self.viewModel = AssertlySampleUseViewModel(networkHandler: networkHandler)
47 | }
48 |
49 | private func setupUI() {
50 | title = "Mock Sample"
51 | view.backgroundColor = .white
52 |
53 | view.addSubview(nameLabel)
54 | view.addSubview(idLabel)
55 | view.addSubview(actionButton)
56 |
57 | nameLabel.translatesAutoresizingMaskIntoConstraints = false
58 | idLabel.translatesAutoresizingMaskIntoConstraints = false
59 |
60 | NSLayoutConstraint.activate([
61 | nameLabel.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor, constant: 20),
62 | nameLabel.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 20),
63 | nameLabel.trailingAnchor.constraint(equalTo: view.trailingAnchor, constant: -20),
64 |
65 | idLabel.topAnchor.constraint(equalTo: nameLabel.bottomAnchor, constant: 10),
66 | idLabel.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 20),
67 | idLabel.trailingAnchor.constraint(equalTo: view.trailingAnchor, constant: -20),
68 |
69 | actionButton.bottomAnchor.constraint(equalTo: view.safeAreaLayoutGuide.bottomAnchor, constant: -20),
70 | actionButton.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 20),
71 | actionButton.trailingAnchor.constraint(equalTo: view.trailingAnchor, constant: -20),
72 | actionButton.heightAnchor.constraint(equalToConstant: 50)
73 | ])
74 | }
75 |
76 | private func onCompletion() {
77 | fetchUser()
78 | viewModel?.onError = { [weak self] error in
79 | self?.handle(error: error)
80 | }
81 | viewModel?.userList = { [weak self] item in
82 | guard let item = item?.data else {return}
83 | self?.updateUI(with: item[0])
84 | }
85 |
86 | }
87 |
88 | func fetchUser() {
89 | fetchUserCalled = true
90 | viewModel?.getUser()
91 | }
92 |
93 | func updateUI(with user: UserListDatum) {
94 | nameLabel.text = (user.firstName ?? "") + " " + (user.lastName ?? "")
95 | idLabel.text = "ID: \(user.id ?? 0)"
96 | }
97 |
98 | func handle(error: Error) {
99 | let alert = UIAlertController(title: "Error", message: error.localizedDescription, preferredStyle: .alert)
100 | alert.addAction(UIAlertAction(title: "OK", style: .default, handler: nil))
101 | presentedAlert = alert
102 | present(alert, animated: true, completion: nil)
103 | }
104 | }
105 |
--------------------------------------------------------------------------------
/AssertlyInUse.xcodeproj/xcshareddata/xcschemes/AssertlyInUse.xcscheme:
--------------------------------------------------------------------------------
1 |
2 |
5 |
8 |
9 |
15 |
21 |
22 |
23 |
24 |
25 |
31 |
32 |
35 |
41 |
42 |
43 |
46 |
52 |
53 |
54 |
55 |
56 |
66 |
68 |
74 |
75 |
76 |
77 |
83 |
85 |
91 |
92 |
93 |
94 |
96 |
97 |
100 |
101 |
102 |
--------------------------------------------------------------------------------
/Assertly/AssertlyMain/ViewModelBaseTest/AssertlyViewModelTests.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ViewModelTests.swift
3 | // AssertlyTests
4 | //
5 | // Created by Sharon Omoyeni Babatunde on 06/07/2024.
6 | //
7 |
8 | import XCTest
9 | @testable import Assertly
10 |
11 | public class AssertlyViewModelTests: XCTestCase {
12 |
13 | public var sut: T?
14 |
15 | public override func setUp() {
16 | super.setUp()
17 | sut = createDependencies()
18 | }
19 |
20 | public override func tearDown() {
21 | sut = nil
22 | super.tearDown()
23 | }
24 |
25 | public func createDependencies() -> T {
26 | fatalError("Override \("createDependencies") this method in subclass")
27 | }
28 |
29 | public func testAction(_ action: String, expectedResult: R) {
30 | let expectation = self.expectation(description: "Test \(action)")
31 |
32 | (sut as? MockViewModel)?.performAction(action) { (result: Result) in
33 | switch result {
34 | case .success(let value):
35 | XCTAssertEqual(value, expectedResult)
36 | case .failure:
37 | XCTFail("Expected success, got failure")
38 | }
39 | expectation.fulfill()
40 | }
41 |
42 | waitForExpectations(timeout: 1, handler: nil)
43 | }
44 |
45 | public func testArrayAction(_ action: String, expectedResult: [Element]) {
46 | let expectation = self.expectation(description: "Test Array \(action)")
47 |
48 | (sut as? MockViewModel)?.performArrayAction(action) { (result: Result<[Element], Error>) in
49 | switch result {
50 | case .success(let value):
51 | XCTAssertEqual(value, expectedResult)
52 | case .failure:
53 | XCTFail("Expected success, got failure")
54 | }
55 | expectation.fulfill()
56 | }
57 |
58 | waitForExpectations(timeout: 1, handler: nil)
59 | }
60 |
61 | public func testDictionaryAction(_ action: String, expectedResult: [Key: Value]) {
62 | let expectation = self.expectation(description: "Test Dictionary \(action)")
63 |
64 | (sut as? MockViewModel)?.performDictionaryAction(action) { (result: Result<[Key: Value], Error>) in
65 | switch result {
66 | case .success(let value):
67 | XCTAssertEqual(value, expectedResult)
68 | case .failure:
69 | XCTFail("Expected success, got failure")
70 | }
71 | expectation.fulfill()
72 | }
73 |
74 | waitForExpectations(timeout: 1, handler: nil)
75 | }
76 |
77 | public func testActionError(_ action: String, expectedError: Error, as type: R.Type) {
78 | let expectation = self.expectation(description: "Test \(action) Error")
79 |
80 | (sut as? MockViewModel)?.performAction(action) { (result: Result) in
81 | switch result {
82 | case .success:
83 | XCTFail("Expected failure, got success")
84 | case .failure(let error):
85 | XCTAssertEqual(error.localizedDescription, expectedError.localizedDescription)
86 | }
87 | expectation.fulfill()
88 | }
89 |
90 | waitForExpectations(timeout: 1, handler: nil)
91 | }
92 |
93 | public func assertElementProperties(
94 | in viewController: UIViewController,
95 | elements: [String: (getter: () -> R?, expectedValue: R)]
96 | ) {
97 | for (elementName, (getter, expectedValue)) in elements {
98 | guard let actualValue = getter() else {
99 | XCTFail("Element \(elementName) not found in view controller or its value is nil")
100 | continue
101 | }
102 |
103 | XCTAssertEqual(actualValue, expectedValue, "Property for \(elementName) does not match expected value")
104 | }
105 | }
106 |
107 | public func assertElementProperties(
108 | in viewController: UIViewController,
109 | elements: [String: (keyPath: KeyPath, expectedValue: V)]
110 | ) {
111 | for (elementName, (keyPath, expectedValue)) in elements {
112 | guard let element = viewController.value(forKey: elementName) as? R else {
113 | XCTFail("Element \(elementName) not found in view controller or is not of expected type")
114 | continue
115 | }
116 |
117 | let actualValue = element[keyPath: keyPath]
118 | XCTAssertEqual(actualValue, expectedValue, "Property for \(elementName) (\(T.self)) does not match expected value")
119 | }
120 | }
121 |
122 | public func assertBoolean(_ getter: @escaping () -> Bool, expectedValue: Bool, message: String? = nil) {
123 | let actualValue = getter()
124 | if expectedValue {
125 | XCTAssertTrue(actualValue, message ?? "Expected true, but got false")
126 | } else {
127 | XCTAssertFalse(actualValue, message ?? "Expected false, but got true")
128 | }
129 | }
130 |
131 | public func assertNotNil(_ expression: @autoclosure () -> V?,
132 | _ message: @autoclosure () -> String = "",
133 | file: StaticString = #file,
134 | line: UInt = #line) {
135 | let value = expression()
136 | XCTAssertNotNil(value, message(), file: file, line: line)
137 | }
138 |
139 | @discardableResult
140 | public func assertNotNilAndUnwrap(_ expression: @autoclosure () -> V?,
141 | _ message: @autoclosure () -> String = "",
142 | file: StaticString = #file,
143 | line: UInt = #line) -> V? {
144 | let value = expression()
145 | XCTAssertNotNil(value, message(), file: file, line: line)
146 | return value
147 | }
148 |
149 | public func assertEqual(_ expression1: @autoclosure () -> V,
150 | _ expression2: @autoclosure () -> V,
151 | _ message: @autoclosure () -> String = "",
152 | file: StaticString = #file,
153 | line: UInt = #line) {
154 | let value1 = expression1()
155 | let value2 = expression2()
156 | XCTAssertEqual(value1, value2, message(), file: file, line: line)
157 | }
158 |
159 | public func assertEqualOptional(_ expression1: @autoclosure () -> V?,
160 | _ expression2: @autoclosure () -> V?,
161 | _ message: @autoclosure () -> String = "",
162 | file: StaticString = #file,
163 | line: UInt = #line) {
164 | let value1 = expression1()
165 | let value2 = expression2()
166 | XCTAssertEqual(value1, value2, message(), file: file, line: line)
167 | }
168 |
169 | public func assertTrue(_ expression: @autoclosure () -> Bool,
170 | _ message: @autoclosure () -> String = "",
171 | file: StaticString = #file,
172 | line: UInt = #line) {
173 | let value = expression()
174 | XCTAssertTrue(value, message(), file: file, line: line)
175 | }
176 |
177 | public func assertFalse(_ expression: @autoclosure () -> Bool,
178 | _ message: @autoclosure () -> String = "",
179 | file: StaticString = #file,
180 | line: UInt = #line) {
181 | let value = expression()
182 | XCTAssertFalse(value, message(), file: file, line: line)
183 | }
184 |
185 | public func assertNotEqual(_ expression1: @autoclosure () -> V,
186 | _ expression2: @autoclosure () -> V,
187 | _ message: @autoclosure () -> String = "",
188 | file: StaticString = #file,
189 | line: UInt = #line) {
190 | let value1 = expression1()
191 | let value2 = expression2()
192 | XCTAssertNotEqual(value1, value2, message(), file: file, line: line)
193 | }
194 |
195 | public func assertNotIdentical(_ expression1: @autoclosure () -> AnyObject?,
196 | _ expression2: @autoclosure () -> AnyObject?,
197 | _ message: @autoclosure () -> String = "",
198 | file: StaticString = #file,
199 | line: UInt = #line) {
200 | let value1 = expression1()
201 | let value2 = expression2()
202 | XCTAssertNotIdentical(value1, value2, message(), file: file, line: line)
203 | }
204 |
205 | public func assertNoThrow(_ expression: @autoclosure () throws -> V,
206 | _ message: @autoclosure () -> String = "",
207 | file: StaticString = #file,
208 | line: UInt = #line) {
209 | XCTAssertNoThrow(try expression(), message(), file: file, line: line)
210 | }
211 | }
212 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | **Assertly:** Simplifying Swift Unit Testing
2 | Assertly is an open-source Swift framework designed to streamline and enhance unit testing in iOS applications. It provides a set of powerful, easy-to-use assertion methods and testing utilities that simplify the process of writing and maintaining unit tests for controllers, view models, and network classes.
3 | Key Benefits
4 |
5 | Simplified Syntax: Assertly offers a more intuitive and readable syntax for common assertions, reducing boilerplate code and making tests easier to write and understand.
6 | Improved Error Messages: Custom assertions provide more detailed and context-specific error messages, making it easier to identify and fix issues.
7 | Support for Asynchronous Testing: Built-in utilities for testing asynchronous operations, reducing the complexity of testing network calls and other async processes.
8 | Type-Safe Assertions: Leverages Swift's type system to catch potential errors at compile-time, reducing runtime errors.
9 | Consistency: Promotes a consistent testing style across your project, improving maintainability and readability.
10 |
11 | ****Installation****
12 |
13 | **Swift Package Manager (SPM)**
14 | To install AssertlySwift package via Xcode
15 |
16 | Go to File -> Swift Packages -> Add Package Dependency...
17 | Then search for https://github.com/yeniObabatunde/Assertly.git
18 | And Add the package to **your unit Test folder** only
19 |
20 | **Core Features
21 | Custom Assertions**
22 | Assertly provides a suite of custom assertion methods that extend XCTest's functionality:
23 |
24 | ```assertEqual``` and ```assertEqualOptional```: For comparing equatable values.
25 | ```assertTrue``` and ```assertFalse```: For boolean checks.
26 | ```assertNotEqual```: For ensuring values are different.
27 | ```assertNotIdentical```: For checking object instances.
28 | ```assertNoThrow```: For verifying that code doesn't throw errors.
29 | ```assertNotNil``` and ```assertNotNilAndUnwrap```: For optional value checks.
30 |
31 | ```AssertlyViewModelTests``` **ViewModelTests Base Class**
32 | A powerful base class for testing view models, providing:
33 |
34 | Automatic setup and teardown of dependencies.
35 | Methods for testing actions, array actions, and dictionary actions.
36 | Utilities for testing error scenarios.
37 |
38 | **Asynchronous Testing Support**
39 | Built-in support for testing asynchronous operations, including:
40 |
41 | **Expectation management.**
42 | Convenient methods for delayed assertions.
43 |
44 | **Usage Examples
45 | Testing a View Controller**
46 |
47 | ```
48 | class YourViewControllerClassTests: AssertlyViewModelTests {
49 | var viewController: MyViewController!
50 |
51 | override func createDependencies() -> MockViewModel {
52 | return MockViewModel()
53 | }
54 |
55 | override func setUp() {
56 | super.setUp()
57 | viewController = MockSampleViewController()
58 | viewController.loadViewIfNeeded()
59 | }
60 |
61 | func testUpdateUI_WithValidUser() {
62 | //MARK: Given
63 | let expectation = self.expectation(description: "Fetch User Success")
64 | let expectedUser = UserListDatum(id: 1, email: "john@example.com", firstName: "John", lastName: "Doe", avatar: "avatar_url")
65 | sut?.setMockResult(expectedUser, for: "getUser")
66 |
67 | //MARK: When
68 | viewController.fetchUser()
69 |
70 | //MARK: Then
71 | self.assertTrue(self.viewController.fetchUserCalled)
72 | assertNotEqual(expectedUser.email, "johnexample@gmail.com")
73 | expectation.fulfill()
74 |
75 | waitForExpectations(timeout: 1, handler: nil)
76 | }
77 | ```
78 |
79 | **Testing a ViewModel**
80 |
81 | ```
82 | class YourViewModelTestsClass: AssertlyViewModelTests {
83 | func testFetchUser() {
84 | let mockUser = User(id: 1, name: "John Doe")
85 | sut?.setMockResult(mockUser, for: "fetchUser")
86 |
87 | testAction("fetchUser", expectedResult: mockUser)
88 | }
89 |
90 | func testFetchUsers() {
91 | let mockUsers = [User(id: 1, name: "John"), User(id: 2, name: "Jane")]
92 | (sut)?.setMockArrayResult(mockUsers, for: "fetchUsers")
93 |
94 | testArrayAction("fetchUsers", expectedResult: mockUsers)
95 | }
96 | let mockUserDict = [1: User(id: 1, name: "John"), 2: User(id: 2, name: "Jane")]
97 | (sut)?.setMockDictionaryResult(mockUserDict, for: "fetchUserDict")
98 |
99 | testDictionaryAction("fetchUserDict", expectedResult: mockUserDict)
100 | }
101 |
102 | func testFetchUserError() {
103 | let mockError = NSError(domain: "UserError", code: 404, userInfo: [NSLocalizedDescriptionKey: "User not found"])
104 | (sut)?.setMockError(mockError, for: "fetchUser")
105 |
106 | testActionError("fetchUser", expectedError: mockError, as: NSError.self)
107 | }
108 |
109 | func testFetchUserFailure() {
110 | //MARK: This should fail because expected result doesn't equal actual result
111 | let expectedUser = User(id: 1, name: "John Doe")
112 | let actualUser = User(id: 2, name: "Jane Smith") // Different user
113 | sut?.setMockResult(actualUser, for: "fetchUser")
114 | assertNotEqual(actualUser, expectedUser)
115 | }
116 | }
117 |
118 | ```
119 |
120 | Testing Network Calls
121 | ```
122 | class YourNetworkServiceTests: BaseTestCase, UnitTestable{
123 | var networking: MockNetworking!
124 |
125 | required init(dependencies: MockNetworking) {
126 | super.init()
127 | self.networking = dependencies
128 | }
129 |
130 | override func setUp() {
131 | super.setUp()
132 | }
133 |
134 | override func tearDown() {
135 | super.tearDown()
136 | }
137 |
138 | override func createDependencies() -> MockNetworking {
139 | return MockNetworking()
140 | }
141 |
142 | func testSuccessfulRequest() {
143 | let expectation = self.expectation(description: "Successful request")
144 | let mockData = ["key": "value"]
145 | networking.mockResult = .success(mockData)
146 |
147 | networking.request("testEndpoint") { (result: Result<[String: String], Error>) in
148 | switch result {
149 | case .success(let data):
150 | XCTAssertEqual(data, mockData)
151 | case .failure:
152 | XCTFail("Expected success, got failure")
153 | }
154 | expectation.fulfill()
155 | }
156 |
157 | waitForExpectations(timeout: 1, handler: nil)
158 | }
159 | }
160 | ```
161 | **Why Assertly?**
162 | Assertly was developed to address common pain points in iOS unit testing:
163 |
164 | **Verbosity:** Standard XCTest assertions often require verbose syntax. Assertly's custom assertions are more concise and expressive.
165 | Asynchronous Testing Complexity: Testing async code with XCTest can be cumbersome. Assertly provides utilities to simplify this process.
166 | Inconsistent Testing Patterns: Teams often develop inconsistent testing styles. Assertly encourages a unified approach to writing tests.
167 | Limited Error Information: XCTest's default error messages can be vague. Assertly's assertions provide more context when tests fail.
168 | Repetitive Setup Code: The ViewModelTests base class reduces the amount of boilerplate needed for testing view models.
169 |
170 | **Implementation**
171 | A sample project demonstrating the implementation of Assertly is included in the repository. This project showcases:
172 |
173 | **How to integrate Assertly into your Xcode project.**
174 | Examples of testing various components (ViewControllers, ViewModels, NetworkServices) using Assertly.
175 | Best practices for structuring your tests with Assertly.
176 |
177 | **Conclusion**
178 | Assertly aims to make unit testing in Swift more accessible, efficient, and enjoyable. By providing a robust set of tools and promoting best practices, it helps developers write more reliable and maintainable tests, ultimately leading to higher quality iOS applications.
179 | We encourage contributions and feedback from the community to continue improving and expanding Assertly's capabilities.
180 |
181 | **Future Development and Contributions**
182 | We are actively working on expanding Assertly's capabilities and are particularly focused on developing support for UI testing. Our goal is to bring the same level of simplification and efficiency to UI testing that we've achieved with unit testing.
183 | We warmly welcome contributions from the community in various areas:
184 |
185 | **UI Testing Support:** Help us develop and refine tools for streamlining UI tests.
186 | **Additional Assertion Methods: **Propose and implement new assertion methods that could benefit the iOS testing community.
187 | **Performance Improvements:** Optimize existing code for better performance.
188 | **Documentation and Examples:** Enhance our documentation or provide additional usage examples.
189 | **Integration with Other Tools:** Develop integrations with popular iOS development and testing tools.
190 | B**ug Fixes and Refinements:** Help identify and fix any issues in the existing codebase.
191 |
192 | Whether you're an experienced developer or just starting out, your input is valuable. Feel free to submit pull requests, open issues for discussion, or share your ideas on how we can make Assertly even better.
193 |
194 | **How to Contribute
195 | **
196 | We appreciate your interest in contributing to Assertly! Here's how you can get started:
197 |
198 | Fork the repository
199 | Clone your fork to your local machine
200 | Create a new branch from the feature/Contribute branch, following this naming convention:
201 | ```
202 | git checkout -b feature/Contribute/"NewChanges"
203 | ```
204 | Replace "NewChanges" with a brief description of your contribution. For example:
205 | ```
206 | git checkout -b feature/Contribute/UITestSupport **OR** git checkout -b feature/Contribute/NewAssertionMethod
207 | ```
208 | Commit your changes:
209 | ```
210 | git commit -m 'Add some AmazingFeature'
211 | ```
212 | Push to the branch:
213 | ```
214 | git push origin feature/Contribute/"NewChanges"
215 | ```
216 | Open a Pull Request from your new branch to our feature/Contribute branch:
217 |
218 | Please ensure your code adheres to our coding standards and includes appropriate tests and documentation for new features.
219 | We review all pull requests and appreciate your patience during this process. We may suggest some changes or improvements.
220 | Thank you for helping make Assertly better for everyone in the iOS development community!
221 |
--------------------------------------------------------------------------------
/AssertlyInUse.xcodeproj/project.pbxproj:
--------------------------------------------------------------------------------
1 | // !$*UTF8*$!
2 | {
3 | archiveVersion = 1;
4 | classes = {
5 | };
6 | objectVersion = 70;
7 | objects = {
8 |
9 | /* Begin PBXBuildFile section */
10 | 581DB4E42D63FE5700CF30A0 /* NetworkHandling in Frameworks */ = {isa = PBXBuildFile; productRef = 581DB4E32D63FE5700CF30A0 /* NetworkHandling */; };
11 | 581DB4E72D63FEA000CF30A0 /* Assertly in Frameworks */ = {isa = PBXBuildFile; productRef = 581DB4E62D63FEA000CF30A0 /* Assertly */; };
12 | 5861BB8C2C38CCC2001382DB /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5861BB8B2C38CCC2001382DB /* AppDelegate.swift */; };
13 | 5861BB8E2C38CCC2001382DB /* SceneDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5861BB8D2C38CCC2001382DB /* SceneDelegate.swift */; };
14 | 5861BB952C38CCC5001382DB /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 5861BB942C38CCC5001382DB /* Assets.xcassets */; };
15 | 5861BB982C38CCC5001382DB /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 5861BB962C38CCC5001382DB /* LaunchScreen.storyboard */; };
16 | 5861BBA32C38CCC5001382DB /* AssertlyTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5861BBA22C38CCC5001382DB /* AssertlyTests.swift */; };
17 | 5861BBAD2C38CCC5001382DB /* AssertlyUITests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5861BBAC2C38CCC5001382DB /* AssertlyUITests.swift */; };
18 | 5861BBAF2C38CCC5001382DB /* AssertlyUITestsLaunchTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5861BBAE2C38CCC5001382DB /* AssertlyUITestsLaunchTests.swift */; };
19 | 5861BBD52C38D6EC001382DB /* NetworkError.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5861BBD42C38D6EC001382DB /* NetworkError.swift */; };
20 | 5861BBDE2C38DE24001382DB /* Extension+Array.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5861BBDD2C38DE24001382DB /* Extension+Array.swift */; };
21 | 588AF5EE2C39A82F0068FE04 /* MockSampleViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 588AF5ED2C39A82F0068FE04 /* MockSampleViewController.swift */; };
22 | 588AF5F32C39ACC80068FE04 /* Users.swift in Sources */ = {isa = PBXBuildFile; fileRef = 588AF5F22C39ACC80068FE04 /* Users.swift */; };
23 | 588AF5F92C39F8120068FE04 /* SampleViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 588AF5F82C39F8120068FE04 /* SampleViewModel.swift */; };
24 | 58AF973C2C554354004E1628 /* AppConstants.swift in Sources */ = {isa = PBXBuildFile; fileRef = 58AF973B2C554354004E1628 /* AppConstants.swift */; };
25 | /* End PBXBuildFile section */
26 |
27 | /* Begin PBXContainerItemProxy section */
28 | 5861BB9F2C38CCC5001382DB /* PBXContainerItemProxy */ = {
29 | isa = PBXContainerItemProxy;
30 | containerPortal = 5861BB802C38CCC2001382DB /* Project object */;
31 | proxyType = 1;
32 | remoteGlobalIDString = 5861BB872C38CCC2001382DB;
33 | remoteInfo = Assertly;
34 | };
35 | 5861BBA92C38CCC5001382DB /* PBXContainerItemProxy */ = {
36 | isa = PBXContainerItemProxy;
37 | containerPortal = 5861BB802C38CCC2001382DB /* Project object */;
38 | proxyType = 1;
39 | remoteGlobalIDString = 5861BB872C38CCC2001382DB;
40 | remoteInfo = Assertly;
41 | };
42 | /* End PBXContainerItemProxy section */
43 |
44 | /* Begin PBXFileReference section */
45 | 5861BB882C38CCC2001382DB /* AssertlyInUse.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = AssertlyInUse.app; sourceTree = BUILT_PRODUCTS_DIR; };
46 | 5861BB8B2C38CCC2001382DB /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; };
47 | 5861BB8D2C38CCC2001382DB /* SceneDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SceneDelegate.swift; sourceTree = ""; };
48 | 5861BB942C38CCC5001382DB /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; };
49 | 5861BB972C38CCC5001382DB /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; };
50 | 5861BB992C38CCC5001382DB /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; };
51 | 5861BB9E2C38CCC5001382DB /* AssertlyInUseTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = AssertlyInUseTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
52 | 5861BBA22C38CCC5001382DB /* AssertlyTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AssertlyTests.swift; sourceTree = ""; };
53 | 5861BBA82C38CCC5001382DB /* AssertlyInUseUITests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = AssertlyInUseUITests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
54 | 5861BBAC2C38CCC5001382DB /* AssertlyUITests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AssertlyUITests.swift; sourceTree = ""; };
55 | 5861BBAE2C38CCC5001382DB /* AssertlyUITestsLaunchTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AssertlyUITestsLaunchTests.swift; sourceTree = ""; };
56 | 5861BBD42C38D6EC001382DB /* NetworkError.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NetworkError.swift; sourceTree = ""; };
57 | 5861BBDD2C38DE24001382DB /* Extension+Array.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Extension+Array.swift"; sourceTree = ""; };
58 | 588AF5ED2C39A82F0068FE04 /* MockSampleViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MockSampleViewController.swift; sourceTree = ""; };
59 | 588AF5F22C39ACC80068FE04 /* Users.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Users.swift; sourceTree = ""; };
60 | 588AF5F82C39F8120068FE04 /* SampleViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SampleViewModel.swift; sourceTree = ""; };
61 | 58AF973B2C554354004E1628 /* AppConstants.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppConstants.swift; sourceTree = ""; };
62 | /* End PBXFileReference section */
63 |
64 | /* Begin PBXFileSystemSynchronizedRootGroup section */
65 | 584238E82D63F31300F3EDDD /* SampleNetworkingTest */ = {isa = PBXFileSystemSynchronizedRootGroup; explicitFileTypes = {}; explicitFolders = (); path = SampleNetworkingTest; sourceTree = ""; };
66 | 584238E92D63F32B00F3EDDD /* ErrorHandlingTests */ = {isa = PBXFileSystemSynchronizedRootGroup; explicitFileTypes = {}; explicitFolders = (); path = ErrorHandlingTests; sourceTree = ""; };
67 | 584238EA2D63F35200F3EDDD /* SampleControllerTest */ = {isa = PBXFileSystemSynchronizedRootGroup; explicitFileTypes = {}; explicitFolders = (); path = SampleControllerTest; sourceTree = ""; };
68 | 584238EB2D63F37700F3EDDD /* SampleUseViewModelTest */ = {isa = PBXFileSystemSynchronizedRootGroup; explicitFileTypes = {}; explicitFolders = (); path = SampleUseViewModelTest; sourceTree = ""; };
69 | /* End PBXFileSystemSynchronizedRootGroup section */
70 |
71 | /* Begin PBXFrameworksBuildPhase section */
72 | 5861BB852C38CCC2001382DB /* Frameworks */ = {
73 | isa = PBXFrameworksBuildPhase;
74 | buildActionMask = 2147483647;
75 | files = (
76 | 581DB4E42D63FE5700CF30A0 /* NetworkHandling in Frameworks */,
77 | );
78 | runOnlyForDeploymentPostprocessing = 0;
79 | };
80 | 5861BB9B2C38CCC5001382DB /* Frameworks */ = {
81 | isa = PBXFrameworksBuildPhase;
82 | buildActionMask = 2147483647;
83 | files = (
84 | 581DB4E72D63FEA000CF30A0 /* Assertly in Frameworks */,
85 | );
86 | runOnlyForDeploymentPostprocessing = 0;
87 | };
88 | 5861BBA52C38CCC5001382DB /* Frameworks */ = {
89 | isa = PBXFrameworksBuildPhase;
90 | buildActionMask = 2147483647;
91 | files = (
92 | );
93 | runOnlyForDeploymentPostprocessing = 0;
94 | };
95 | /* End PBXFrameworksBuildPhase section */
96 |
97 | /* Begin PBXGroup section */
98 | 5861BB7F2C38CCC2001382DB = {
99 | isa = PBXGroup;
100 | children = (
101 | 5861BB8A2C38CCC2001382DB /* Assertly */,
102 | 5861BBA12C38CCC5001382DB /* AssertlyTests */,
103 | 5861BBAB2C38CCC5001382DB /* AssertlyUITests */,
104 | 5861BB892C38CCC2001382DB /* Products */,
105 | );
106 | sourceTree = "";
107 | };
108 | 5861BB892C38CCC2001382DB /* Products */ = {
109 | isa = PBXGroup;
110 | children = (
111 | 5861BB882C38CCC2001382DB /* AssertlyInUse.app */,
112 | 5861BB9E2C38CCC5001382DB /* AssertlyInUseTests.xctest */,
113 | 5861BBA82C38CCC5001382DB /* AssertlyInUseUITests.xctest */,
114 | );
115 | name = Products;
116 | sourceTree = "";
117 | };
118 | 5861BB8A2C38CCC2001382DB /* Assertly */ = {
119 | isa = PBXGroup;
120 | children = (
121 | 5861BBC52C38D15F001382DB /* App */,
122 | 5861BBDF2C38E3F7001382DB /* SampleApp */,
123 | 5861BBBB2C38CEC9001382DB /* Utils */,
124 | 5861BBC62C38D16C001382DB /* Resources */,
125 | 5861BB992C38CCC5001382DB /* Info.plist */,
126 | );
127 | path = Assertly;
128 | sourceTree = "";
129 | };
130 | 5861BBA12C38CCC5001382DB /* AssertlyTests */ = {
131 | isa = PBXGroup;
132 | children = (
133 | 584238EB2D63F37700F3EDDD /* SampleUseViewModelTest */,
134 | 584238EA2D63F35200F3EDDD /* SampleControllerTest */,
135 | 584238E92D63F32B00F3EDDD /* ErrorHandlingTests */,
136 | 584238E82D63F31300F3EDDD /* SampleNetworkingTest */,
137 | 5861BBA22C38CCC5001382DB /* AssertlyTests.swift */,
138 | );
139 | path = AssertlyTests;
140 | sourceTree = "";
141 | };
142 | 5861BBAB2C38CCC5001382DB /* AssertlyUITests */ = {
143 | isa = PBXGroup;
144 | children = (
145 | 5861BBAC2C38CCC5001382DB /* AssertlyUITests.swift */,
146 | 5861BBAE2C38CCC5001382DB /* AssertlyUITestsLaunchTests.swift */,
147 | );
148 | path = AssertlyUITests;
149 | sourceTree = "";
150 | };
151 | 5861BBBB2C38CEC9001382DB /* Utils */ = {
152 | isa = PBXGroup;
153 | children = (
154 | 5861BBD42C38D6EC001382DB /* NetworkError.swift */,
155 | 5861BBDD2C38DE24001382DB /* Extension+Array.swift */,
156 | 58AF973B2C554354004E1628 /* AppConstants.swift */,
157 | );
158 | path = Utils;
159 | sourceTree = "";
160 | };
161 | 5861BBC52C38D15F001382DB /* App */ = {
162 | isa = PBXGroup;
163 | children = (
164 | 5861BB8D2C38CCC2001382DB /* SceneDelegate.swift */,
165 | 5861BB8B2C38CCC2001382DB /* AppDelegate.swift */,
166 | );
167 | path = App;
168 | sourceTree = "";
169 | };
170 | 5861BBC62C38D16C001382DB /* Resources */ = {
171 | isa = PBXGroup;
172 | children = (
173 | 5861BB962C38CCC5001382DB /* LaunchScreen.storyboard */,
174 | 5861BB942C38CCC5001382DB /* Assets.xcassets */,
175 | );
176 | path = Resources;
177 | sourceTree = "";
178 | };
179 | 5861BBDF2C38E3F7001382DB /* SampleApp */ = {
180 | isa = PBXGroup;
181 | children = (
182 | 588AF5F12C39ACBC0068FE04 /* Model */,
183 | 588AF5FF2C3A25C60068FE04 /* ViewController */,
184 | 588AF5F72C39F7FC0068FE04 /* ViewModel */,
185 | );
186 | path = SampleApp;
187 | sourceTree = "";
188 | };
189 | 588AF5F12C39ACBC0068FE04 /* Model */ = {
190 | isa = PBXGroup;
191 | children = (
192 | 588AF5F22C39ACC80068FE04 /* Users.swift */,
193 | );
194 | path = Model;
195 | sourceTree = "";
196 | };
197 | 588AF5F72C39F7FC0068FE04 /* ViewModel */ = {
198 | isa = PBXGroup;
199 | children = (
200 | 588AF5F82C39F8120068FE04 /* SampleViewModel.swift */,
201 | );
202 | path = ViewModel;
203 | sourceTree = "";
204 | };
205 | 588AF5FF2C3A25C60068FE04 /* ViewController */ = {
206 | isa = PBXGroup;
207 | children = (
208 | 588AF5ED2C39A82F0068FE04 /* MockSampleViewController.swift */,
209 | );
210 | path = ViewController;
211 | sourceTree = "";
212 | };
213 | /* End PBXGroup section */
214 |
215 | /* Begin PBXNativeTarget section */
216 | 5861BB872C38CCC2001382DB /* AssertlyInUse */ = {
217 | isa = PBXNativeTarget;
218 | buildConfigurationList = 5861BBB22C38CCC5001382DB /* Build configuration list for PBXNativeTarget "AssertlyInUse" */;
219 | buildPhases = (
220 | 5861BB842C38CCC2001382DB /* Sources */,
221 | 5861BB852C38CCC2001382DB /* Frameworks */,
222 | 5861BB862C38CCC2001382DB /* Resources */,
223 | );
224 | buildRules = (
225 | );
226 | dependencies = (
227 | );
228 | name = AssertlyInUse;
229 | packageProductDependencies = (
230 | 581DB4E32D63FE5700CF30A0 /* NetworkHandling */,
231 | );
232 | productName = Assertly;
233 | productReference = 5861BB882C38CCC2001382DB /* AssertlyInUse.app */;
234 | productType = "com.apple.product-type.application";
235 | };
236 | 5861BB9D2C38CCC5001382DB /* AssertlyInUseTests */ = {
237 | isa = PBXNativeTarget;
238 | buildConfigurationList = 5861BBB52C38CCC5001382DB /* Build configuration list for PBXNativeTarget "AssertlyInUseTests" */;
239 | buildPhases = (
240 | 5861BB9A2C38CCC5001382DB /* Sources */,
241 | 5861BB9B2C38CCC5001382DB /* Frameworks */,
242 | 5861BB9C2C38CCC5001382DB /* Resources */,
243 | );
244 | buildRules = (
245 | );
246 | dependencies = (
247 | 5861BBA02C38CCC5001382DB /* PBXTargetDependency */,
248 | );
249 | fileSystemSynchronizedGroups = (
250 | 584238E82D63F31300F3EDDD /* SampleNetworkingTest */,
251 | 584238E92D63F32B00F3EDDD /* ErrorHandlingTests */,
252 | 584238EA2D63F35200F3EDDD /* SampleControllerTest */,
253 | 584238EB2D63F37700F3EDDD /* SampleUseViewModelTest */,
254 | );
255 | name = AssertlyInUseTests;
256 | productName = AssertlyTests;
257 | productReference = 5861BB9E2C38CCC5001382DB /* AssertlyInUseTests.xctest */;
258 | productType = "com.apple.product-type.bundle.unit-test";
259 | };
260 | 5861BBA72C38CCC5001382DB /* AssertlyInUseUITests */ = {
261 | isa = PBXNativeTarget;
262 | buildConfigurationList = 5861BBB82C38CCC5001382DB /* Build configuration list for PBXNativeTarget "AssertlyInUseUITests" */;
263 | buildPhases = (
264 | 5861BBA42C38CCC5001382DB /* Sources */,
265 | 5861BBA52C38CCC5001382DB /* Frameworks */,
266 | 5861BBA62C38CCC5001382DB /* Resources */,
267 | );
268 | buildRules = (
269 | );
270 | dependencies = (
271 | 5861BBAA2C38CCC5001382DB /* PBXTargetDependency */,
272 | );
273 | name = AssertlyInUseUITests;
274 | productName = AssertlyUITests;
275 | productReference = 5861BBA82C38CCC5001382DB /* AssertlyInUseUITests.xctest */;
276 | productType = "com.apple.product-type.bundle.ui-testing";
277 | };
278 | /* End PBXNativeTarget section */
279 |
280 | /* Begin PBXProject section */
281 | 5861BB802C38CCC2001382DB /* Project object */ = {
282 | isa = PBXProject;
283 | attributes = {
284 | BuildIndependentTargetsInParallel = 1;
285 | LastSwiftUpdateCheck = 1500;
286 | LastUpgradeCheck = 1500;
287 | TargetAttributes = {
288 | 5861BB872C38CCC2001382DB = {
289 | CreatedOnToolsVersion = 15.0.1;
290 | };
291 | 5861BB9D2C38CCC5001382DB = {
292 | CreatedOnToolsVersion = 15.0.1;
293 | TestTargetID = 5861BB872C38CCC2001382DB;
294 | };
295 | 5861BBA72C38CCC5001382DB = {
296 | CreatedOnToolsVersion = 15.0.1;
297 | TestTargetID = 5861BB872C38CCC2001382DB;
298 | };
299 | };
300 | };
301 | buildConfigurationList = 5861BB832C38CCC2001382DB /* Build configuration list for PBXProject "AssertlyInUse" */;
302 | compatibilityVersion = "Xcode 14.0";
303 | developmentRegion = en;
304 | hasScannedForEncodings = 0;
305 | knownRegions = (
306 | en,
307 | Base,
308 | );
309 | mainGroup = 5861BB7F2C38CCC2001382DB;
310 | packageReferences = (
311 | 581DB4E22D63FE5700CF30A0 /* XCRemoteSwiftPackageReference "NetworkHandlerPackage" */,
312 | 581DB4E52D63FEA000CF30A0 /* XCRemoteSwiftPackageReference "Assertly" */,
313 | );
314 | productRefGroup = 5861BB892C38CCC2001382DB /* Products */;
315 | projectDirPath = "";
316 | projectRoot = "";
317 | targets = (
318 | 5861BB872C38CCC2001382DB /* AssertlyInUse */,
319 | 5861BB9D2C38CCC5001382DB /* AssertlyInUseTests */,
320 | 5861BBA72C38CCC5001382DB /* AssertlyInUseUITests */,
321 | );
322 | };
323 | /* End PBXProject section */
324 |
325 | /* Begin PBXResourcesBuildPhase section */
326 | 5861BB862C38CCC2001382DB /* Resources */ = {
327 | isa = PBXResourcesBuildPhase;
328 | buildActionMask = 2147483647;
329 | files = (
330 | 5861BB982C38CCC5001382DB /* LaunchScreen.storyboard in Resources */,
331 | 5861BB952C38CCC5001382DB /* Assets.xcassets in Resources */,
332 | );
333 | runOnlyForDeploymentPostprocessing = 0;
334 | };
335 | 5861BB9C2C38CCC5001382DB /* Resources */ = {
336 | isa = PBXResourcesBuildPhase;
337 | buildActionMask = 2147483647;
338 | files = (
339 | );
340 | runOnlyForDeploymentPostprocessing = 0;
341 | };
342 | 5861BBA62C38CCC5001382DB /* Resources */ = {
343 | isa = PBXResourcesBuildPhase;
344 | buildActionMask = 2147483647;
345 | files = (
346 | );
347 | runOnlyForDeploymentPostprocessing = 0;
348 | };
349 | /* End PBXResourcesBuildPhase section */
350 |
351 | /* Begin PBXSourcesBuildPhase section */
352 | 5861BB842C38CCC2001382DB /* Sources */ = {
353 | isa = PBXSourcesBuildPhase;
354 | buildActionMask = 2147483647;
355 | files = (
356 | 588AF5EE2C39A82F0068FE04 /* MockSampleViewController.swift in Sources */,
357 | 588AF5F92C39F8120068FE04 /* SampleViewModel.swift in Sources */,
358 | 588AF5F32C39ACC80068FE04 /* Users.swift in Sources */,
359 | 5861BBDE2C38DE24001382DB /* Extension+Array.swift in Sources */,
360 | 5861BBD52C38D6EC001382DB /* NetworkError.swift in Sources */,
361 | 58AF973C2C554354004E1628 /* AppConstants.swift in Sources */,
362 | 5861BB8C2C38CCC2001382DB /* AppDelegate.swift in Sources */,
363 | 5861BB8E2C38CCC2001382DB /* SceneDelegate.swift in Sources */,
364 | );
365 | runOnlyForDeploymentPostprocessing = 0;
366 | };
367 | 5861BB9A2C38CCC5001382DB /* Sources */ = {
368 | isa = PBXSourcesBuildPhase;
369 | buildActionMask = 2147483647;
370 | files = (
371 | 5861BBA32C38CCC5001382DB /* AssertlyTests.swift in Sources */,
372 | );
373 | runOnlyForDeploymentPostprocessing = 0;
374 | };
375 | 5861BBA42C38CCC5001382DB /* Sources */ = {
376 | isa = PBXSourcesBuildPhase;
377 | buildActionMask = 2147483647;
378 | files = (
379 | 5861BBAF2C38CCC5001382DB /* AssertlyUITestsLaunchTests.swift in Sources */,
380 | 5861BBAD2C38CCC5001382DB /* AssertlyUITests.swift in Sources */,
381 | );
382 | runOnlyForDeploymentPostprocessing = 0;
383 | };
384 | /* End PBXSourcesBuildPhase section */
385 |
386 | /* Begin PBXTargetDependency section */
387 | 5861BBA02C38CCC5001382DB /* PBXTargetDependency */ = {
388 | isa = PBXTargetDependency;
389 | target = 5861BB872C38CCC2001382DB /* AssertlyInUse */;
390 | targetProxy = 5861BB9F2C38CCC5001382DB /* PBXContainerItemProxy */;
391 | };
392 | 5861BBAA2C38CCC5001382DB /* PBXTargetDependency */ = {
393 | isa = PBXTargetDependency;
394 | target = 5861BB872C38CCC2001382DB /* AssertlyInUse */;
395 | targetProxy = 5861BBA92C38CCC5001382DB /* PBXContainerItemProxy */;
396 | };
397 | /* End PBXTargetDependency section */
398 |
399 | /* Begin PBXVariantGroup section */
400 | 5861BB962C38CCC5001382DB /* LaunchScreen.storyboard */ = {
401 | isa = PBXVariantGroup;
402 | children = (
403 | 5861BB972C38CCC5001382DB /* Base */,
404 | );
405 | name = LaunchScreen.storyboard;
406 | sourceTree = "";
407 | };
408 | /* End PBXVariantGroup section */
409 |
410 | /* Begin XCBuildConfiguration section */
411 | 5861BBB02C38CCC5001382DB /* Debug */ = {
412 | isa = XCBuildConfiguration;
413 | buildSettings = {
414 | ALWAYS_SEARCH_USER_PATHS = NO;
415 | ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES;
416 | CLANG_ANALYZER_NONNULL = YES;
417 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
418 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++20";
419 | CLANG_ENABLE_MODULES = YES;
420 | CLANG_ENABLE_OBJC_ARC = YES;
421 | CLANG_ENABLE_OBJC_WEAK = YES;
422 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
423 | CLANG_WARN_BOOL_CONVERSION = YES;
424 | CLANG_WARN_COMMA = YES;
425 | CLANG_WARN_CONSTANT_CONVERSION = YES;
426 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
427 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
428 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
429 | CLANG_WARN_EMPTY_BODY = YES;
430 | CLANG_WARN_ENUM_CONVERSION = YES;
431 | CLANG_WARN_INFINITE_RECURSION = YES;
432 | CLANG_WARN_INT_CONVERSION = YES;
433 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
434 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
435 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
436 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
437 | CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
438 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
439 | CLANG_WARN_STRICT_PROTOTYPES = YES;
440 | CLANG_WARN_SUSPICIOUS_MOVE = YES;
441 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
442 | CLANG_WARN_UNREACHABLE_CODE = YES;
443 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
444 | COPY_PHASE_STRIP = NO;
445 | DEBUG_INFORMATION_FORMAT = dwarf;
446 | ENABLE_STRICT_OBJC_MSGSEND = YES;
447 | ENABLE_TESTABILITY = YES;
448 | ENABLE_TESTING_SEARCH_PATHS = YES;
449 | ENABLE_USER_SCRIPT_SANDBOXING = YES;
450 | GCC_C_LANGUAGE_STANDARD = gnu17;
451 | GCC_DYNAMIC_NO_PIC = NO;
452 | GCC_NO_COMMON_BLOCKS = YES;
453 | GCC_OPTIMIZATION_LEVEL = 0;
454 | GCC_PREPROCESSOR_DEFINITIONS = (
455 | "DEBUG=1",
456 | "$(inherited)",
457 | );
458 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
459 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
460 | GCC_WARN_UNDECLARED_SELECTOR = YES;
461 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
462 | GCC_WARN_UNUSED_FUNCTION = YES;
463 | GCC_WARN_UNUSED_VARIABLE = YES;
464 | IPHONEOS_DEPLOYMENT_TARGET = 17.0;
465 | LOCALIZATION_PREFERS_STRING_CATALOGS = YES;
466 | MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
467 | MTL_FAST_MATH = YES;
468 | ONLY_ACTIVE_ARCH = NO;
469 | SDKROOT = iphoneos;
470 | SWIFT_ACTIVE_COMPILATION_CONDITIONS = "DEBUG $(inherited)";
471 | SWIFT_OPTIMIZATION_LEVEL = "-Onone";
472 | };
473 | name = Debug;
474 | };
475 | 5861BBB12C38CCC5001382DB /* Release */ = {
476 | isa = XCBuildConfiguration;
477 | buildSettings = {
478 | ALWAYS_SEARCH_USER_PATHS = NO;
479 | ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES;
480 | CLANG_ANALYZER_NONNULL = YES;
481 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
482 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++20";
483 | CLANG_ENABLE_MODULES = YES;
484 | CLANG_ENABLE_OBJC_ARC = YES;
485 | CLANG_ENABLE_OBJC_WEAK = YES;
486 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
487 | CLANG_WARN_BOOL_CONVERSION = YES;
488 | CLANG_WARN_COMMA = YES;
489 | CLANG_WARN_CONSTANT_CONVERSION = YES;
490 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
491 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
492 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
493 | CLANG_WARN_EMPTY_BODY = YES;
494 | CLANG_WARN_ENUM_CONVERSION = YES;
495 | CLANG_WARN_INFINITE_RECURSION = YES;
496 | CLANG_WARN_INT_CONVERSION = YES;
497 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
498 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
499 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
500 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
501 | CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
502 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
503 | CLANG_WARN_STRICT_PROTOTYPES = YES;
504 | CLANG_WARN_SUSPICIOUS_MOVE = YES;
505 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
506 | CLANG_WARN_UNREACHABLE_CODE = YES;
507 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
508 | COPY_PHASE_STRIP = NO;
509 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
510 | ENABLE_NS_ASSERTIONS = NO;
511 | ENABLE_STRICT_OBJC_MSGSEND = YES;
512 | ENABLE_TESTING_SEARCH_PATHS = YES;
513 | ENABLE_USER_SCRIPT_SANDBOXING = YES;
514 | GCC_C_LANGUAGE_STANDARD = gnu17;
515 | GCC_NO_COMMON_BLOCKS = YES;
516 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
517 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
518 | GCC_WARN_UNDECLARED_SELECTOR = YES;
519 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
520 | GCC_WARN_UNUSED_FUNCTION = YES;
521 | GCC_WARN_UNUSED_VARIABLE = YES;
522 | IPHONEOS_DEPLOYMENT_TARGET = 17.0;
523 | LOCALIZATION_PREFERS_STRING_CATALOGS = YES;
524 | MTL_ENABLE_DEBUG_INFO = NO;
525 | MTL_FAST_MATH = YES;
526 | SDKROOT = iphoneos;
527 | SWIFT_COMPILATION_MODE = wholemodule;
528 | VALIDATE_PRODUCT = YES;
529 | };
530 | name = Release;
531 | };
532 | 5861BBB32C38CCC5001382DB /* Debug */ = {
533 | isa = XCBuildConfiguration;
534 | buildSettings = {
535 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
536 | ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
537 | BASE_URL = "https://reqres.in/api/users";
538 | CODE_SIGN_STYLE = Automatic;
539 | CURRENT_PROJECT_VERSION = 1;
540 | DEVELOPMENT_TEAM = 8JM7VY488D;
541 | GENERATE_INFOPLIST_FILE = YES;
542 | INFOPLIST_FILE = Assertly/Info.plist;
543 | INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES;
544 | INFOPLIST_KEY_UILaunchStoryboardName = LaunchScreen;
545 | INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight";
546 | INFOPLIST_KEY_UISupportedInterfaceOrientations_iPhone = "UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight";
547 | LD_RUNPATH_SEARCH_PATHS = (
548 | "$(inherited)",
549 | "@executable_path/Frameworks",
550 | );
551 | MARKETING_VERSION = 1.0;
552 | ONLY_ACTIVE_ARCH = NO;
553 | PRODUCT_BUNDLE_IDENTIFIER = Items.Assertly;
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 | 5861BBB42C38CCC5001382DB /* Release */ = {
562 | isa = XCBuildConfiguration;
563 | buildSettings = {
564 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
565 | ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
566 | BASE_URL = "https://reqres.in/api/users";
567 | CODE_SIGN_STYLE = Automatic;
568 | CURRENT_PROJECT_VERSION = 1;
569 | DEVELOPMENT_TEAM = 8JM7VY488D;
570 | GENERATE_INFOPLIST_FILE = YES;
571 | INFOPLIST_FILE = Assertly/Info.plist;
572 | INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES;
573 | INFOPLIST_KEY_UILaunchStoryboardName = LaunchScreen;
574 | INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight";
575 | INFOPLIST_KEY_UISupportedInterfaceOrientations_iPhone = "UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight";
576 | LD_RUNPATH_SEARCH_PATHS = (
577 | "$(inherited)",
578 | "@executable_path/Frameworks",
579 | );
580 | MARKETING_VERSION = 1.0;
581 | PRODUCT_BUNDLE_IDENTIFIER = Items.Assertly;
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 | 5861BBB62C38CCC5001382DB /* 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 = 8JM7VY488D;
597 | GENERATE_INFOPLIST_FILE = YES;
598 | IPHONEOS_DEPLOYMENT_TARGET = 17.0;
599 | MARKETING_VERSION = 1.0;
600 | PRODUCT_BUNDLE_IDENTIFIER = Items.AssertlyTests;
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)/AssertlyInUse.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/AssertlyInUse";
606 | };
607 | name = Debug;
608 | };
609 | 5861BBB72C38CCC5001382DB /* 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 = 8JM7VY488D;
617 | GENERATE_INFOPLIST_FILE = YES;
618 | IPHONEOS_DEPLOYMENT_TARGET = 17.0;
619 | MARKETING_VERSION = 1.0;
620 | PRODUCT_BUNDLE_IDENTIFIER = Items.AssertlyTests;
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)/AssertlyInUse.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/AssertlyInUse";
626 | };
627 | name = Release;
628 | };
629 | 5861BBB92C38CCC5001382DB /* 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 = 8JM7VY488D;
636 | GENERATE_INFOPLIST_FILE = YES;
637 | MARKETING_VERSION = 1.0;
638 | PRODUCT_BUNDLE_IDENTIFIER = Items.AssertlyUITests;
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 = Assertly;
644 | };
645 | name = Debug;
646 | };
647 | 5861BBBA2C38CCC5001382DB /* 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 = 8JM7VY488D;
654 | GENERATE_INFOPLIST_FILE = YES;
655 | MARKETING_VERSION = 1.0;
656 | PRODUCT_BUNDLE_IDENTIFIER = Items.AssertlyUITests;
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 = Assertly;
662 | };
663 | name = Release;
664 | };
665 | /* End XCBuildConfiguration section */
666 |
667 | /* Begin XCConfigurationList section */
668 | 5861BB832C38CCC2001382DB /* Build configuration list for PBXProject "AssertlyInUse" */ = {
669 | isa = XCConfigurationList;
670 | buildConfigurations = (
671 | 5861BBB02C38CCC5001382DB /* Debug */,
672 | 5861BBB12C38CCC5001382DB /* Release */,
673 | );
674 | defaultConfigurationIsVisible = 0;
675 | defaultConfigurationName = Release;
676 | };
677 | 5861BBB22C38CCC5001382DB /* Build configuration list for PBXNativeTarget "AssertlyInUse" */ = {
678 | isa = XCConfigurationList;
679 | buildConfigurations = (
680 | 5861BBB32C38CCC5001382DB /* Debug */,
681 | 5861BBB42C38CCC5001382DB /* Release */,
682 | );
683 | defaultConfigurationIsVisible = 0;
684 | defaultConfigurationName = Release;
685 | };
686 | 5861BBB52C38CCC5001382DB /* Build configuration list for PBXNativeTarget "AssertlyInUseTests" */ = {
687 | isa = XCConfigurationList;
688 | buildConfigurations = (
689 | 5861BBB62C38CCC5001382DB /* Debug */,
690 | 5861BBB72C38CCC5001382DB /* Release */,
691 | );
692 | defaultConfigurationIsVisible = 0;
693 | defaultConfigurationName = Release;
694 | };
695 | 5861BBB82C38CCC5001382DB /* Build configuration list for PBXNativeTarget "AssertlyInUseUITests" */ = {
696 | isa = XCConfigurationList;
697 | buildConfigurations = (
698 | 5861BBB92C38CCC5001382DB /* Debug */,
699 | 5861BBBA2C38CCC5001382DB /* Release */,
700 | );
701 | defaultConfigurationIsVisible = 0;
702 | defaultConfigurationName = Release;
703 | };
704 | /* End XCConfigurationList section */
705 |
706 | /* Begin XCRemoteSwiftPackageReference section */
707 | 581DB4E22D63FE5700CF30A0 /* XCRemoteSwiftPackageReference "NetworkHandlerPackage" */ = {
708 | isa = XCRemoteSwiftPackageReference;
709 | repositoryURL = "https://github.com/yeniObabatunde/NetworkHandlerPackage.git";
710 | requirement = {
711 | branch = main;
712 | kind = branch;
713 | };
714 | };
715 | 581DB4E52D63FEA000CF30A0 /* XCRemoteSwiftPackageReference "Assertly" */ = {
716 | isa = XCRemoteSwiftPackageReference;
717 | repositoryURL = "https://github.com/yeniObabatunde/Assertly.git";
718 | requirement = {
719 | branch = main;
720 | kind = branch;
721 | };
722 | };
723 | /* End XCRemoteSwiftPackageReference section */
724 |
725 | /* Begin XCSwiftPackageProductDependency section */
726 | 581DB4E32D63FE5700CF30A0 /* NetworkHandling */ = {
727 | isa = XCSwiftPackageProductDependency;
728 | package = 581DB4E22D63FE5700CF30A0 /* XCRemoteSwiftPackageReference "NetworkHandlerPackage" */;
729 | productName = NetworkHandling;
730 | };
731 | 581DB4E62D63FEA000CF30A0 /* Assertly */ = {
732 | isa = XCSwiftPackageProductDependency;
733 | package = 581DB4E52D63FEA000CF30A0 /* XCRemoteSwiftPackageReference "Assertly" */;
734 | productName = Assertly;
735 | };
736 | /* End XCSwiftPackageProductDependency section */
737 | };
738 | rootObject = 5861BB802C38CCC2001382DB /* Project object */;
739 | }
740 |
--------------------------------------------------------------------------------