├── Podfile ├── Podfile.lock ├── Pods ├── Alamofire │ ├── LICENSE │ ├── README.md │ └── Source │ │ ├── AFError.swift │ │ ├── Alamofire.swift │ │ ├── AlamofireExtended.swift │ │ ├── AuthenticationInterceptor.swift │ │ ├── CachedResponseHandler.swift │ │ ├── Combine.swift │ │ ├── DispatchQueue+Alamofire.swift │ │ ├── EventMonitor.swift │ │ ├── HTTPHeaders.swift │ │ ├── HTTPMethod.swift │ │ ├── MultipartFormData.swift │ │ ├── MultipartUpload.swift │ │ ├── NetworkReachabilityManager.swift │ │ ├── Notifications.swift │ │ ├── OperationQueue+Alamofire.swift │ │ ├── ParameterEncoder.swift │ │ ├── ParameterEncoding.swift │ │ ├── Protected.swift │ │ ├── RedirectHandler.swift │ │ ├── Request.swift │ │ ├── RequestInterceptor.swift │ │ ├── RequestTaskMap.swift │ │ ├── Response.swift │ │ ├── ResponseSerialization.swift │ │ ├── Result+Alamofire.swift │ │ ├── RetryPolicy.swift │ │ ├── ServerTrustEvaluation.swift │ │ ├── Session.swift │ │ ├── SessionDelegate.swift │ │ ├── StringEncoding+Alamofire.swift │ │ ├── URLConvertible+URLRequestConvertible.swift │ │ ├── URLEncodedFormEncoder.swift │ │ ├── URLRequest+Alamofire.swift │ │ ├── URLSessionConfiguration+Alamofire.swift │ │ └── Validation.swift ├── Manifest.lock ├── Pods.xcodeproj │ ├── project.pbxproj │ └── xcuserdata │ │ └── dani.xcuserdatad │ │ └── xcschemes │ │ ├── Alamofire.xcscheme │ │ ├── Pods-clean_architecture_mvvm_base-clean_architecture_mvvm_baseUITests.xcscheme │ │ ├── Pods-clean_architecture_mvvm_base.xcscheme │ │ ├── Pods-clean_architecture_mvvm_baseTests.xcscheme │ │ └── xcschememanagement.plist └── Target Support Files │ ├── Alamofire │ ├── Alamofire-Info.plist │ ├── Alamofire-dummy.m │ ├── Alamofire-prefix.pch │ ├── Alamofire-umbrella.h │ ├── Alamofire.debug.xcconfig │ ├── Alamofire.modulemap │ └── Alamofire.release.xcconfig │ ├── Pods-clean_architecture_mvvm_base-clean_architecture_mvvm_baseUITests │ ├── Pods-clean_architecture_mvvm_base-clean_architecture_mvvm_baseUITests-Info.plist │ ├── Pods-clean_architecture_mvvm_base-clean_architecture_mvvm_baseUITests-acknowledgements.markdown │ ├── Pods-clean_architecture_mvvm_base-clean_architecture_mvvm_baseUITests-acknowledgements.plist │ ├── Pods-clean_architecture_mvvm_base-clean_architecture_mvvm_baseUITests-dummy.m │ ├── Pods-clean_architecture_mvvm_base-clean_architecture_mvvm_baseUITests-frameworks-Debug-input-files.xcfilelist │ ├── Pods-clean_architecture_mvvm_base-clean_architecture_mvvm_baseUITests-frameworks-Debug-output-files.xcfilelist │ ├── Pods-clean_architecture_mvvm_base-clean_architecture_mvvm_baseUITests-frameworks-Release-input-files.xcfilelist │ ├── Pods-clean_architecture_mvvm_base-clean_architecture_mvvm_baseUITests-frameworks-Release-output-files.xcfilelist │ ├── Pods-clean_architecture_mvvm_base-clean_architecture_mvvm_baseUITests-frameworks.sh │ ├── Pods-clean_architecture_mvvm_base-clean_architecture_mvvm_baseUITests-umbrella.h │ ├── Pods-clean_architecture_mvvm_base-clean_architecture_mvvm_baseUITests.debug.xcconfig │ ├── Pods-clean_architecture_mvvm_base-clean_architecture_mvvm_baseUITests.modulemap │ └── Pods-clean_architecture_mvvm_base-clean_architecture_mvvm_baseUITests.release.xcconfig │ ├── Pods-clean_architecture_mvvm_base │ ├── Pods-clean_architecture_mvvm_base-Info.plist │ ├── Pods-clean_architecture_mvvm_base-acknowledgements.markdown │ ├── Pods-clean_architecture_mvvm_base-acknowledgements.plist │ ├── Pods-clean_architecture_mvvm_base-dummy.m │ ├── Pods-clean_architecture_mvvm_base-frameworks-Debug-input-files.xcfilelist │ ├── Pods-clean_architecture_mvvm_base-frameworks-Debug-output-files.xcfilelist │ ├── Pods-clean_architecture_mvvm_base-frameworks-Release-input-files.xcfilelist │ ├── Pods-clean_architecture_mvvm_base-frameworks-Release-output-files.xcfilelist │ ├── Pods-clean_architecture_mvvm_base-frameworks.sh │ ├── Pods-clean_architecture_mvvm_base-umbrella.h │ ├── Pods-clean_architecture_mvvm_base.debug.xcconfig │ ├── Pods-clean_architecture_mvvm_base.modulemap │ └── Pods-clean_architecture_mvvm_base.release.xcconfig │ └── Pods-clean_architecture_mvvm_baseTests │ ├── Pods-clean_architecture_mvvm_baseTests-Info.plist │ ├── Pods-clean_architecture_mvvm_baseTests-acknowledgements.markdown │ ├── Pods-clean_architecture_mvvm_baseTests-acknowledgements.plist │ ├── Pods-clean_architecture_mvvm_baseTests-dummy.m │ ├── Pods-clean_architecture_mvvm_baseTests-umbrella.h │ ├── Pods-clean_architecture_mvvm_baseTests.debug.xcconfig │ ├── Pods-clean_architecture_mvvm_baseTests.modulemap │ └── Pods-clean_architecture_mvvm_baseTests.release.xcconfig ├── README.md ├── clean_architecture_mvvm_base.xcodeproj ├── project.pbxproj ├── project.xcworkspace │ ├── contents.xcworkspacedata │ ├── xcshareddata │ │ └── IDEWorkspaceChecks.plist │ └── xcuserdata │ │ └── dani.xcuserdatad │ │ └── UserInterfaceState.xcuserstate └── xcuserdata │ └── dani.xcuserdatad │ └── xcschemes │ └── xcschememanagement.plist ├── clean_architecture_mvvm_base.xcworkspace ├── contents.xcworkspacedata ├── xcshareddata │ └── IDEWorkspaceChecks.plist └── xcuserdata │ └── dani.xcuserdatad │ └── UserInterfaceState.xcuserstate ├── clean_architecture_mvvm_base ├── Application │ ├── AppDelegate.swift │ ├── Architecture │ │ ├── BaseViewController.swift │ │ └── BaseViewModel.swift │ └── SceneDelegate.swift ├── Data │ ├── DataSource │ │ └── CarTypeDataSource.swift │ ├── Models │ │ ├── Request │ │ │ ├── MainTypeRequest.swift │ │ │ ├── ManufacturerTypesRequest.swift │ │ │ └── PagingRequest.swift │ │ └── Response │ │ │ ├── CarTypesResponse.swift │ │ │ └── ErrorResponse.swift │ ├── Remote │ │ ├── EndPoints.swift │ │ └── Services │ │ │ └── CarTypes.swift │ └── Repository │ │ └── CarTypeRepositoryIMP.swift ├── Domain │ ├── Entities │ │ ├── MainTypes │ │ │ ├── MainTypeEntity.swift │ │ │ └── MainTypesEntity.swift │ │ ├── Manufacturer │ │ │ ├── ManufacturerEntity.swift │ │ │ └── ManufacturersEntity.swift │ │ └── Response.swift │ ├── Repository │ │ └── CarTypeRepository.swift │ └── UseCase │ │ ├── CarType │ │ ├── CarTypeUseCase.swift │ │ ├── FetchMainTypes.swift │ │ └── FetchManufacturer.swift │ │ └── UseCase.swift ├── Extensions │ ├── CodableExtension.swift │ ├── StringExtension.swift │ ├── UITableViewExtension.swift │ ├── UIView.swift │ └── UIViewControllerExtension.swift ├── Infrastructure │ ├── Enums │ │ ├── Environment.swift │ │ └── HTTPMethodType.swift │ └── Networking │ │ ├── API.swift │ │ └── BaseNetworking.swift ├── Presentation │ ├── Base.lproj │ │ └── Main.storyboard │ ├── CarTypes │ │ ├── Cell │ │ │ ├── ManufacturerCell.swift │ │ │ └── ManufacturerCell.xib │ │ ├── StoryBoard │ │ │ └── CarTypesStoryBoard.storyboard │ │ └── ViewController │ │ │ ├── MainTypes │ │ │ ├── MainTypesViewController.swift │ │ │ ├── MainTypesViewControllerExtension.swift │ │ │ └── MainTypesViewModel.swift │ │ │ └── Manufacturer │ │ │ ├── ManufacturerViewController.swift │ │ │ ├── ManufacturerViewControllerExtension.swift │ │ │ └── ManufacturerViewModel.swift │ ├── Helpers │ │ └── PresentationHelper.swift │ ├── SplashScreenViewController.swift │ └── SplashScreenViewModel.swift └── Resources │ ├── AppEnvironment.swift │ ├── Assets.xcassets │ ├── AccentColor.colorset │ │ └── Contents.json │ ├── AppIcon.appiconset │ │ └── Contents.json │ ├── Contents.json │ └── logo.imageset │ │ └── Contents.json │ ├── Constants.swift │ ├── Info.plist │ ├── UI │ ├── Base.lproj │ │ └── LaunchScreen.storyboard │ └── Loader.swift │ └── en.lproj │ └── Localizable.strings ├── clean_architecture_mvvm_baseTests └── clean_architecture_mvvm_baseTests.swift └── clean_architecture_mvvm_baseUITests ├── clean_architecture_mvvm_baseUITests.swift └── clean_architecture_mvvm_baseUITestsLaunchTests.swift /Podfile: -------------------------------------------------------------------------------- 1 | # Uncomment the next line to define a global platform for your project 2 | # platform :ios, '9.0' 3 | 4 | target 'clean_architecture_mvvm_base' do 5 | # Comment the next line if you don't want to use dynamic frameworks 6 | use_frameworks! 7 | 8 | # Pods for clean_architecture_mvvm_base 9 | 10 | pod 'Alamofire', '~> 5.4' 11 | 12 | target 'clean_architecture_mvvm_baseTests' do 13 | inherit! :search_paths 14 | # Pods for testing 15 | end 16 | 17 | target 'clean_architecture_mvvm_baseUITests' do 18 | # Pods for testing 19 | end 20 | 21 | end 22 | -------------------------------------------------------------------------------- /Podfile.lock: -------------------------------------------------------------------------------- 1 | PODS: 2 | - Alamofire (5.4.4) 3 | 4 | DEPENDENCIES: 5 | - Alamofire (~> 5.4) 6 | 7 | SPEC REPOS: 8 | trunk: 9 | - Alamofire 10 | 11 | SPEC CHECKSUMS: 12 | Alamofire: f3b09a368f1582ab751b3fff5460276e0d2cf5c9 13 | 14 | PODFILE CHECKSUM: 9c6229bd8decf88d475ec993127e0b09e0caed83 15 | 16 | COCOAPODS: 1.11.2 17 | -------------------------------------------------------------------------------- /Pods/Alamofire/LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2014-2021 Alamofire Software Foundation (http://alamofire.org/) 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in 11 | all copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | THE SOFTWARE. 20 | -------------------------------------------------------------------------------- /Pods/Alamofire/Source/Alamofire.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Alamofire.swift 3 | // 4 | // Copyright (c) 2014-2021 Alamofire Software Foundation (http://alamofire.org/) 5 | // 6 | // Permission is hereby granted, free of charge, to any person obtaining a copy 7 | // of this software and associated documentation files (the "Software"), to deal 8 | // in the Software without restriction, including without limitation the rights 9 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | // copies of the Software, and to permit persons to whom the Software is 11 | // furnished to do so, subject to the following conditions: 12 | // 13 | // The above copyright notice and this permission notice shall be included in 14 | // all copies or substantial portions of the Software. 15 | // 16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | // THE SOFTWARE. 23 | // 24 | 25 | import Dispatch 26 | import Foundation 27 | #if canImport(FoundationNetworking) 28 | @_exported import FoundationNetworking 29 | #endif 30 | 31 | /// Reference to `Session.default` for quick bootstrapping and examples. 32 | public let AF = Session.default 33 | 34 | /// Current Alamofire version. Necessary since SPM doesn't use dynamic libraries. Plus this will be more accurate. 35 | let version = "5.4.4" 36 | -------------------------------------------------------------------------------- /Pods/Alamofire/Source/AlamofireExtended.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AlamofireExtended.swift 3 | // 4 | // Copyright (c) 2019 Alamofire Software Foundation (http://alamofire.org/) 5 | // 6 | // Permission is hereby granted, free of charge, to any person obtaining a copy 7 | // of this software and associated documentation files (the "Software"), to deal 8 | // in the Software without restriction, including without limitation the rights 9 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | // copies of the Software, and to permit persons to whom the Software is 11 | // furnished to do so, subject to the following conditions: 12 | // 13 | // The above copyright notice and this permission notice shall be included in 14 | // all copies or substantial portions of the Software. 15 | // 16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | // THE SOFTWARE. 23 | // 24 | 25 | /// Type that acts as a generic extension point for all `AlamofireExtended` types. 26 | public struct AlamofireExtension { 27 | /// Stores the type or meta-type of any extended type. 28 | public private(set) var type: ExtendedType 29 | 30 | /// Create an instance from the provided value. 31 | /// 32 | /// - Parameter type: Instance being extended. 33 | public init(_ type: ExtendedType) { 34 | self.type = type 35 | } 36 | } 37 | 38 | /// Protocol describing the `af` extension points for Alamofire extended types. 39 | public protocol AlamofireExtended { 40 | /// Type being extended. 41 | associatedtype ExtendedType 42 | 43 | /// Static Alamofire extension point. 44 | static var af: AlamofireExtension.Type { get set } 45 | /// Instance Alamofire extension point. 46 | var af: AlamofireExtension { get set } 47 | } 48 | 49 | extension AlamofireExtended { 50 | /// Static Alamofire extension point. 51 | public static var af: AlamofireExtension.Type { 52 | get { AlamofireExtension.self } 53 | set {} 54 | } 55 | 56 | /// Instance Alamofire extension point. 57 | public var af: AlamofireExtension { 58 | get { AlamofireExtension(self) } 59 | set {} 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /Pods/Alamofire/Source/CachedResponseHandler.swift: -------------------------------------------------------------------------------- 1 | // 2 | // CachedResponseHandler.swift 3 | // 4 | // Copyright (c) 2019 Alamofire Software Foundation (http://alamofire.org/) 5 | // 6 | // Permission is hereby granted, free of charge, to any person obtaining a copy 7 | // of this software and associated documentation files (the "Software"), to deal 8 | // in the Software without restriction, including without limitation the rights 9 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | // copies of the Software, and to permit persons to whom the Software is 11 | // furnished to do so, subject to the following conditions: 12 | // 13 | // The above copyright notice and this permission notice shall be included in 14 | // all copies or substantial portions of the Software. 15 | // 16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | // THE SOFTWARE. 23 | // 24 | 25 | import Foundation 26 | 27 | /// A type that handles whether the data task should store the HTTP response in the cache. 28 | public protocol CachedResponseHandler { 29 | /// Determines whether the HTTP response should be stored in the cache. 30 | /// 31 | /// The `completion` closure should be passed one of three possible options: 32 | /// 33 | /// 1. The cached response provided by the server (this is the most common use case). 34 | /// 2. A modified version of the cached response (you may want to modify it in some way before caching). 35 | /// 3. A `nil` value to prevent the cached response from being stored in the cache. 36 | /// 37 | /// - Parameters: 38 | /// - task: The data task whose request resulted in the cached response. 39 | /// - response: The cached response to potentially store in the cache. 40 | /// - completion: The closure to execute containing cached response, a modified response, or `nil`. 41 | func dataTask(_ task: URLSessionDataTask, 42 | willCacheResponse response: CachedURLResponse, 43 | completion: @escaping (CachedURLResponse?) -> Void) 44 | } 45 | 46 | // MARK: - 47 | 48 | /// `ResponseCacher` is a convenience `CachedResponseHandler` making it easy to cache, not cache, or modify a cached 49 | /// response. 50 | public struct ResponseCacher { 51 | /// Defines the behavior of the `ResponseCacher` type. 52 | public enum Behavior { 53 | /// Stores the cached response in the cache. 54 | case cache 55 | /// Prevents the cached response from being stored in the cache. 56 | case doNotCache 57 | /// Modifies the cached response before storing it in the cache. 58 | case modify((URLSessionDataTask, CachedURLResponse) -> CachedURLResponse?) 59 | } 60 | 61 | /// Returns a `ResponseCacher` with a `.cache` `Behavior`. 62 | public static let cache = ResponseCacher(behavior: .cache) 63 | /// Returns a `ResponseCacher` with a `.doNotCache` `Behavior`. 64 | public static let doNotCache = ResponseCacher(behavior: .doNotCache) 65 | 66 | /// The `Behavior` of the `ResponseCacher`. 67 | public let behavior: Behavior 68 | 69 | /// Creates a `ResponseCacher` instance from the `Behavior`. 70 | /// 71 | /// - Parameter behavior: The `Behavior`. 72 | public init(behavior: Behavior) { 73 | self.behavior = behavior 74 | } 75 | } 76 | 77 | extension ResponseCacher: CachedResponseHandler { 78 | public func dataTask(_ task: URLSessionDataTask, 79 | willCacheResponse response: CachedURLResponse, 80 | completion: @escaping (CachedURLResponse?) -> Void) { 81 | switch behavior { 82 | case .cache: 83 | completion(response) 84 | case .doNotCache: 85 | completion(nil) 86 | case let .modify(closure): 87 | let response = closure(task, response) 88 | completion(response) 89 | } 90 | } 91 | } 92 | -------------------------------------------------------------------------------- /Pods/Alamofire/Source/DispatchQueue+Alamofire.swift: -------------------------------------------------------------------------------- 1 | // 2 | // DispatchQueue+Alamofire.swift 3 | // 4 | // Copyright (c) 2014-2018 Alamofire Software Foundation (http://alamofire.org/) 5 | // 6 | // Permission is hereby granted, free of charge, to any person obtaining a copy 7 | // of this software and associated documentation files (the "Software"), to deal 8 | // in the Software without restriction, including without limitation the rights 9 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | // copies of the Software, and to permit persons to whom the Software is 11 | // furnished to do so, subject to the following conditions: 12 | // 13 | // The above copyright notice and this permission notice shall be included in 14 | // all copies or substantial portions of the Software. 15 | // 16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | // THE SOFTWARE. 23 | // 24 | 25 | import Dispatch 26 | import Foundation 27 | 28 | extension DispatchQueue { 29 | /// Execute the provided closure after a `TimeInterval`. 30 | /// 31 | /// - Parameters: 32 | /// - delay: `TimeInterval` to delay execution. 33 | /// - closure: Closure to execute. 34 | func after(_ delay: TimeInterval, execute closure: @escaping () -> Void) { 35 | asyncAfter(deadline: .now() + delay, execute: closure) 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /Pods/Alamofire/Source/HTTPMethod.swift: -------------------------------------------------------------------------------- 1 | // 2 | // HTTPMethod.swift 3 | // 4 | // Copyright (c) 2014-2018 Alamofire Software Foundation (http://alamofire.org/) 5 | // 6 | // Permission is hereby granted, free of charge, to any person obtaining a copy 7 | // of this software and associated documentation files (the "Software"), to deal 8 | // in the Software without restriction, including without limitation the rights 9 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | // copies of the Software, and to permit persons to whom the Software is 11 | // furnished to do so, subject to the following conditions: 12 | // 13 | // The above copyright notice and this permission notice shall be included in 14 | // all copies or substantial portions of the Software. 15 | // 16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | // THE SOFTWARE. 23 | // 24 | 25 | /// Type representing HTTP methods. Raw `String` value is stored and compared case-sensitively, so 26 | /// `HTTPMethod.get != HTTPMethod(rawValue: "get")`. 27 | /// 28 | /// See https://tools.ietf.org/html/rfc7231#section-4.3 29 | public struct HTTPMethod: RawRepresentable, Equatable, Hashable { 30 | /// `CONNECT` method. 31 | public static let connect = HTTPMethod(rawValue: "CONNECT") 32 | /// `DELETE` method. 33 | public static let delete = HTTPMethod(rawValue: "DELETE") 34 | /// `GET` method. 35 | public static let get = HTTPMethod(rawValue: "GET") 36 | /// `HEAD` method. 37 | public static let head = HTTPMethod(rawValue: "HEAD") 38 | /// `OPTIONS` method. 39 | public static let options = HTTPMethod(rawValue: "OPTIONS") 40 | /// `PATCH` method. 41 | public static let patch = HTTPMethod(rawValue: "PATCH") 42 | /// `POST` method. 43 | public static let post = HTTPMethod(rawValue: "POST") 44 | /// `PUT` method. 45 | public static let put = HTTPMethod(rawValue: "PUT") 46 | /// `TRACE` method. 47 | public static let trace = HTTPMethod(rawValue: "TRACE") 48 | 49 | public let rawValue: String 50 | 51 | public init(rawValue: String) { 52 | self.rawValue = rawValue 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /Pods/Alamofire/Source/MultipartUpload.swift: -------------------------------------------------------------------------------- 1 | // 2 | // MultipartUpload.swift 3 | // 4 | // Copyright (c) 2014-2018 Alamofire Software Foundation (http://alamofire.org/) 5 | // 6 | // Permission is hereby granted, free of charge, to any person obtaining a copy 7 | // of this software and associated documentation files (the "Software"), to deal 8 | // in the Software without restriction, including without limitation the rights 9 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | // copies of the Software, and to permit persons to whom the Software is 11 | // furnished to do so, subject to the following conditions: 12 | // 13 | // The above copyright notice and this permission notice shall be included in 14 | // all copies or substantial portions of the Software. 15 | // 16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | // THE SOFTWARE. 23 | // 24 | 25 | import Foundation 26 | 27 | /// Internal type which encapsulates a `MultipartFormData` upload. 28 | final class MultipartUpload { 29 | lazy var result = Result { try build() } 30 | 31 | @Protected 32 | private(set) var multipartFormData: MultipartFormData 33 | let encodingMemoryThreshold: UInt64 34 | let request: URLRequestConvertible 35 | let fileManager: FileManager 36 | 37 | init(encodingMemoryThreshold: UInt64, 38 | request: URLRequestConvertible, 39 | multipartFormData: MultipartFormData) { 40 | self.encodingMemoryThreshold = encodingMemoryThreshold 41 | self.request = request 42 | fileManager = multipartFormData.fileManager 43 | self.multipartFormData = multipartFormData 44 | } 45 | 46 | func build() throws -> UploadRequest.Uploadable { 47 | let uploadable: UploadRequest.Uploadable 48 | if multipartFormData.contentLength < encodingMemoryThreshold { 49 | let data = try multipartFormData.encode() 50 | 51 | uploadable = .data(data) 52 | } else { 53 | let tempDirectoryURL = fileManager.temporaryDirectory 54 | let directoryURL = tempDirectoryURL.appendingPathComponent("org.alamofire.manager/multipart.form.data") 55 | let fileName = UUID().uuidString 56 | let fileURL = directoryURL.appendingPathComponent(fileName) 57 | 58 | try fileManager.createDirectory(at: directoryURL, withIntermediateDirectories: true, attributes: nil) 59 | 60 | do { 61 | try multipartFormData.writeEncodedData(to: fileURL) 62 | } catch { 63 | // Cleanup after attempted write if it fails. 64 | try? fileManager.removeItem(at: fileURL) 65 | throw error 66 | } 67 | 68 | uploadable = .file(fileURL, shouldRemove: true) 69 | } 70 | 71 | return uploadable 72 | } 73 | } 74 | 75 | extension MultipartUpload: UploadConvertible { 76 | func asURLRequest() throws -> URLRequest { 77 | var urlRequest = try request.asURLRequest() 78 | 79 | $multipartFormData.read { multipartFormData in 80 | urlRequest.headers.add(.contentType(multipartFormData.contentType)) 81 | } 82 | 83 | return urlRequest 84 | } 85 | 86 | func createUploadable() throws -> UploadRequest.Uploadable { 87 | try result.get() 88 | } 89 | } 90 | -------------------------------------------------------------------------------- /Pods/Alamofire/Source/Notifications.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Notifications.swift 3 | // 4 | // Copyright (c) 2014-2018 Alamofire Software Foundation (http://alamofire.org/) 5 | // 6 | // Permission is hereby granted, free of charge, to any person obtaining a copy 7 | // of this software and associated documentation files (the "Software"), to deal 8 | // in the Software without restriction, including without limitation the rights 9 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | // copies of the Software, and to permit persons to whom the Software is 11 | // furnished to do so, subject to the following conditions: 12 | // 13 | // The above copyright notice and this permission notice shall be included in 14 | // all copies or substantial portions of the Software. 15 | // 16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | // THE SOFTWARE. 23 | // 24 | 25 | import Foundation 26 | 27 | extension Request { 28 | /// Posted when a `Request` is resumed. The `Notification` contains the resumed `Request`. 29 | public static let didResumeNotification = Notification.Name(rawValue: "org.alamofire.notification.name.request.didResume") 30 | /// Posted when a `Request` is suspended. The `Notification` contains the suspended `Request`. 31 | public static let didSuspendNotification = Notification.Name(rawValue: "org.alamofire.notification.name.request.didSuspend") 32 | /// Posted when a `Request` is cancelled. The `Notification` contains the cancelled `Request`. 33 | public static let didCancelNotification = Notification.Name(rawValue: "org.alamofire.notification.name.request.didCancel") 34 | /// Posted when a `Request` is finished. The `Notification` contains the completed `Request`. 35 | public static let didFinishNotification = Notification.Name(rawValue: "org.alamofire.notification.name.request.didFinish") 36 | 37 | /// Posted when a `URLSessionTask` is resumed. The `Notification` contains the `Request` associated with the `URLSessionTask`. 38 | public static let didResumeTaskNotification = Notification.Name(rawValue: "org.alamofire.notification.name.request.didResumeTask") 39 | /// Posted when a `URLSessionTask` is suspended. The `Notification` contains the `Request` associated with the `URLSessionTask`. 40 | public static let didSuspendTaskNotification = Notification.Name(rawValue: "org.alamofire.notification.name.request.didSuspendTask") 41 | /// Posted when a `URLSessionTask` is cancelled. The `Notification` contains the `Request` associated with the `URLSessionTask`. 42 | public static let didCancelTaskNotification = Notification.Name(rawValue: "org.alamofire.notification.name.request.didCancelTask") 43 | /// Posted when a `URLSessionTask` is completed. The `Notification` contains the `Request` associated with the `URLSessionTask`. 44 | public static let didCompleteTaskNotification = Notification.Name(rawValue: "org.alamofire.notification.name.request.didCompleteTask") 45 | } 46 | 47 | // MARK: - 48 | 49 | extension Notification { 50 | /// The `Request` contained by the instance's `userInfo`, `nil` otherwise. 51 | public var request: Request? { 52 | userInfo?[String.requestKey] as? Request 53 | } 54 | 55 | /// Convenience initializer for a `Notification` containing a `Request` payload. 56 | /// 57 | /// - Parameters: 58 | /// - name: The name of the notification. 59 | /// - request: The `Request` payload. 60 | init(name: Notification.Name, request: Request) { 61 | self.init(name: name, object: nil, userInfo: [String.requestKey: request]) 62 | } 63 | } 64 | 65 | extension NotificationCenter { 66 | /// Convenience function for posting notifications with `Request` payloads. 67 | /// 68 | /// - Parameters: 69 | /// - name: The name of the notification. 70 | /// - request: The `Request` payload. 71 | func postNotification(named name: Notification.Name, with request: Request) { 72 | let notification = Notification(name: name, request: request) 73 | post(notification) 74 | } 75 | } 76 | 77 | extension String { 78 | /// User info dictionary key representing the `Request` associated with the notification. 79 | fileprivate static let requestKey = "org.alamofire.notification.key.request" 80 | } 81 | 82 | /// `EventMonitor` that provides Alamofire's notifications. 83 | public final class AlamofireNotifications: EventMonitor { 84 | public func requestDidResume(_ request: Request) { 85 | NotificationCenter.default.postNotification(named: Request.didResumeNotification, with: request) 86 | } 87 | 88 | public func requestDidSuspend(_ request: Request) { 89 | NotificationCenter.default.postNotification(named: Request.didSuspendNotification, with: request) 90 | } 91 | 92 | public func requestDidCancel(_ request: Request) { 93 | NotificationCenter.default.postNotification(named: Request.didCancelNotification, with: request) 94 | } 95 | 96 | public func requestDidFinish(_ request: Request) { 97 | NotificationCenter.default.postNotification(named: Request.didFinishNotification, with: request) 98 | } 99 | 100 | public func request(_ request: Request, didResumeTask task: URLSessionTask) { 101 | NotificationCenter.default.postNotification(named: Request.didResumeTaskNotification, with: request) 102 | } 103 | 104 | public func request(_ request: Request, didSuspendTask task: URLSessionTask) { 105 | NotificationCenter.default.postNotification(named: Request.didSuspendTaskNotification, with: request) 106 | } 107 | 108 | public func request(_ request: Request, didCancelTask task: URLSessionTask) { 109 | NotificationCenter.default.postNotification(named: Request.didCancelTaskNotification, with: request) 110 | } 111 | 112 | public func request(_ request: Request, didCompleteTask task: URLSessionTask, with error: AFError?) { 113 | NotificationCenter.default.postNotification(named: Request.didCompleteTaskNotification, with: request) 114 | } 115 | } 116 | -------------------------------------------------------------------------------- /Pods/Alamofire/Source/OperationQueue+Alamofire.swift: -------------------------------------------------------------------------------- 1 | // 2 | // OperationQueue+Alamofire.swift 3 | // 4 | // Copyright (c) 2014-2018 Alamofire Software Foundation (http://alamofire.org/) 5 | // 6 | // Permission is hereby granted, free of charge, to any person obtaining a copy 7 | // of this software and associated documentation files (the "Software"), to deal 8 | // in the Software without restriction, including without limitation the rights 9 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | // copies of the Software, and to permit persons to whom the Software is 11 | // furnished to do so, subject to the following conditions: 12 | // 13 | // The above copyright notice and this permission notice shall be included in 14 | // all copies or substantial portions of the Software. 15 | // 16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | // THE SOFTWARE. 23 | // 24 | 25 | import Foundation 26 | 27 | extension OperationQueue { 28 | /// Creates an instance using the provided parameters. 29 | /// 30 | /// - Parameters: 31 | /// - qualityOfService: `QualityOfService` to be applied to the queue. `.default` by default. 32 | /// - maxConcurrentOperationCount: Maximum concurrent operations. 33 | /// `OperationQueue.defaultMaxConcurrentOperationCount` by default. 34 | /// - underlyingQueue: Underlying `DispatchQueue`. `nil` by default. 35 | /// - name: Name for the queue. `nil` by default. 36 | /// - startSuspended: Whether the queue starts suspended. `false` by default. 37 | convenience init(qualityOfService: QualityOfService = .default, 38 | maxConcurrentOperationCount: Int = OperationQueue.defaultMaxConcurrentOperationCount, 39 | underlyingQueue: DispatchQueue? = nil, 40 | name: String? = nil, 41 | startSuspended: Bool = false) { 42 | self.init() 43 | self.qualityOfService = qualityOfService 44 | self.maxConcurrentOperationCount = maxConcurrentOperationCount 45 | self.underlyingQueue = underlyingQueue 46 | self.name = name 47 | isSuspended = startSuspended 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /Pods/Alamofire/Source/ParameterEncoder.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ParameterEncoder.swift 3 | // 4 | // Copyright (c) 2014-2018 Alamofire Software Foundation (http://alamofire.org/) 5 | // 6 | // Permission is hereby granted, free of charge, to any person obtaining a copy 7 | // of this software and associated documentation files (the "Software"), to deal 8 | // in the Software without restriction, including without limitation the rights 9 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | // copies of the Software, and to permit persons to whom the Software is 11 | // furnished to do so, subject to the following conditions: 12 | // 13 | // The above copyright notice and this permission notice shall be included in 14 | // all copies or substantial portions of the Software. 15 | // 16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | // THE SOFTWARE. 23 | // 24 | 25 | import Foundation 26 | 27 | /// A type that can encode any `Encodable` type into a `URLRequest`. 28 | public protocol ParameterEncoder { 29 | /// Encode the provided `Encodable` parameters into `request`. 30 | /// 31 | /// - Parameters: 32 | /// - parameters: The `Encodable` parameter value. 33 | /// - request: The `URLRequest` into which to encode the parameters. 34 | /// 35 | /// - Returns: A `URLRequest` with the result of the encoding. 36 | /// - Throws: An `Error` when encoding fails. For Alamofire provided encoders, this will be an instance of 37 | /// `AFError.parameterEncoderFailed` with an associated `ParameterEncoderFailureReason`. 38 | func encode(_ parameters: Parameters?, into request: URLRequest) throws -> URLRequest 39 | } 40 | 41 | /// A `ParameterEncoder` that encodes types as JSON body data. 42 | /// 43 | /// If no `Content-Type` header is already set on the provided `URLRequest`s, it's set to `application/json`. 44 | open class JSONParameterEncoder: ParameterEncoder { 45 | /// Returns an encoder with default parameters. 46 | public static var `default`: JSONParameterEncoder { JSONParameterEncoder() } 47 | 48 | /// Returns an encoder with `JSONEncoder.outputFormatting` set to `.prettyPrinted`. 49 | public static var prettyPrinted: JSONParameterEncoder { 50 | let encoder = JSONEncoder() 51 | encoder.outputFormatting = .prettyPrinted 52 | 53 | return JSONParameterEncoder(encoder: encoder) 54 | } 55 | 56 | /// Returns an encoder with `JSONEncoder.outputFormatting` set to `.sortedKeys`. 57 | @available(macOS 10.13, iOS 11.0, tvOS 11.0, watchOS 4.0, *) 58 | public static var sortedKeys: JSONParameterEncoder { 59 | let encoder = JSONEncoder() 60 | encoder.outputFormatting = .sortedKeys 61 | 62 | return JSONParameterEncoder(encoder: encoder) 63 | } 64 | 65 | /// `JSONEncoder` used to encode parameters. 66 | public let encoder: JSONEncoder 67 | 68 | /// Creates an instance with the provided `JSONEncoder`. 69 | /// 70 | /// - Parameter encoder: The `JSONEncoder`. `JSONEncoder()` by default. 71 | public init(encoder: JSONEncoder = JSONEncoder()) { 72 | self.encoder = encoder 73 | } 74 | 75 | open func encode(_ parameters: Parameters?, 76 | into request: URLRequest) throws -> URLRequest { 77 | guard let parameters = parameters else { return request } 78 | 79 | var request = request 80 | 81 | do { 82 | let data = try encoder.encode(parameters) 83 | request.httpBody = data 84 | if request.headers["Content-Type"] == nil { 85 | request.headers.update(.contentType("application/json")) 86 | } 87 | } catch { 88 | throw AFError.parameterEncodingFailed(reason: .jsonEncodingFailed(error: error)) 89 | } 90 | 91 | return request 92 | } 93 | } 94 | 95 | /// A `ParameterEncoder` that encodes types as URL-encoded query strings to be set on the URL or as body data, depending 96 | /// on the `Destination` set. 97 | /// 98 | /// If no `Content-Type` header is already set on the provided `URLRequest`s, it will be set to 99 | /// `application/x-www-form-urlencoded; charset=utf-8`. 100 | /// 101 | /// Encoding behavior can be customized by passing an instance of `URLEncodedFormEncoder` to the initializer. 102 | open class URLEncodedFormParameterEncoder: ParameterEncoder { 103 | /// Defines where the URL-encoded string should be set for each `URLRequest`. 104 | public enum Destination { 105 | /// Applies the encoded query string to any existing query string for `.get`, `.head`, and `.delete` request. 106 | /// Sets it to the `httpBody` for all other methods. 107 | case methodDependent 108 | /// Applies the encoded query string to any existing query string from the `URLRequest`. 109 | case queryString 110 | /// Applies the encoded query string to the `httpBody` of the `URLRequest`. 111 | case httpBody 112 | 113 | /// Determines whether the URL-encoded string should be applied to the `URLRequest`'s `url`. 114 | /// 115 | /// - Parameter method: The `HTTPMethod`. 116 | /// 117 | /// - Returns: Whether the URL-encoded string should be applied to a `URL`. 118 | func encodesParametersInURL(for method: HTTPMethod) -> Bool { 119 | switch self { 120 | case .methodDependent: return [.get, .head, .delete].contains(method) 121 | case .queryString: return true 122 | case .httpBody: return false 123 | } 124 | } 125 | } 126 | 127 | /// Returns an encoder with default parameters. 128 | public static var `default`: URLEncodedFormParameterEncoder { URLEncodedFormParameterEncoder() } 129 | 130 | /// The `URLEncodedFormEncoder` to use. 131 | public let encoder: URLEncodedFormEncoder 132 | 133 | /// The `Destination` for the URL-encoded string. 134 | public let destination: Destination 135 | 136 | /// Creates an instance with the provided `URLEncodedFormEncoder` instance and `Destination` value. 137 | /// 138 | /// - Parameters: 139 | /// - encoder: The `URLEncodedFormEncoder`. `URLEncodedFormEncoder()` by default. 140 | /// - destination: The `Destination`. `.methodDependent` by default. 141 | public init(encoder: URLEncodedFormEncoder = URLEncodedFormEncoder(), destination: Destination = .methodDependent) { 142 | self.encoder = encoder 143 | self.destination = destination 144 | } 145 | 146 | open func encode(_ parameters: Parameters?, 147 | into request: URLRequest) throws -> URLRequest { 148 | guard let parameters = parameters else { return request } 149 | 150 | var request = request 151 | 152 | guard let url = request.url else { 153 | throw AFError.parameterEncoderFailed(reason: .missingRequiredComponent(.url)) 154 | } 155 | 156 | guard let method = request.method else { 157 | let rawValue = request.method?.rawValue ?? "nil" 158 | throw AFError.parameterEncoderFailed(reason: .missingRequiredComponent(.httpMethod(rawValue: rawValue))) 159 | } 160 | 161 | if destination.encodesParametersInURL(for: method), 162 | var components = URLComponents(url: url, resolvingAgainstBaseURL: false) { 163 | let query: String = try Result { try encoder.encode(parameters) } 164 | .mapError { AFError.parameterEncoderFailed(reason: .encoderFailed(error: $0)) }.get() 165 | let newQueryString = [components.percentEncodedQuery, query].compactMap { $0 }.joinedWithAmpersands() 166 | components.percentEncodedQuery = newQueryString.isEmpty ? nil : newQueryString 167 | 168 | guard let newURL = components.url else { 169 | throw AFError.parameterEncoderFailed(reason: .missingRequiredComponent(.url)) 170 | } 171 | 172 | request.url = newURL 173 | } else { 174 | if request.headers["Content-Type"] == nil { 175 | request.headers.update(.contentType("application/x-www-form-urlencoded; charset=utf-8")) 176 | } 177 | 178 | request.httpBody = try Result { try encoder.encode(parameters) } 179 | .mapError { AFError.parameterEncoderFailed(reason: .encoderFailed(error: $0)) }.get() 180 | } 181 | 182 | return request 183 | } 184 | } 185 | -------------------------------------------------------------------------------- /Pods/Alamofire/Source/Protected.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Protected.swift 3 | // 4 | // Copyright (c) 2014-2020 Alamofire Software Foundation (http://alamofire.org/) 5 | // 6 | // Permission is hereby granted, free of charge, to any person obtaining a copy 7 | // of this software and associated documentation files (the "Software"), to deal 8 | // in the Software without restriction, including without limitation the rights 9 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | // copies of the Software, and to permit persons to whom the Software is 11 | // furnished to do so, subject to the following conditions: 12 | // 13 | // The above copyright notice and this permission notice shall be included in 14 | // all copies or substantial portions of the Software. 15 | // 16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | // THE SOFTWARE. 23 | // 24 | 25 | import Foundation 26 | 27 | private protocol Lock { 28 | func lock() 29 | func unlock() 30 | } 31 | 32 | extension Lock { 33 | /// Executes a closure returning a value while acquiring the lock. 34 | /// 35 | /// - Parameter closure: The closure to run. 36 | /// 37 | /// - Returns: The value the closure generated. 38 | func around(_ closure: () -> T) -> T { 39 | lock(); defer { unlock() } 40 | return closure() 41 | } 42 | 43 | /// Execute a closure while acquiring the lock. 44 | /// 45 | /// - Parameter closure: The closure to run. 46 | func around(_ closure: () -> Void) { 47 | lock(); defer { unlock() } 48 | closure() 49 | } 50 | } 51 | 52 | #if os(Linux) || os(Windows) 53 | 54 | extension NSLock: Lock {} 55 | 56 | #endif 57 | 58 | #if os(macOS) || os(iOS) || os(watchOS) || os(tvOS) 59 | /// An `os_unfair_lock` wrapper. 60 | final class UnfairLock: Lock { 61 | private let unfairLock: os_unfair_lock_t 62 | 63 | init() { 64 | unfairLock = .allocate(capacity: 1) 65 | unfairLock.initialize(to: os_unfair_lock()) 66 | } 67 | 68 | deinit { 69 | unfairLock.deinitialize(count: 1) 70 | unfairLock.deallocate() 71 | } 72 | 73 | fileprivate func lock() { 74 | os_unfair_lock_lock(unfairLock) 75 | } 76 | 77 | fileprivate func unlock() { 78 | os_unfair_lock_unlock(unfairLock) 79 | } 80 | } 81 | #endif 82 | 83 | /// A thread-safe wrapper around a value. 84 | @propertyWrapper 85 | @dynamicMemberLookup 86 | final class Protected { 87 | #if os(macOS) || os(iOS) || os(watchOS) || os(tvOS) 88 | private let lock = UnfairLock() 89 | #elseif os(Linux) || os(Windows) 90 | private let lock = NSLock() 91 | #endif 92 | private var value: T 93 | 94 | init(_ value: T) { 95 | self.value = value 96 | } 97 | 98 | /// The contained value. Unsafe for anything more than direct read or write. 99 | var wrappedValue: T { 100 | get { lock.around { value } } 101 | set { lock.around { value = newValue } } 102 | } 103 | 104 | var projectedValue: Protected { self } 105 | 106 | init(wrappedValue: T) { 107 | value = wrappedValue 108 | } 109 | 110 | /// Synchronously read or transform the contained value. 111 | /// 112 | /// - Parameter closure: The closure to execute. 113 | /// 114 | /// - Returns: The return value of the closure passed. 115 | func read(_ closure: (T) -> U) -> U { 116 | lock.around { closure(self.value) } 117 | } 118 | 119 | /// Synchronously modify the protected value. 120 | /// 121 | /// - Parameter closure: The closure to execute. 122 | /// 123 | /// - Returns: The modified value. 124 | @discardableResult 125 | func write(_ closure: (inout T) -> U) -> U { 126 | lock.around { closure(&self.value) } 127 | } 128 | 129 | subscript(dynamicMember keyPath: WritableKeyPath) -> Property { 130 | get { lock.around { value[keyPath: keyPath] } } 131 | set { lock.around { value[keyPath: keyPath] = newValue } } 132 | } 133 | } 134 | 135 | extension Protected where T: RangeReplaceableCollection { 136 | /// Adds a new element to the end of this protected collection. 137 | /// 138 | /// - Parameter newElement: The `Element` to append. 139 | func append(_ newElement: T.Element) { 140 | write { (ward: inout T) in 141 | ward.append(newElement) 142 | } 143 | } 144 | 145 | /// Adds the elements of a sequence to the end of this protected collection. 146 | /// 147 | /// - Parameter newElements: The `Sequence` to append. 148 | func append(contentsOf newElements: S) where S.Element == T.Element { 149 | write { (ward: inout T) in 150 | ward.append(contentsOf: newElements) 151 | } 152 | } 153 | 154 | /// Add the elements of a collection to the end of the protected collection. 155 | /// 156 | /// - Parameter newElements: The `Collection` to append. 157 | func append(contentsOf newElements: C) where C.Element == T.Element { 158 | write { (ward: inout T) in 159 | ward.append(contentsOf: newElements) 160 | } 161 | } 162 | } 163 | 164 | extension Protected where T == Data? { 165 | /// Adds the contents of a `Data` value to the end of the protected `Data`. 166 | /// 167 | /// - Parameter data: The `Data` to be appended. 168 | func append(_ data: Data) { 169 | write { (ward: inout T) in 170 | ward?.append(data) 171 | } 172 | } 173 | } 174 | 175 | extension Protected where T == Request.MutableState { 176 | /// Attempts to transition to the passed `State`. 177 | /// 178 | /// - Parameter state: The `State` to attempt transition to. 179 | /// 180 | /// - Returns: Whether the transition occurred. 181 | func attemptToTransitionTo(_ state: Request.State) -> Bool { 182 | lock.around { 183 | guard value.state.canTransitionTo(state) else { return false } 184 | 185 | value.state = state 186 | 187 | return true 188 | } 189 | } 190 | 191 | /// Perform a closure while locked with the provided `Request.State`. 192 | /// 193 | /// - Parameter perform: The closure to perform while locked. 194 | func withState(perform: (Request.State) -> Void) { 195 | lock.around { perform(value.state) } 196 | } 197 | } 198 | -------------------------------------------------------------------------------- /Pods/Alamofire/Source/RedirectHandler.swift: -------------------------------------------------------------------------------- 1 | // 2 | // RedirectHandler.swift 3 | // 4 | // Copyright (c) 2014-2018 Alamofire Software Foundation (http://alamofire.org/) 5 | // 6 | // Permission is hereby granted, free of charge, to any person obtaining a copy 7 | // of this software and associated documentation files (the "Software"), to deal 8 | // in the Software without restriction, including without limitation the rights 9 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | // copies of the Software, and to permit persons to whom the Software is 11 | // furnished to do so, subject to the following conditions: 12 | // 13 | // The above copyright notice and this permission notice shall be included in 14 | // all copies or substantial portions of the Software. 15 | // 16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | // THE SOFTWARE. 23 | // 24 | 25 | import Foundation 26 | 27 | /// A type that handles how an HTTP redirect response from a remote server should be redirected to the new request. 28 | public protocol RedirectHandler { 29 | /// Determines how the HTTP redirect response should be redirected to the new request. 30 | /// 31 | /// The `completion` closure should be passed one of three possible options: 32 | /// 33 | /// 1. The new request specified by the redirect (this is the most common use case). 34 | /// 2. A modified version of the new request (you may want to route it somewhere else). 35 | /// 3. A `nil` value to deny the redirect request and return the body of the redirect response. 36 | /// 37 | /// - Parameters: 38 | /// - task: The `URLSessionTask` whose request resulted in a redirect. 39 | /// - request: The `URLRequest` to the new location specified by the redirect response. 40 | /// - response: The `HTTPURLResponse` containing the server's response to the original request. 41 | /// - completion: The closure to execute containing the new `URLRequest`, a modified `URLRequest`, or `nil`. 42 | func task(_ task: URLSessionTask, 43 | willBeRedirectedTo request: URLRequest, 44 | for response: HTTPURLResponse, 45 | completion: @escaping (URLRequest?) -> Void) 46 | } 47 | 48 | // MARK: - 49 | 50 | /// `Redirector` is a convenience `RedirectHandler` making it easy to follow, not follow, or modify a redirect. 51 | public struct Redirector { 52 | /// Defines the behavior of the `Redirector` type. 53 | public enum Behavior { 54 | /// Follow the redirect as defined in the response. 55 | case follow 56 | /// Do not follow the redirect defined in the response. 57 | case doNotFollow 58 | /// Modify the redirect request defined in the response. 59 | case modify((URLSessionTask, URLRequest, HTTPURLResponse) -> URLRequest?) 60 | } 61 | 62 | /// Returns a `Redirector` with a `.follow` `Behavior`. 63 | public static let follow = Redirector(behavior: .follow) 64 | /// Returns a `Redirector` with a `.doNotFollow` `Behavior`. 65 | public static let doNotFollow = Redirector(behavior: .doNotFollow) 66 | 67 | /// The `Behavior` of the `Redirector`. 68 | public let behavior: Behavior 69 | 70 | /// Creates a `Redirector` instance from the `Behavior`. 71 | /// 72 | /// - Parameter behavior: The `Behavior`. 73 | public init(behavior: Behavior) { 74 | self.behavior = behavior 75 | } 76 | } 77 | 78 | // MARK: - 79 | 80 | extension Redirector: RedirectHandler { 81 | public func task(_ task: URLSessionTask, 82 | willBeRedirectedTo request: URLRequest, 83 | for response: HTTPURLResponse, 84 | completion: @escaping (URLRequest?) -> Void) { 85 | switch behavior { 86 | case .follow: 87 | completion(request) 88 | case .doNotFollow: 89 | completion(nil) 90 | case let .modify(closure): 91 | let request = closure(task, request, response) 92 | completion(request) 93 | } 94 | } 95 | } 96 | -------------------------------------------------------------------------------- /Pods/Alamofire/Source/RequestTaskMap.swift: -------------------------------------------------------------------------------- 1 | // 2 | // RequestTaskMap.swift 3 | // 4 | // Copyright (c) 2014-2018 Alamofire Software Foundation (http://alamofire.org/) 5 | // 6 | // Permission is hereby granted, free of charge, to any person obtaining a copy 7 | // of this software and associated documentation files (the "Software"), to deal 8 | // in the Software without restriction, including without limitation the rights 9 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | // copies of the Software, and to permit persons to whom the Software is 11 | // furnished to do so, subject to the following conditions: 12 | // 13 | // The above copyright notice and this permission notice shall be included in 14 | // all copies or substantial portions of the Software. 15 | // 16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | // THE SOFTWARE. 23 | // 24 | 25 | import Foundation 26 | 27 | /// A type that maintains a two way, one to one map of `URLSessionTask`s to `Request`s. 28 | struct RequestTaskMap { 29 | private typealias Events = (completed: Bool, metricsGathered: Bool) 30 | 31 | private var tasksToRequests: [URLSessionTask: Request] 32 | private var requestsToTasks: [Request: URLSessionTask] 33 | private var taskEvents: [URLSessionTask: Events] 34 | 35 | var requests: [Request] { 36 | Array(tasksToRequests.values) 37 | } 38 | 39 | init(tasksToRequests: [URLSessionTask: Request] = [:], 40 | requestsToTasks: [Request: URLSessionTask] = [:], 41 | taskEvents: [URLSessionTask: (completed: Bool, metricsGathered: Bool)] = [:]) { 42 | self.tasksToRequests = tasksToRequests 43 | self.requestsToTasks = requestsToTasks 44 | self.taskEvents = taskEvents 45 | } 46 | 47 | subscript(_ request: Request) -> URLSessionTask? { 48 | get { requestsToTasks[request] } 49 | set { 50 | guard let newValue = newValue else { 51 | guard let task = requestsToTasks[request] else { 52 | fatalError("RequestTaskMap consistency error: no task corresponding to request found.") 53 | } 54 | 55 | requestsToTasks.removeValue(forKey: request) 56 | tasksToRequests.removeValue(forKey: task) 57 | taskEvents.removeValue(forKey: task) 58 | 59 | return 60 | } 61 | 62 | requestsToTasks[request] = newValue 63 | tasksToRequests[newValue] = request 64 | taskEvents[newValue] = (completed: false, metricsGathered: false) 65 | } 66 | } 67 | 68 | subscript(_ task: URLSessionTask) -> Request? { 69 | get { tasksToRequests[task] } 70 | set { 71 | guard let newValue = newValue else { 72 | guard let request = tasksToRequests[task] else { 73 | fatalError("RequestTaskMap consistency error: no request corresponding to task found.") 74 | } 75 | 76 | tasksToRequests.removeValue(forKey: task) 77 | requestsToTasks.removeValue(forKey: request) 78 | taskEvents.removeValue(forKey: task) 79 | 80 | return 81 | } 82 | 83 | tasksToRequests[task] = newValue 84 | requestsToTasks[newValue] = task 85 | taskEvents[task] = (completed: false, metricsGathered: false) 86 | } 87 | } 88 | 89 | var count: Int { 90 | precondition(tasksToRequests.count == requestsToTasks.count, 91 | "RequestTaskMap.count invalid, requests.count: \(tasksToRequests.count) != tasks.count: \(requestsToTasks.count)") 92 | 93 | return tasksToRequests.count 94 | } 95 | 96 | var eventCount: Int { 97 | precondition(taskEvents.count == count, "RequestTaskMap.eventCount invalid, count: \(count) != taskEvents.count: \(taskEvents.count)") 98 | 99 | return taskEvents.count 100 | } 101 | 102 | var isEmpty: Bool { 103 | precondition(tasksToRequests.isEmpty == requestsToTasks.isEmpty, 104 | "RequestTaskMap.isEmpty invalid, requests.isEmpty: \(tasksToRequests.isEmpty) != tasks.isEmpty: \(requestsToTasks.isEmpty)") 105 | 106 | return tasksToRequests.isEmpty 107 | } 108 | 109 | var isEventsEmpty: Bool { 110 | precondition(taskEvents.isEmpty == isEmpty, "RequestTaskMap.isEventsEmpty invalid, isEmpty: \(isEmpty) != taskEvents.isEmpty: \(taskEvents.isEmpty)") 111 | 112 | return taskEvents.isEmpty 113 | } 114 | 115 | mutating func disassociateIfNecessaryAfterGatheringMetricsForTask(_ task: URLSessionTask) -> Bool { 116 | guard let events = taskEvents[task] else { 117 | fatalError("RequestTaskMap consistency error: no events corresponding to task found.") 118 | } 119 | 120 | switch (events.completed, events.metricsGathered) { 121 | case (_, true): fatalError("RequestTaskMap consistency error: duplicate metricsGatheredForTask call.") 122 | case (false, false): taskEvents[task] = (completed: false, metricsGathered: true); return false 123 | case (true, false): self[task] = nil; return true 124 | } 125 | } 126 | 127 | mutating func disassociateIfNecessaryAfterCompletingTask(_ task: URLSessionTask) -> Bool { 128 | guard let events = taskEvents[task] else { 129 | fatalError("RequestTaskMap consistency error: no events corresponding to task found.") 130 | } 131 | 132 | switch (events.completed, events.metricsGathered) { 133 | case (true, _): fatalError("RequestTaskMap consistency error: duplicate completionReceivedForTask call.") 134 | #if os(Linux) // Linux doesn't gather metrics, so unconditionally remove the reference and return true. 135 | default: self[task] = nil; return true 136 | #else 137 | case (false, false): 138 | if #available(macOS 10.12, iOS 10, watchOS 7, tvOS 10, *) { 139 | taskEvents[task] = (completed: true, metricsGathered: false); return false 140 | } else { 141 | // watchOS < 7 doesn't gather metrics, so unconditionally remove the reference and return true. 142 | self[task] = nil; return true 143 | } 144 | case (false, true): 145 | self[task] = nil; return true 146 | #endif 147 | } 148 | } 149 | } 150 | -------------------------------------------------------------------------------- /Pods/Alamofire/Source/Result+Alamofire.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Result+Alamofire.swift 3 | // 4 | // Copyright (c) 2019 Alamofire Software Foundation (http://alamofire.org/) 5 | // 6 | // Permission is hereby granted, free of charge, to any person obtaining a copy 7 | // of this software and associated documentation files (the "Software"), to deal 8 | // in the Software without restriction, including without limitation the rights 9 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | // copies of the Software, and to permit persons to whom the Software is 11 | // furnished to do so, subject to the following conditions: 12 | // 13 | // The above copyright notice and this permission notice shall be included in 14 | // all copies or substantial portions of the Software. 15 | // 16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | // THE SOFTWARE. 23 | // 24 | 25 | import Foundation 26 | 27 | /// Default type of `Result` returned by Alamofire, with an `AFError` `Failure` type. 28 | public typealias AFResult = Result 29 | 30 | // MARK: - Internal APIs 31 | 32 | extension Result { 33 | /// Returns whether the instance is `.success`. 34 | var isSuccess: Bool { 35 | guard case .success = self else { return false } 36 | return true 37 | } 38 | 39 | /// Returns whether the instance is `.failure`. 40 | var isFailure: Bool { 41 | !isSuccess 42 | } 43 | 44 | /// Returns the associated value if the result is a success, `nil` otherwise. 45 | var success: Success? { 46 | guard case let .success(value) = self else { return nil } 47 | return value 48 | } 49 | 50 | /// Returns the associated error value if the result is a failure, `nil` otherwise. 51 | var failure: Failure? { 52 | guard case let .failure(error) = self else { return nil } 53 | return error 54 | } 55 | 56 | /// Initializes a `Result` from value or error. Returns `.failure` if the error is non-nil, `.success` otherwise. 57 | /// 58 | /// - Parameters: 59 | /// - value: A value. 60 | /// - error: An `Error`. 61 | init(value: Success, error: Failure?) { 62 | if let error = error { 63 | self = .failure(error) 64 | } else { 65 | self = .success(value) 66 | } 67 | } 68 | 69 | /// Evaluates the specified closure when the `Result` is a success, passing the unwrapped value as a parameter. 70 | /// 71 | /// Use the `tryMap` method with a closure that may throw an error. For example: 72 | /// 73 | /// let possibleData: Result = .success(Data(...)) 74 | /// let possibleObject = possibleData.tryMap { 75 | /// try JSONSerialization.jsonObject(with: $0) 76 | /// } 77 | /// 78 | /// - parameter transform: A closure that takes the success value of the instance. 79 | /// 80 | /// - returns: A `Result` containing the result of the given closure. If this instance is a failure, returns the 81 | /// same failure. 82 | func tryMap(_ transform: (Success) throws -> NewSuccess) -> Result { 83 | switch self { 84 | case let .success(value): 85 | do { 86 | return try .success(transform(value)) 87 | } catch { 88 | return .failure(error) 89 | } 90 | case let .failure(error): 91 | return .failure(error) 92 | } 93 | } 94 | 95 | /// Evaluates the specified closure when the `Result` is a failure, passing the unwrapped error as a parameter. 96 | /// 97 | /// Use the `tryMapError` function with a closure that may throw an error. For example: 98 | /// 99 | /// let possibleData: Result = .success(Data(...)) 100 | /// let possibleObject = possibleData.tryMapError { 101 | /// try someFailableFunction(taking: $0) 102 | /// } 103 | /// 104 | /// - Parameter transform: A throwing closure that takes the error of the instance. 105 | /// 106 | /// - Returns: A `Result` instance containing the result of the transform. If this instance is a success, returns 107 | /// the same success. 108 | func tryMapError(_ transform: (Failure) throws -> NewFailure) -> Result { 109 | switch self { 110 | case let .failure(error): 111 | do { 112 | return try .failure(transform(error)) 113 | } catch { 114 | return .failure(error) 115 | } 116 | case let .success(value): 117 | return .success(value) 118 | } 119 | } 120 | } 121 | -------------------------------------------------------------------------------- /Pods/Alamofire/Source/StringEncoding+Alamofire.swift: -------------------------------------------------------------------------------- 1 | // 2 | // StringEncoding+Alamofire.swift 3 | // 4 | // Copyright (c) 2020 Alamofire Software Foundation (http://alamofire.org/) 5 | // 6 | // Permission is hereby granted, free of charge, to any person obtaining a copy 7 | // of this software and associated documentation files (the "Software"), to deal 8 | // in the Software without restriction, including without limitation the rights 9 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | // copies of the Software, and to permit persons to whom the Software is 11 | // furnished to do so, subject to the following conditions: 12 | // 13 | // The above copyright notice and this permission notice shall be included in 14 | // all copies or substantial portions of the Software. 15 | // 16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | // THE SOFTWARE. 23 | // 24 | 25 | import Foundation 26 | 27 | extension String.Encoding { 28 | /// Creates an encoding from the IANA charset name. 29 | /// 30 | /// - Notes: These mappings match those [provided by CoreFoundation](https://opensource.apple.com/source/CF/CF-476.18/CFStringUtilities.c.auto.html) 31 | /// 32 | /// - Parameter name: IANA charset name. 33 | init?(ianaCharsetName name: String) { 34 | switch name.lowercased() { 35 | case "utf-8": 36 | self = .utf8 37 | case "iso-8859-1": 38 | self = .isoLatin1 39 | case "unicode-1-1", "iso-10646-ucs-2", "utf-16": 40 | self = .utf16 41 | case "utf-16be": 42 | self = .utf16BigEndian 43 | case "utf-16le": 44 | self = .utf16LittleEndian 45 | case "utf-32": 46 | self = .utf32 47 | case "utf-32be": 48 | self = .utf32BigEndian 49 | case "utf-32le": 50 | self = .utf32LittleEndian 51 | default: 52 | return nil 53 | } 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /Pods/Alamofire/Source/URLConvertible+URLRequestConvertible.swift: -------------------------------------------------------------------------------- 1 | // 2 | // URLConvertible+URLRequestConvertible.swift 3 | // 4 | // Copyright (c) 2014-2018 Alamofire Software Foundation (http://alamofire.org/) 5 | // 6 | // Permission is hereby granted, free of charge, to any person obtaining a copy 7 | // of this software and associated documentation files (the "Software"), to deal 8 | // in the Software without restriction, including without limitation the rights 9 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | // copies of the Software, and to permit persons to whom the Software is 11 | // furnished to do so, subject to the following conditions: 12 | // 13 | // The above copyright notice and this permission notice shall be included in 14 | // all copies or substantial portions of the Software. 15 | // 16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | // THE SOFTWARE. 23 | // 24 | 25 | import Foundation 26 | 27 | /// Types adopting the `URLConvertible` protocol can be used to construct `URL`s, which can then be used to construct 28 | /// `URLRequests`. 29 | public protocol URLConvertible { 30 | /// Returns a `URL` from the conforming instance or throws. 31 | /// 32 | /// - Returns: The `URL` created from the instance. 33 | /// - Throws: Any error thrown while creating the `URL`. 34 | func asURL() throws -> URL 35 | } 36 | 37 | extension String: URLConvertible { 38 | /// Returns a `URL` if `self` can be used to initialize a `URL` instance, otherwise throws. 39 | /// 40 | /// - Returns: The `URL` initialized with `self`. 41 | /// - Throws: An `AFError.invalidURL` instance. 42 | public func asURL() throws -> URL { 43 | guard let url = URL(string: self) else { throw AFError.invalidURL(url: self) } 44 | 45 | return url 46 | } 47 | } 48 | 49 | extension URL: URLConvertible { 50 | /// Returns `self`. 51 | public func asURL() throws -> URL { self } 52 | } 53 | 54 | extension URLComponents: URLConvertible { 55 | /// Returns a `URL` if the `self`'s `url` is not nil, otherwise throws. 56 | /// 57 | /// - Returns: The `URL` from the `url` property. 58 | /// - Throws: An `AFError.invalidURL` instance. 59 | public func asURL() throws -> URL { 60 | guard let url = url else { throw AFError.invalidURL(url: self) } 61 | 62 | return url 63 | } 64 | } 65 | 66 | // MARK: - 67 | 68 | /// Types adopting the `URLRequestConvertible` protocol can be used to safely construct `URLRequest`s. 69 | public protocol URLRequestConvertible { 70 | /// Returns a `URLRequest` or throws if an `Error` was encountered. 71 | /// 72 | /// - Returns: A `URLRequest`. 73 | /// - Throws: Any error thrown while constructing the `URLRequest`. 74 | func asURLRequest() throws -> URLRequest 75 | } 76 | 77 | extension URLRequestConvertible { 78 | /// The `URLRequest` returned by discarding any `Error` encountered. 79 | public var urlRequest: URLRequest? { try? asURLRequest() } 80 | } 81 | 82 | extension URLRequest: URLRequestConvertible { 83 | /// Returns `self`. 84 | public func asURLRequest() throws -> URLRequest { self } 85 | } 86 | 87 | // MARK: - 88 | 89 | extension URLRequest { 90 | /// Creates an instance with the specified `url`, `method`, and `headers`. 91 | /// 92 | /// - Parameters: 93 | /// - url: The `URLConvertible` value. 94 | /// - method: The `HTTPMethod`. 95 | /// - headers: The `HTTPHeaders`, `nil` by default. 96 | /// - Throws: Any error thrown while converting the `URLConvertible` to a `URL`. 97 | public init(url: URLConvertible, method: HTTPMethod, headers: HTTPHeaders? = nil) throws { 98 | let url = try url.asURL() 99 | 100 | self.init(url: url) 101 | 102 | httpMethod = method.rawValue 103 | allHTTPHeaderFields = headers?.dictionary 104 | } 105 | } 106 | -------------------------------------------------------------------------------- /Pods/Alamofire/Source/URLRequest+Alamofire.swift: -------------------------------------------------------------------------------- 1 | // 2 | // URLRequest+Alamofire.swift 3 | // 4 | // Copyright (c) 2019 Alamofire Software Foundation (http://alamofire.org/) 5 | // 6 | // Permission is hereby granted, free of charge, to any person obtaining a copy 7 | // of this software and associated documentation files (the "Software"), to deal 8 | // in the Software without restriction, including without limitation the rights 9 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | // copies of the Software, and to permit persons to whom the Software is 11 | // furnished to do so, subject to the following conditions: 12 | // 13 | // The above copyright notice and this permission notice shall be included in 14 | // all copies or substantial portions of the Software. 15 | // 16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | // THE SOFTWARE. 23 | // 24 | 25 | import Foundation 26 | 27 | extension URLRequest { 28 | /// Returns the `httpMethod` as Alamofire's `HTTPMethod` type. 29 | public var method: HTTPMethod? { 30 | get { httpMethod.flatMap(HTTPMethod.init) } 31 | set { httpMethod = newValue?.rawValue } 32 | } 33 | 34 | public func validate() throws { 35 | if method == .get, let bodyData = httpBody { 36 | throw AFError.urlRequestValidationFailed(reason: .bodyDataInGETRequest(bodyData)) 37 | } 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /Pods/Alamofire/Source/URLSessionConfiguration+Alamofire.swift: -------------------------------------------------------------------------------- 1 | // 2 | // URLSessionConfiguration+Alamofire.swift 3 | // 4 | // Copyright (c) 2014-2018 Alamofire Software Foundation (http://alamofire.org/) 5 | // 6 | // Permission is hereby granted, free of charge, to any person obtaining a copy 7 | // of this software and associated documentation files (the "Software"), to deal 8 | // in the Software without restriction, including without limitation the rights 9 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | // copies of the Software, and to permit persons to whom the Software is 11 | // furnished to do so, subject to the following conditions: 12 | // 13 | // The above copyright notice and this permission notice shall be included in 14 | // all copies or substantial portions of the Software. 15 | // 16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | // THE SOFTWARE. 23 | // 24 | 25 | import Foundation 26 | 27 | extension URLSessionConfiguration: AlamofireExtended {} 28 | extension AlamofireExtension where ExtendedType: URLSessionConfiguration { 29 | /// Alamofire's default configuration. Same as `URLSessionConfiguration.default` but adds Alamofire default 30 | /// `Accept-Language`, `Accept-Encoding`, and `User-Agent` headers. 31 | public static var `default`: URLSessionConfiguration { 32 | let configuration = URLSessionConfiguration.default 33 | configuration.headers = .default 34 | 35 | return configuration 36 | } 37 | 38 | /// `.ephemeral` configuration with Alamofire's default `Accept-Language`, `Accept-Encoding`, and `User-Agent` 39 | /// headers. 40 | public static var ephemeral: URLSessionConfiguration { 41 | let configuration = URLSessionConfiguration.ephemeral 42 | configuration.headers = .default 43 | 44 | return configuration 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /Pods/Manifest.lock: -------------------------------------------------------------------------------- 1 | PODS: 2 | - Alamofire (5.4.4) 3 | 4 | DEPENDENCIES: 5 | - Alamofire (~> 5.4) 6 | 7 | SPEC REPOS: 8 | trunk: 9 | - Alamofire 10 | 11 | SPEC CHECKSUMS: 12 | Alamofire: f3b09a368f1582ab751b3fff5460276e0d2cf5c9 13 | 14 | PODFILE CHECKSUM: 9c6229bd8decf88d475ec993127e0b09e0caed83 15 | 16 | COCOAPODS: 1.11.2 17 | -------------------------------------------------------------------------------- /Pods/Pods.xcodeproj/xcuserdata/dani.xcuserdatad/xcschemes/Alamofire.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 32 | 33 | 43 | 44 | 50 | 51 | 53 | 54 | 57 | 58 | 59 | -------------------------------------------------------------------------------- /Pods/Pods.xcodeproj/xcuserdata/dani.xcuserdatad/xcschemes/Pods-clean_architecture_mvvm_base-clean_architecture_mvvm_baseUITests.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 32 | 33 | 43 | 44 | 50 | 51 | 53 | 54 | 57 | 58 | 59 | -------------------------------------------------------------------------------- /Pods/Pods.xcodeproj/xcuserdata/dani.xcuserdatad/xcschemes/Pods-clean_architecture_mvvm_base.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 32 | 33 | 43 | 44 | 50 | 51 | 53 | 54 | 57 | 58 | 59 | -------------------------------------------------------------------------------- /Pods/Pods.xcodeproj/xcuserdata/dani.xcuserdatad/xcschemes/Pods-clean_architecture_mvvm_baseTests.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 32 | 33 | 43 | 44 | 50 | 51 | 53 | 54 | 57 | 58 | 59 | -------------------------------------------------------------------------------- /Pods/Pods.xcodeproj/xcuserdata/dani.xcuserdatad/xcschemes/xcschememanagement.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | SchemeUserState 6 | 7 | Alamofire.xcscheme 8 | 9 | isShown 10 | 11 | orderHint 12 | 0 13 | 14 | Pods-clean_architecture_mvvm_base-clean_architecture_mvvm_baseUITests.xcscheme 15 | 16 | isShown 17 | 18 | orderHint 19 | 2 20 | 21 | Pods-clean_architecture_mvvm_base.xcscheme 22 | 23 | isShown 24 | 25 | orderHint 26 | 1 27 | 28 | Pods-clean_architecture_mvvm_baseTests.xcscheme 29 | 30 | isShown 31 | 32 | orderHint 33 | 3 34 | 35 | 36 | SuppressBuildableAutocreation 37 | 38 | 39 | 40 | -------------------------------------------------------------------------------- /Pods/Target Support Files/Alamofire/Alamofire-Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | ${EXECUTABLE_NAME} 9 | CFBundleIdentifier 10 | ${PRODUCT_BUNDLE_IDENTIFIER} 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | ${PRODUCT_NAME} 15 | CFBundlePackageType 16 | FMWK 17 | CFBundleShortVersionString 18 | 5.4.4 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | ${CURRENT_PROJECT_VERSION} 23 | NSPrincipalClass 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /Pods/Target Support Files/Alamofire/Alamofire-dummy.m: -------------------------------------------------------------------------------- 1 | #import 2 | @interface PodsDummy_Alamofire : NSObject 3 | @end 4 | @implementation PodsDummy_Alamofire 5 | @end 6 | -------------------------------------------------------------------------------- /Pods/Target Support Files/Alamofire/Alamofire-prefix.pch: -------------------------------------------------------------------------------- 1 | #ifdef __OBJC__ 2 | #import 3 | #else 4 | #ifndef FOUNDATION_EXPORT 5 | #if defined(__cplusplus) 6 | #define FOUNDATION_EXPORT extern "C" 7 | #else 8 | #define FOUNDATION_EXPORT extern 9 | #endif 10 | #endif 11 | #endif 12 | 13 | -------------------------------------------------------------------------------- /Pods/Target Support Files/Alamofire/Alamofire-umbrella.h: -------------------------------------------------------------------------------- 1 | #ifdef __OBJC__ 2 | #import 3 | #else 4 | #ifndef FOUNDATION_EXPORT 5 | #if defined(__cplusplus) 6 | #define FOUNDATION_EXPORT extern "C" 7 | #else 8 | #define FOUNDATION_EXPORT extern 9 | #endif 10 | #endif 11 | #endif 12 | 13 | 14 | FOUNDATION_EXPORT double AlamofireVersionNumber; 15 | FOUNDATION_EXPORT const unsigned char AlamofireVersionString[]; 16 | 17 | -------------------------------------------------------------------------------- /Pods/Target Support Files/Alamofire/Alamofire.debug.xcconfig: -------------------------------------------------------------------------------- 1 | CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = NO 2 | CONFIGURATION_BUILD_DIR = ${PODS_CONFIGURATION_BUILD_DIR}/Alamofire 3 | GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 4 | LIBRARY_SEARCH_PATHS = $(inherited) "${DT_TOOLCHAIN_DIR}/usr/lib/swift/${PLATFORM_NAME}" /usr/lib/swift 5 | OTHER_LDFLAGS = $(inherited) -framework "CFNetwork" 6 | OTHER_SWIFT_FLAGS = $(inherited) -D COCOAPODS 7 | PODS_BUILD_DIR = ${BUILD_DIR} 8 | PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME) 9 | PODS_ROOT = ${SRCROOT} 10 | PODS_TARGET_SRCROOT = ${PODS_ROOT}/Alamofire 11 | PODS_XCFRAMEWORKS_BUILD_DIR = $(PODS_CONFIGURATION_BUILD_DIR)/XCFrameworkIntermediates 12 | PRODUCT_BUNDLE_IDENTIFIER = org.cocoapods.${PRODUCT_NAME:rfc1034identifier} 13 | SKIP_INSTALL = YES 14 | USE_RECURSIVE_SCRIPT_INPUTS_IN_SCRIPT_PHASES = YES 15 | -------------------------------------------------------------------------------- /Pods/Target Support Files/Alamofire/Alamofire.modulemap: -------------------------------------------------------------------------------- 1 | framework module Alamofire { 2 | umbrella header "Alamofire-umbrella.h" 3 | 4 | export * 5 | module * { export * } 6 | } 7 | -------------------------------------------------------------------------------- /Pods/Target Support Files/Alamofire/Alamofire.release.xcconfig: -------------------------------------------------------------------------------- 1 | CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = NO 2 | CONFIGURATION_BUILD_DIR = ${PODS_CONFIGURATION_BUILD_DIR}/Alamofire 3 | GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 4 | LIBRARY_SEARCH_PATHS = $(inherited) "${DT_TOOLCHAIN_DIR}/usr/lib/swift/${PLATFORM_NAME}" /usr/lib/swift 5 | OTHER_LDFLAGS = $(inherited) -framework "CFNetwork" 6 | OTHER_SWIFT_FLAGS = $(inherited) -D COCOAPODS 7 | PODS_BUILD_DIR = ${BUILD_DIR} 8 | PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME) 9 | PODS_ROOT = ${SRCROOT} 10 | PODS_TARGET_SRCROOT = ${PODS_ROOT}/Alamofire 11 | PODS_XCFRAMEWORKS_BUILD_DIR = $(PODS_CONFIGURATION_BUILD_DIR)/XCFrameworkIntermediates 12 | PRODUCT_BUNDLE_IDENTIFIER = org.cocoapods.${PRODUCT_NAME:rfc1034identifier} 13 | SKIP_INSTALL = YES 14 | USE_RECURSIVE_SCRIPT_INPUTS_IN_SCRIPT_PHASES = YES 15 | -------------------------------------------------------------------------------- /Pods/Target Support Files/Pods-clean_architecture_mvvm_base-clean_architecture_mvvm_baseUITests/Pods-clean_architecture_mvvm_base-clean_architecture_mvvm_baseUITests-Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | ${EXECUTABLE_NAME} 9 | CFBundleIdentifier 10 | ${PRODUCT_BUNDLE_IDENTIFIER} 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | ${PRODUCT_NAME} 15 | CFBundlePackageType 16 | FMWK 17 | CFBundleShortVersionString 18 | 1.0.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | ${CURRENT_PROJECT_VERSION} 23 | NSPrincipalClass 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /Pods/Target Support Files/Pods-clean_architecture_mvvm_base-clean_architecture_mvvm_baseUITests/Pods-clean_architecture_mvvm_base-clean_architecture_mvvm_baseUITests-acknowledgements.markdown: -------------------------------------------------------------------------------- 1 | # Acknowledgements 2 | This application makes use of the following third party libraries: 3 | 4 | ## Alamofire 5 | 6 | Copyright (c) 2014-2021 Alamofire Software Foundation (http://alamofire.org/) 7 | 8 | Permission is hereby granted, free of charge, to any person obtaining a copy 9 | of this software and associated documentation files (the "Software"), to deal 10 | in the Software without restriction, including without limitation the rights 11 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 12 | copies of the Software, and to permit persons to whom the Software is 13 | furnished to do so, subject to the following conditions: 14 | 15 | The above copyright notice and this permission notice shall be included in 16 | all copies or substantial portions of the Software. 17 | 18 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 19 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 20 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 21 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 22 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 23 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 24 | THE SOFTWARE. 25 | 26 | Generated by CocoaPods - https://cocoapods.org 27 | -------------------------------------------------------------------------------- /Pods/Target Support Files/Pods-clean_architecture_mvvm_base-clean_architecture_mvvm_baseUITests/Pods-clean_architecture_mvvm_base-clean_architecture_mvvm_baseUITests-acknowledgements.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | PreferenceSpecifiers 6 | 7 | 8 | FooterText 9 | This application makes use of the following third party libraries: 10 | Title 11 | Acknowledgements 12 | Type 13 | PSGroupSpecifier 14 | 15 | 16 | FooterText 17 | Copyright (c) 2014-2021 Alamofire Software Foundation (http://alamofire.org/) 18 | 19 | Permission is hereby granted, free of charge, to any person obtaining a copy 20 | of this software and associated documentation files (the "Software"), to deal 21 | in the Software without restriction, including without limitation the rights 22 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 23 | copies of the Software, and to permit persons to whom the Software is 24 | furnished to do so, subject to the following conditions: 25 | 26 | The above copyright notice and this permission notice shall be included in 27 | all copies or substantial portions of the Software. 28 | 29 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 30 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 31 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 32 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 33 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 34 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 35 | THE SOFTWARE. 36 | 37 | License 38 | MIT 39 | Title 40 | Alamofire 41 | Type 42 | PSGroupSpecifier 43 | 44 | 45 | FooterText 46 | Generated by CocoaPods - https://cocoapods.org 47 | Title 48 | 49 | Type 50 | PSGroupSpecifier 51 | 52 | 53 | StringsTable 54 | Acknowledgements 55 | Title 56 | Acknowledgements 57 | 58 | 59 | -------------------------------------------------------------------------------- /Pods/Target Support Files/Pods-clean_architecture_mvvm_base-clean_architecture_mvvm_baseUITests/Pods-clean_architecture_mvvm_base-clean_architecture_mvvm_baseUITests-dummy.m: -------------------------------------------------------------------------------- 1 | #import 2 | @interface PodsDummy_Pods_clean_architecture_mvvm_base_clean_architecture_mvvm_baseUITests : NSObject 3 | @end 4 | @implementation PodsDummy_Pods_clean_architecture_mvvm_base_clean_architecture_mvvm_baseUITests 5 | @end 6 | -------------------------------------------------------------------------------- /Pods/Target Support Files/Pods-clean_architecture_mvvm_base-clean_architecture_mvvm_baseUITests/Pods-clean_architecture_mvvm_base-clean_architecture_mvvm_baseUITests-frameworks-Debug-input-files.xcfilelist: -------------------------------------------------------------------------------- 1 | ${PODS_ROOT}/Target Support Files/Pods-clean_architecture_mvvm_base-clean_architecture_mvvm_baseUITests/Pods-clean_architecture_mvvm_base-clean_architecture_mvvm_baseUITests-frameworks.sh 2 | ${BUILT_PRODUCTS_DIR}/Alamofire/Alamofire.framework -------------------------------------------------------------------------------- /Pods/Target Support Files/Pods-clean_architecture_mvvm_base-clean_architecture_mvvm_baseUITests/Pods-clean_architecture_mvvm_base-clean_architecture_mvvm_baseUITests-frameworks-Debug-output-files.xcfilelist: -------------------------------------------------------------------------------- 1 | ${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/Alamofire.framework -------------------------------------------------------------------------------- /Pods/Target Support Files/Pods-clean_architecture_mvvm_base-clean_architecture_mvvm_baseUITests/Pods-clean_architecture_mvvm_base-clean_architecture_mvvm_baseUITests-frameworks-Release-input-files.xcfilelist: -------------------------------------------------------------------------------- 1 | ${PODS_ROOT}/Target Support Files/Pods-clean_architecture_mvvm_base-clean_architecture_mvvm_baseUITests/Pods-clean_architecture_mvvm_base-clean_architecture_mvvm_baseUITests-frameworks.sh 2 | ${BUILT_PRODUCTS_DIR}/Alamofire/Alamofire.framework -------------------------------------------------------------------------------- /Pods/Target Support Files/Pods-clean_architecture_mvvm_base-clean_architecture_mvvm_baseUITests/Pods-clean_architecture_mvvm_base-clean_architecture_mvvm_baseUITests-frameworks-Release-output-files.xcfilelist: -------------------------------------------------------------------------------- 1 | ${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/Alamofire.framework -------------------------------------------------------------------------------- /Pods/Target Support Files/Pods-clean_architecture_mvvm_base-clean_architecture_mvvm_baseUITests/Pods-clean_architecture_mvvm_base-clean_architecture_mvvm_baseUITests-frameworks.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | set -e 3 | set -u 4 | set -o pipefail 5 | 6 | function on_error { 7 | echo "$(realpath -mq "${0}"):$1: error: Unexpected failure" 8 | } 9 | trap 'on_error $LINENO' ERR 10 | 11 | if [ -z ${FRAMEWORKS_FOLDER_PATH+x} ]; then 12 | # If FRAMEWORKS_FOLDER_PATH is not set, then there's nowhere for us to copy 13 | # frameworks to, so exit 0 (signalling the script phase was successful). 14 | exit 0 15 | fi 16 | 17 | echo "mkdir -p ${CONFIGURATION_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" 18 | mkdir -p "${CONFIGURATION_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" 19 | 20 | COCOAPODS_PARALLEL_CODE_SIGN="${COCOAPODS_PARALLEL_CODE_SIGN:-false}" 21 | SWIFT_STDLIB_PATH="${DT_TOOLCHAIN_DIR}/usr/lib/swift/${PLATFORM_NAME}" 22 | BCSYMBOLMAP_DIR="BCSymbolMaps" 23 | 24 | 25 | # This protects against multiple targets copying the same framework dependency at the same time. The solution 26 | # was originally proposed here: https://lists.samba.org/archive/rsync/2008-February/020158.html 27 | RSYNC_PROTECT_TMP_FILES=(--filter "P .*.??????") 28 | 29 | # Copies and strips a vendored framework 30 | install_framework() 31 | { 32 | if [ -r "${BUILT_PRODUCTS_DIR}/$1" ]; then 33 | local source="${BUILT_PRODUCTS_DIR}/$1" 34 | elif [ -r "${BUILT_PRODUCTS_DIR}/$(basename "$1")" ]; then 35 | local source="${BUILT_PRODUCTS_DIR}/$(basename "$1")" 36 | elif [ -r "$1" ]; then 37 | local source="$1" 38 | fi 39 | 40 | local destination="${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" 41 | 42 | if [ -L "${source}" ]; then 43 | echo "Symlinked..." 44 | source="$(readlink "${source}")" 45 | fi 46 | 47 | if [ -d "${source}/${BCSYMBOLMAP_DIR}" ]; then 48 | # Locate and install any .bcsymbolmaps if present, and remove them from the .framework before the framework is copied 49 | find "${source}/${BCSYMBOLMAP_DIR}" -name "*.bcsymbolmap"|while read f; do 50 | echo "Installing $f" 51 | install_bcsymbolmap "$f" "$destination" 52 | rm "$f" 53 | done 54 | rmdir "${source}/${BCSYMBOLMAP_DIR}" 55 | fi 56 | 57 | # Use filter instead of exclude so missing patterns don't throw errors. 58 | echo "rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --links --filter \"- CVS/\" --filter \"- .svn/\" --filter \"- .git/\" --filter \"- .hg/\" --filter \"- Headers\" --filter \"- PrivateHeaders\" --filter \"- Modules\" \"${source}\" \"${destination}\"" 59 | rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --links --filter "- CVS/" --filter "- .svn/" --filter "- .git/" --filter "- .hg/" --filter "- Headers" --filter "- PrivateHeaders" --filter "- Modules" "${source}" "${destination}" 60 | 61 | local basename 62 | basename="$(basename -s .framework "$1")" 63 | binary="${destination}/${basename}.framework/${basename}" 64 | 65 | if ! [ -r "$binary" ]; then 66 | binary="${destination}/${basename}" 67 | elif [ -L "${binary}" ]; then 68 | echo "Destination binary is symlinked..." 69 | dirname="$(dirname "${binary}")" 70 | binary="${dirname}/$(readlink "${binary}")" 71 | fi 72 | 73 | # Strip invalid architectures so "fat" simulator / device frameworks work on device 74 | if [[ "$(file "$binary")" == *"dynamically linked shared library"* ]]; then 75 | strip_invalid_archs "$binary" 76 | fi 77 | 78 | # Resign the code if required by the build settings to avoid unstable apps 79 | code_sign_if_enabled "${destination}/$(basename "$1")" 80 | 81 | # Embed linked Swift runtime libraries. No longer necessary as of Xcode 7. 82 | if [ "${XCODE_VERSION_MAJOR}" -lt 7 ]; then 83 | local swift_runtime_libs 84 | swift_runtime_libs=$(xcrun otool -LX "$binary" | grep --color=never @rpath/libswift | sed -E s/@rpath\\/\(.+dylib\).*/\\1/g | uniq -u) 85 | for lib in $swift_runtime_libs; do 86 | echo "rsync -auv \"${SWIFT_STDLIB_PATH}/${lib}\" \"${destination}\"" 87 | rsync -auv "${SWIFT_STDLIB_PATH}/${lib}" "${destination}" 88 | code_sign_if_enabled "${destination}/${lib}" 89 | done 90 | fi 91 | } 92 | # Copies and strips a vendored dSYM 93 | install_dsym() { 94 | local source="$1" 95 | warn_missing_arch=${2:-true} 96 | if [ -r "$source" ]; then 97 | # Copy the dSYM into the targets temp dir. 98 | echo "rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --filter \"- CVS/\" --filter \"- .svn/\" --filter \"- .git/\" --filter \"- .hg/\" --filter \"- Headers\" --filter \"- PrivateHeaders\" --filter \"- Modules\" \"${source}\" \"${DERIVED_FILES_DIR}\"" 99 | rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --filter "- CVS/" --filter "- .svn/" --filter "- .git/" --filter "- .hg/" --filter "- Headers" --filter "- PrivateHeaders" --filter "- Modules" "${source}" "${DERIVED_FILES_DIR}" 100 | 101 | local basename 102 | basename="$(basename -s .dSYM "$source")" 103 | binary_name="$(ls "$source/Contents/Resources/DWARF")" 104 | binary="${DERIVED_FILES_DIR}/${basename}.dSYM/Contents/Resources/DWARF/${binary_name}" 105 | 106 | # Strip invalid architectures from the dSYM. 107 | if [[ "$(file "$binary")" == *"Mach-O "*"dSYM companion"* ]]; then 108 | strip_invalid_archs "$binary" "$warn_missing_arch" 109 | fi 110 | if [[ $STRIP_BINARY_RETVAL == 0 ]]; then 111 | # Move the stripped file into its final destination. 112 | echo "rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --links --filter \"- CVS/\" --filter \"- .svn/\" --filter \"- .git/\" --filter \"- .hg/\" --filter \"- Headers\" --filter \"- PrivateHeaders\" --filter \"- Modules\" \"${DERIVED_FILES_DIR}/${basename}.framework.dSYM\" \"${DWARF_DSYM_FOLDER_PATH}\"" 113 | rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --links --filter "- CVS/" --filter "- .svn/" --filter "- .git/" --filter "- .hg/" --filter "- Headers" --filter "- PrivateHeaders" --filter "- Modules" "${DERIVED_FILES_DIR}/${basename}.dSYM" "${DWARF_DSYM_FOLDER_PATH}" 114 | else 115 | # The dSYM was not stripped at all, in this case touch a fake folder so the input/output paths from Xcode do not reexecute this script because the file is missing. 116 | mkdir -p "${DWARF_DSYM_FOLDER_PATH}" 117 | touch "${DWARF_DSYM_FOLDER_PATH}/${basename}.dSYM" 118 | fi 119 | fi 120 | } 121 | 122 | # Used as a return value for each invocation of `strip_invalid_archs` function. 123 | STRIP_BINARY_RETVAL=0 124 | 125 | # Strip invalid architectures 126 | strip_invalid_archs() { 127 | binary="$1" 128 | warn_missing_arch=${2:-true} 129 | # Get architectures for current target binary 130 | binary_archs="$(lipo -info "$binary" | rev | cut -d ':' -f1 | awk '{$1=$1;print}' | rev)" 131 | # Intersect them with the architectures we are building for 132 | intersected_archs="$(echo ${ARCHS[@]} ${binary_archs[@]} | tr ' ' '\n' | sort | uniq -d)" 133 | # If there are no archs supported by this binary then warn the user 134 | if [[ -z "$intersected_archs" ]]; then 135 | if [[ "$warn_missing_arch" == "true" ]]; then 136 | echo "warning: [CP] Vendored binary '$binary' contains architectures ($binary_archs) none of which match the current build architectures ($ARCHS)." 137 | fi 138 | STRIP_BINARY_RETVAL=1 139 | return 140 | fi 141 | stripped="" 142 | for arch in $binary_archs; do 143 | if ! [[ "${ARCHS}" == *"$arch"* ]]; then 144 | # Strip non-valid architectures in-place 145 | lipo -remove "$arch" -output "$binary" "$binary" 146 | stripped="$stripped $arch" 147 | fi 148 | done 149 | if [[ "$stripped" ]]; then 150 | echo "Stripped $binary of architectures:$stripped" 151 | fi 152 | STRIP_BINARY_RETVAL=0 153 | } 154 | 155 | # Copies the bcsymbolmap files of a vendored framework 156 | install_bcsymbolmap() { 157 | local bcsymbolmap_path="$1" 158 | local destination="${BUILT_PRODUCTS_DIR}" 159 | echo "rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --filter "- CVS/" --filter "- .svn/" --filter "- .git/" --filter "- .hg/" --filter "- Headers" --filter "- PrivateHeaders" --filter "- Modules" "${bcsymbolmap_path}" "${destination}"" 160 | rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --filter "- CVS/" --filter "- .svn/" --filter "- .git/" --filter "- .hg/" --filter "- Headers" --filter "- PrivateHeaders" --filter "- Modules" "${bcsymbolmap_path}" "${destination}" 161 | } 162 | 163 | # Signs a framework with the provided identity 164 | code_sign_if_enabled() { 165 | if [ -n "${EXPANDED_CODE_SIGN_IDENTITY:-}" -a "${CODE_SIGNING_REQUIRED:-}" != "NO" -a "${CODE_SIGNING_ALLOWED}" != "NO" ]; then 166 | # Use the current code_sign_identity 167 | echo "Code Signing $1 with Identity ${EXPANDED_CODE_SIGN_IDENTITY_NAME}" 168 | local code_sign_cmd="/usr/bin/codesign --force --sign ${EXPANDED_CODE_SIGN_IDENTITY} ${OTHER_CODE_SIGN_FLAGS:-} --preserve-metadata=identifier,entitlements '$1'" 169 | 170 | if [ "${COCOAPODS_PARALLEL_CODE_SIGN}" == "true" ]; then 171 | code_sign_cmd="$code_sign_cmd &" 172 | fi 173 | echo "$code_sign_cmd" 174 | eval "$code_sign_cmd" 175 | fi 176 | } 177 | 178 | if [[ "$CONFIGURATION" == "Debug" ]]; then 179 | install_framework "${BUILT_PRODUCTS_DIR}/Alamofire/Alamofire.framework" 180 | fi 181 | if [[ "$CONFIGURATION" == "Release" ]]; then 182 | install_framework "${BUILT_PRODUCTS_DIR}/Alamofire/Alamofire.framework" 183 | fi 184 | if [ "${COCOAPODS_PARALLEL_CODE_SIGN}" == "true" ]; then 185 | wait 186 | fi 187 | -------------------------------------------------------------------------------- /Pods/Target Support Files/Pods-clean_architecture_mvvm_base-clean_architecture_mvvm_baseUITests/Pods-clean_architecture_mvvm_base-clean_architecture_mvvm_baseUITests-umbrella.h: -------------------------------------------------------------------------------- 1 | #ifdef __OBJC__ 2 | #import 3 | #else 4 | #ifndef FOUNDATION_EXPORT 5 | #if defined(__cplusplus) 6 | #define FOUNDATION_EXPORT extern "C" 7 | #else 8 | #define FOUNDATION_EXPORT extern 9 | #endif 10 | #endif 11 | #endif 12 | 13 | 14 | FOUNDATION_EXPORT double Pods_clean_architecture_mvvm_base_clean_architecture_mvvm_baseUITestsVersionNumber; 15 | FOUNDATION_EXPORT const unsigned char Pods_clean_architecture_mvvm_base_clean_architecture_mvvm_baseUITestsVersionString[]; 16 | 17 | -------------------------------------------------------------------------------- /Pods/Target Support Files/Pods-clean_architecture_mvvm_base-clean_architecture_mvvm_baseUITests/Pods-clean_architecture_mvvm_base-clean_architecture_mvvm_baseUITests.debug.xcconfig: -------------------------------------------------------------------------------- 1 | ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES 2 | CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = NO 3 | FRAMEWORK_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/Alamofire" 4 | GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 5 | HEADER_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/Alamofire/Alamofire.framework/Headers" 6 | LD_RUNPATH_SEARCH_PATHS = $(inherited) /usr/lib/swift "$(PLATFORM_DIR)/Developer/Library/Frameworks" '@executable_path/Frameworks' '@loader_path/Frameworks' 7 | LIBRARY_SEARCH_PATHS = $(inherited) "${DT_TOOLCHAIN_DIR}/usr/lib/swift/${PLATFORM_NAME}" /usr/lib/swift 8 | OTHER_LDFLAGS = $(inherited) -framework "Alamofire" -framework "CFNetwork" 9 | OTHER_SWIFT_FLAGS = $(inherited) -D COCOAPODS 10 | PODS_BUILD_DIR = ${BUILD_DIR} 11 | PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME) 12 | PODS_PODFILE_DIR_PATH = ${SRCROOT}/. 13 | PODS_ROOT = ${SRCROOT}/Pods 14 | PODS_XCFRAMEWORKS_BUILD_DIR = $(PODS_CONFIGURATION_BUILD_DIR)/XCFrameworkIntermediates 15 | USE_RECURSIVE_SCRIPT_INPUTS_IN_SCRIPT_PHASES = YES 16 | -------------------------------------------------------------------------------- /Pods/Target Support Files/Pods-clean_architecture_mvvm_base-clean_architecture_mvvm_baseUITests/Pods-clean_architecture_mvvm_base-clean_architecture_mvvm_baseUITests.modulemap: -------------------------------------------------------------------------------- 1 | framework module Pods_clean_architecture_mvvm_base_clean_architecture_mvvm_baseUITests { 2 | umbrella header "Pods-clean_architecture_mvvm_base-clean_architecture_mvvm_baseUITests-umbrella.h" 3 | 4 | export * 5 | module * { export * } 6 | } 7 | -------------------------------------------------------------------------------- /Pods/Target Support Files/Pods-clean_architecture_mvvm_base-clean_architecture_mvvm_baseUITests/Pods-clean_architecture_mvvm_base-clean_architecture_mvvm_baseUITests.release.xcconfig: -------------------------------------------------------------------------------- 1 | ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES 2 | CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = NO 3 | FRAMEWORK_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/Alamofire" 4 | GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 5 | HEADER_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/Alamofire/Alamofire.framework/Headers" 6 | LD_RUNPATH_SEARCH_PATHS = $(inherited) /usr/lib/swift "$(PLATFORM_DIR)/Developer/Library/Frameworks" '@executable_path/Frameworks' '@loader_path/Frameworks' 7 | LIBRARY_SEARCH_PATHS = $(inherited) "${DT_TOOLCHAIN_DIR}/usr/lib/swift/${PLATFORM_NAME}" /usr/lib/swift 8 | OTHER_LDFLAGS = $(inherited) -framework "Alamofire" -framework "CFNetwork" 9 | OTHER_SWIFT_FLAGS = $(inherited) -D COCOAPODS 10 | PODS_BUILD_DIR = ${BUILD_DIR} 11 | PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME) 12 | PODS_PODFILE_DIR_PATH = ${SRCROOT}/. 13 | PODS_ROOT = ${SRCROOT}/Pods 14 | PODS_XCFRAMEWORKS_BUILD_DIR = $(PODS_CONFIGURATION_BUILD_DIR)/XCFrameworkIntermediates 15 | USE_RECURSIVE_SCRIPT_INPUTS_IN_SCRIPT_PHASES = YES 16 | -------------------------------------------------------------------------------- /Pods/Target Support Files/Pods-clean_architecture_mvvm_base/Pods-clean_architecture_mvvm_base-Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | ${EXECUTABLE_NAME} 9 | CFBundleIdentifier 10 | ${PRODUCT_BUNDLE_IDENTIFIER} 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | ${PRODUCT_NAME} 15 | CFBundlePackageType 16 | FMWK 17 | CFBundleShortVersionString 18 | 1.0.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | ${CURRENT_PROJECT_VERSION} 23 | NSPrincipalClass 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /Pods/Target Support Files/Pods-clean_architecture_mvvm_base/Pods-clean_architecture_mvvm_base-acknowledgements.markdown: -------------------------------------------------------------------------------- 1 | # Acknowledgements 2 | This application makes use of the following third party libraries: 3 | 4 | ## Alamofire 5 | 6 | Copyright (c) 2014-2021 Alamofire Software Foundation (http://alamofire.org/) 7 | 8 | Permission is hereby granted, free of charge, to any person obtaining a copy 9 | of this software and associated documentation files (the "Software"), to deal 10 | in the Software without restriction, including without limitation the rights 11 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 12 | copies of the Software, and to permit persons to whom the Software is 13 | furnished to do so, subject to the following conditions: 14 | 15 | The above copyright notice and this permission notice shall be included in 16 | all copies or substantial portions of the Software. 17 | 18 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 19 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 20 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 21 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 22 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 23 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 24 | THE SOFTWARE. 25 | 26 | Generated by CocoaPods - https://cocoapods.org 27 | -------------------------------------------------------------------------------- /Pods/Target Support Files/Pods-clean_architecture_mvvm_base/Pods-clean_architecture_mvvm_base-acknowledgements.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | PreferenceSpecifiers 6 | 7 | 8 | FooterText 9 | This application makes use of the following third party libraries: 10 | Title 11 | Acknowledgements 12 | Type 13 | PSGroupSpecifier 14 | 15 | 16 | FooterText 17 | Copyright (c) 2014-2021 Alamofire Software Foundation (http://alamofire.org/) 18 | 19 | Permission is hereby granted, free of charge, to any person obtaining a copy 20 | of this software and associated documentation files (the "Software"), to deal 21 | in the Software without restriction, including without limitation the rights 22 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 23 | copies of the Software, and to permit persons to whom the Software is 24 | furnished to do so, subject to the following conditions: 25 | 26 | The above copyright notice and this permission notice shall be included in 27 | all copies or substantial portions of the Software. 28 | 29 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 30 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 31 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 32 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 33 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 34 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 35 | THE SOFTWARE. 36 | 37 | License 38 | MIT 39 | Title 40 | Alamofire 41 | Type 42 | PSGroupSpecifier 43 | 44 | 45 | FooterText 46 | Generated by CocoaPods - https://cocoapods.org 47 | Title 48 | 49 | Type 50 | PSGroupSpecifier 51 | 52 | 53 | StringsTable 54 | Acknowledgements 55 | Title 56 | Acknowledgements 57 | 58 | 59 | -------------------------------------------------------------------------------- /Pods/Target Support Files/Pods-clean_architecture_mvvm_base/Pods-clean_architecture_mvvm_base-dummy.m: -------------------------------------------------------------------------------- 1 | #import 2 | @interface PodsDummy_Pods_clean_architecture_mvvm_base : NSObject 3 | @end 4 | @implementation PodsDummy_Pods_clean_architecture_mvvm_base 5 | @end 6 | -------------------------------------------------------------------------------- /Pods/Target Support Files/Pods-clean_architecture_mvvm_base/Pods-clean_architecture_mvvm_base-frameworks-Debug-input-files.xcfilelist: -------------------------------------------------------------------------------- 1 | ${PODS_ROOT}/Target Support Files/Pods-clean_architecture_mvvm_base/Pods-clean_architecture_mvvm_base-frameworks.sh 2 | ${BUILT_PRODUCTS_DIR}/Alamofire/Alamofire.framework -------------------------------------------------------------------------------- /Pods/Target Support Files/Pods-clean_architecture_mvvm_base/Pods-clean_architecture_mvvm_base-frameworks-Debug-output-files.xcfilelist: -------------------------------------------------------------------------------- 1 | ${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/Alamofire.framework -------------------------------------------------------------------------------- /Pods/Target Support Files/Pods-clean_architecture_mvvm_base/Pods-clean_architecture_mvvm_base-frameworks-Release-input-files.xcfilelist: -------------------------------------------------------------------------------- 1 | ${PODS_ROOT}/Target Support Files/Pods-clean_architecture_mvvm_base/Pods-clean_architecture_mvvm_base-frameworks.sh 2 | ${BUILT_PRODUCTS_DIR}/Alamofire/Alamofire.framework -------------------------------------------------------------------------------- /Pods/Target Support Files/Pods-clean_architecture_mvvm_base/Pods-clean_architecture_mvvm_base-frameworks-Release-output-files.xcfilelist: -------------------------------------------------------------------------------- 1 | ${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/Alamofire.framework -------------------------------------------------------------------------------- /Pods/Target Support Files/Pods-clean_architecture_mvvm_base/Pods-clean_architecture_mvvm_base-frameworks.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | set -e 3 | set -u 4 | set -o pipefail 5 | 6 | function on_error { 7 | echo "$(realpath -mq "${0}"):$1: error: Unexpected failure" 8 | } 9 | trap 'on_error $LINENO' ERR 10 | 11 | if [ -z ${FRAMEWORKS_FOLDER_PATH+x} ]; then 12 | # If FRAMEWORKS_FOLDER_PATH is not set, then there's nowhere for us to copy 13 | # frameworks to, so exit 0 (signalling the script phase was successful). 14 | exit 0 15 | fi 16 | 17 | echo "mkdir -p ${CONFIGURATION_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" 18 | mkdir -p "${CONFIGURATION_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" 19 | 20 | COCOAPODS_PARALLEL_CODE_SIGN="${COCOAPODS_PARALLEL_CODE_SIGN:-false}" 21 | SWIFT_STDLIB_PATH="${DT_TOOLCHAIN_DIR}/usr/lib/swift/${PLATFORM_NAME}" 22 | BCSYMBOLMAP_DIR="BCSymbolMaps" 23 | 24 | 25 | # This protects against multiple targets copying the same framework dependency at the same time. The solution 26 | # was originally proposed here: https://lists.samba.org/archive/rsync/2008-February/020158.html 27 | RSYNC_PROTECT_TMP_FILES=(--filter "P .*.??????") 28 | 29 | # Copies and strips a vendored framework 30 | install_framework() 31 | { 32 | if [ -r "${BUILT_PRODUCTS_DIR}/$1" ]; then 33 | local source="${BUILT_PRODUCTS_DIR}/$1" 34 | elif [ -r "${BUILT_PRODUCTS_DIR}/$(basename "$1")" ]; then 35 | local source="${BUILT_PRODUCTS_DIR}/$(basename "$1")" 36 | elif [ -r "$1" ]; then 37 | local source="$1" 38 | fi 39 | 40 | local destination="${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" 41 | 42 | if [ -L "${source}" ]; then 43 | echo "Symlinked..." 44 | source="$(readlink "${source}")" 45 | fi 46 | 47 | if [ -d "${source}/${BCSYMBOLMAP_DIR}" ]; then 48 | # Locate and install any .bcsymbolmaps if present, and remove them from the .framework before the framework is copied 49 | find "${source}/${BCSYMBOLMAP_DIR}" -name "*.bcsymbolmap"|while read f; do 50 | echo "Installing $f" 51 | install_bcsymbolmap "$f" "$destination" 52 | rm "$f" 53 | done 54 | rmdir "${source}/${BCSYMBOLMAP_DIR}" 55 | fi 56 | 57 | # Use filter instead of exclude so missing patterns don't throw errors. 58 | echo "rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --links --filter \"- CVS/\" --filter \"- .svn/\" --filter \"- .git/\" --filter \"- .hg/\" --filter \"- Headers\" --filter \"- PrivateHeaders\" --filter \"- Modules\" \"${source}\" \"${destination}\"" 59 | rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --links --filter "- CVS/" --filter "- .svn/" --filter "- .git/" --filter "- .hg/" --filter "- Headers" --filter "- PrivateHeaders" --filter "- Modules" "${source}" "${destination}" 60 | 61 | local basename 62 | basename="$(basename -s .framework "$1")" 63 | binary="${destination}/${basename}.framework/${basename}" 64 | 65 | if ! [ -r "$binary" ]; then 66 | binary="${destination}/${basename}" 67 | elif [ -L "${binary}" ]; then 68 | echo "Destination binary is symlinked..." 69 | dirname="$(dirname "${binary}")" 70 | binary="${dirname}/$(readlink "${binary}")" 71 | fi 72 | 73 | # Strip invalid architectures so "fat" simulator / device frameworks work on device 74 | if [[ "$(file "$binary")" == *"dynamically linked shared library"* ]]; then 75 | strip_invalid_archs "$binary" 76 | fi 77 | 78 | # Resign the code if required by the build settings to avoid unstable apps 79 | code_sign_if_enabled "${destination}/$(basename "$1")" 80 | 81 | # Embed linked Swift runtime libraries. No longer necessary as of Xcode 7. 82 | if [ "${XCODE_VERSION_MAJOR}" -lt 7 ]; then 83 | local swift_runtime_libs 84 | swift_runtime_libs=$(xcrun otool -LX "$binary" | grep --color=never @rpath/libswift | sed -E s/@rpath\\/\(.+dylib\).*/\\1/g | uniq -u) 85 | for lib in $swift_runtime_libs; do 86 | echo "rsync -auv \"${SWIFT_STDLIB_PATH}/${lib}\" \"${destination}\"" 87 | rsync -auv "${SWIFT_STDLIB_PATH}/${lib}" "${destination}" 88 | code_sign_if_enabled "${destination}/${lib}" 89 | done 90 | fi 91 | } 92 | # Copies and strips a vendored dSYM 93 | install_dsym() { 94 | local source="$1" 95 | warn_missing_arch=${2:-true} 96 | if [ -r "$source" ]; then 97 | # Copy the dSYM into the targets temp dir. 98 | echo "rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --filter \"- CVS/\" --filter \"- .svn/\" --filter \"- .git/\" --filter \"- .hg/\" --filter \"- Headers\" --filter \"- PrivateHeaders\" --filter \"- Modules\" \"${source}\" \"${DERIVED_FILES_DIR}\"" 99 | rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --filter "- CVS/" --filter "- .svn/" --filter "- .git/" --filter "- .hg/" --filter "- Headers" --filter "- PrivateHeaders" --filter "- Modules" "${source}" "${DERIVED_FILES_DIR}" 100 | 101 | local basename 102 | basename="$(basename -s .dSYM "$source")" 103 | binary_name="$(ls "$source/Contents/Resources/DWARF")" 104 | binary="${DERIVED_FILES_DIR}/${basename}.dSYM/Contents/Resources/DWARF/${binary_name}" 105 | 106 | # Strip invalid architectures from the dSYM. 107 | if [[ "$(file "$binary")" == *"Mach-O "*"dSYM companion"* ]]; then 108 | strip_invalid_archs "$binary" "$warn_missing_arch" 109 | fi 110 | if [[ $STRIP_BINARY_RETVAL == 0 ]]; then 111 | # Move the stripped file into its final destination. 112 | echo "rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --links --filter \"- CVS/\" --filter \"- .svn/\" --filter \"- .git/\" --filter \"- .hg/\" --filter \"- Headers\" --filter \"- PrivateHeaders\" --filter \"- Modules\" \"${DERIVED_FILES_DIR}/${basename}.framework.dSYM\" \"${DWARF_DSYM_FOLDER_PATH}\"" 113 | rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --links --filter "- CVS/" --filter "- .svn/" --filter "- .git/" --filter "- .hg/" --filter "- Headers" --filter "- PrivateHeaders" --filter "- Modules" "${DERIVED_FILES_DIR}/${basename}.dSYM" "${DWARF_DSYM_FOLDER_PATH}" 114 | else 115 | # The dSYM was not stripped at all, in this case touch a fake folder so the input/output paths from Xcode do not reexecute this script because the file is missing. 116 | mkdir -p "${DWARF_DSYM_FOLDER_PATH}" 117 | touch "${DWARF_DSYM_FOLDER_PATH}/${basename}.dSYM" 118 | fi 119 | fi 120 | } 121 | 122 | # Used as a return value for each invocation of `strip_invalid_archs` function. 123 | STRIP_BINARY_RETVAL=0 124 | 125 | # Strip invalid architectures 126 | strip_invalid_archs() { 127 | binary="$1" 128 | warn_missing_arch=${2:-true} 129 | # Get architectures for current target binary 130 | binary_archs="$(lipo -info "$binary" | rev | cut -d ':' -f1 | awk '{$1=$1;print}' | rev)" 131 | # Intersect them with the architectures we are building for 132 | intersected_archs="$(echo ${ARCHS[@]} ${binary_archs[@]} | tr ' ' '\n' | sort | uniq -d)" 133 | # If there are no archs supported by this binary then warn the user 134 | if [[ -z "$intersected_archs" ]]; then 135 | if [[ "$warn_missing_arch" == "true" ]]; then 136 | echo "warning: [CP] Vendored binary '$binary' contains architectures ($binary_archs) none of which match the current build architectures ($ARCHS)." 137 | fi 138 | STRIP_BINARY_RETVAL=1 139 | return 140 | fi 141 | stripped="" 142 | for arch in $binary_archs; do 143 | if ! [[ "${ARCHS}" == *"$arch"* ]]; then 144 | # Strip non-valid architectures in-place 145 | lipo -remove "$arch" -output "$binary" "$binary" 146 | stripped="$stripped $arch" 147 | fi 148 | done 149 | if [[ "$stripped" ]]; then 150 | echo "Stripped $binary of architectures:$stripped" 151 | fi 152 | STRIP_BINARY_RETVAL=0 153 | } 154 | 155 | # Copies the bcsymbolmap files of a vendored framework 156 | install_bcsymbolmap() { 157 | local bcsymbolmap_path="$1" 158 | local destination="${BUILT_PRODUCTS_DIR}" 159 | echo "rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --filter "- CVS/" --filter "- .svn/" --filter "- .git/" --filter "- .hg/" --filter "- Headers" --filter "- PrivateHeaders" --filter "- Modules" "${bcsymbolmap_path}" "${destination}"" 160 | rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --filter "- CVS/" --filter "- .svn/" --filter "- .git/" --filter "- .hg/" --filter "- Headers" --filter "- PrivateHeaders" --filter "- Modules" "${bcsymbolmap_path}" "${destination}" 161 | } 162 | 163 | # Signs a framework with the provided identity 164 | code_sign_if_enabled() { 165 | if [ -n "${EXPANDED_CODE_SIGN_IDENTITY:-}" -a "${CODE_SIGNING_REQUIRED:-}" != "NO" -a "${CODE_SIGNING_ALLOWED}" != "NO" ]; then 166 | # Use the current code_sign_identity 167 | echo "Code Signing $1 with Identity ${EXPANDED_CODE_SIGN_IDENTITY_NAME}" 168 | local code_sign_cmd="/usr/bin/codesign --force --sign ${EXPANDED_CODE_SIGN_IDENTITY} ${OTHER_CODE_SIGN_FLAGS:-} --preserve-metadata=identifier,entitlements '$1'" 169 | 170 | if [ "${COCOAPODS_PARALLEL_CODE_SIGN}" == "true" ]; then 171 | code_sign_cmd="$code_sign_cmd &" 172 | fi 173 | echo "$code_sign_cmd" 174 | eval "$code_sign_cmd" 175 | fi 176 | } 177 | 178 | if [[ "$CONFIGURATION" == "Debug" ]]; then 179 | install_framework "${BUILT_PRODUCTS_DIR}/Alamofire/Alamofire.framework" 180 | fi 181 | if [[ "$CONFIGURATION" == "Release" ]]; then 182 | install_framework "${BUILT_PRODUCTS_DIR}/Alamofire/Alamofire.framework" 183 | fi 184 | if [ "${COCOAPODS_PARALLEL_CODE_SIGN}" == "true" ]; then 185 | wait 186 | fi 187 | -------------------------------------------------------------------------------- /Pods/Target Support Files/Pods-clean_architecture_mvvm_base/Pods-clean_architecture_mvvm_base-umbrella.h: -------------------------------------------------------------------------------- 1 | #ifdef __OBJC__ 2 | #import 3 | #else 4 | #ifndef FOUNDATION_EXPORT 5 | #if defined(__cplusplus) 6 | #define FOUNDATION_EXPORT extern "C" 7 | #else 8 | #define FOUNDATION_EXPORT extern 9 | #endif 10 | #endif 11 | #endif 12 | 13 | 14 | FOUNDATION_EXPORT double Pods_clean_architecture_mvvm_baseVersionNumber; 15 | FOUNDATION_EXPORT const unsigned char Pods_clean_architecture_mvvm_baseVersionString[]; 16 | 17 | -------------------------------------------------------------------------------- /Pods/Target Support Files/Pods-clean_architecture_mvvm_base/Pods-clean_architecture_mvvm_base.debug.xcconfig: -------------------------------------------------------------------------------- 1 | ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES 2 | CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = NO 3 | FRAMEWORK_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/Alamofire" 4 | GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 5 | HEADER_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/Alamofire/Alamofire.framework/Headers" 6 | LD_RUNPATH_SEARCH_PATHS = $(inherited) /usr/lib/swift '@executable_path/Frameworks' '@loader_path/Frameworks' 7 | LIBRARY_SEARCH_PATHS = $(inherited) "${DT_TOOLCHAIN_DIR}/usr/lib/swift/${PLATFORM_NAME}" /usr/lib/swift 8 | OTHER_LDFLAGS = $(inherited) -framework "Alamofire" -framework "CFNetwork" 9 | OTHER_SWIFT_FLAGS = $(inherited) -D COCOAPODS 10 | PODS_BUILD_DIR = ${BUILD_DIR} 11 | PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME) 12 | PODS_PODFILE_DIR_PATH = ${SRCROOT}/. 13 | PODS_ROOT = ${SRCROOT}/Pods 14 | PODS_XCFRAMEWORKS_BUILD_DIR = $(PODS_CONFIGURATION_BUILD_DIR)/XCFrameworkIntermediates 15 | USE_RECURSIVE_SCRIPT_INPUTS_IN_SCRIPT_PHASES = YES 16 | -------------------------------------------------------------------------------- /Pods/Target Support Files/Pods-clean_architecture_mvvm_base/Pods-clean_architecture_mvvm_base.modulemap: -------------------------------------------------------------------------------- 1 | framework module Pods_clean_architecture_mvvm_base { 2 | umbrella header "Pods-clean_architecture_mvvm_base-umbrella.h" 3 | 4 | export * 5 | module * { export * } 6 | } 7 | -------------------------------------------------------------------------------- /Pods/Target Support Files/Pods-clean_architecture_mvvm_base/Pods-clean_architecture_mvvm_base.release.xcconfig: -------------------------------------------------------------------------------- 1 | ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES 2 | CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = NO 3 | FRAMEWORK_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/Alamofire" 4 | GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 5 | HEADER_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/Alamofire/Alamofire.framework/Headers" 6 | LD_RUNPATH_SEARCH_PATHS = $(inherited) /usr/lib/swift '@executable_path/Frameworks' '@loader_path/Frameworks' 7 | LIBRARY_SEARCH_PATHS = $(inherited) "${DT_TOOLCHAIN_DIR}/usr/lib/swift/${PLATFORM_NAME}" /usr/lib/swift 8 | OTHER_LDFLAGS = $(inherited) -framework "Alamofire" -framework "CFNetwork" 9 | OTHER_SWIFT_FLAGS = $(inherited) -D COCOAPODS 10 | PODS_BUILD_DIR = ${BUILD_DIR} 11 | PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME) 12 | PODS_PODFILE_DIR_PATH = ${SRCROOT}/. 13 | PODS_ROOT = ${SRCROOT}/Pods 14 | PODS_XCFRAMEWORKS_BUILD_DIR = $(PODS_CONFIGURATION_BUILD_DIR)/XCFrameworkIntermediates 15 | USE_RECURSIVE_SCRIPT_INPUTS_IN_SCRIPT_PHASES = YES 16 | -------------------------------------------------------------------------------- /Pods/Target Support Files/Pods-clean_architecture_mvvm_baseTests/Pods-clean_architecture_mvvm_baseTests-Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | ${EXECUTABLE_NAME} 9 | CFBundleIdentifier 10 | ${PRODUCT_BUNDLE_IDENTIFIER} 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | ${PRODUCT_NAME} 15 | CFBundlePackageType 16 | FMWK 17 | CFBundleShortVersionString 18 | 1.0.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | ${CURRENT_PROJECT_VERSION} 23 | NSPrincipalClass 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /Pods/Target Support Files/Pods-clean_architecture_mvvm_baseTests/Pods-clean_architecture_mvvm_baseTests-acknowledgements.markdown: -------------------------------------------------------------------------------- 1 | # Acknowledgements 2 | This application makes use of the following third party libraries: 3 | Generated by CocoaPods - https://cocoapods.org 4 | -------------------------------------------------------------------------------- /Pods/Target Support Files/Pods-clean_architecture_mvvm_baseTests/Pods-clean_architecture_mvvm_baseTests-acknowledgements.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | PreferenceSpecifiers 6 | 7 | 8 | FooterText 9 | This application makes use of the following third party libraries: 10 | Title 11 | Acknowledgements 12 | Type 13 | PSGroupSpecifier 14 | 15 | 16 | FooterText 17 | Generated by CocoaPods - https://cocoapods.org 18 | Title 19 | 20 | Type 21 | PSGroupSpecifier 22 | 23 | 24 | StringsTable 25 | Acknowledgements 26 | Title 27 | Acknowledgements 28 | 29 | 30 | -------------------------------------------------------------------------------- /Pods/Target Support Files/Pods-clean_architecture_mvvm_baseTests/Pods-clean_architecture_mvvm_baseTests-dummy.m: -------------------------------------------------------------------------------- 1 | #import 2 | @interface PodsDummy_Pods_clean_architecture_mvvm_baseTests : NSObject 3 | @end 4 | @implementation PodsDummy_Pods_clean_architecture_mvvm_baseTests 5 | @end 6 | -------------------------------------------------------------------------------- /Pods/Target Support Files/Pods-clean_architecture_mvvm_baseTests/Pods-clean_architecture_mvvm_baseTests-umbrella.h: -------------------------------------------------------------------------------- 1 | #ifdef __OBJC__ 2 | #import 3 | #else 4 | #ifndef FOUNDATION_EXPORT 5 | #if defined(__cplusplus) 6 | #define FOUNDATION_EXPORT extern "C" 7 | #else 8 | #define FOUNDATION_EXPORT extern 9 | #endif 10 | #endif 11 | #endif 12 | 13 | 14 | FOUNDATION_EXPORT double Pods_clean_architecture_mvvm_baseTestsVersionNumber; 15 | FOUNDATION_EXPORT const unsigned char Pods_clean_architecture_mvvm_baseTestsVersionString[]; 16 | 17 | -------------------------------------------------------------------------------- /Pods/Target Support Files/Pods-clean_architecture_mvvm_baseTests/Pods-clean_architecture_mvvm_baseTests.debug.xcconfig: -------------------------------------------------------------------------------- 1 | CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = NO 2 | FRAMEWORK_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/Alamofire" 3 | GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 4 | HEADER_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/Alamofire/Alamofire.framework/Headers" 5 | OTHER_LDFLAGS = $(inherited) -framework "Alamofire" -framework "CFNetwork" 6 | PODS_BUILD_DIR = ${BUILD_DIR} 7 | PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME) 8 | PODS_PODFILE_DIR_PATH = ${SRCROOT}/. 9 | PODS_ROOT = ${SRCROOT}/Pods 10 | PODS_XCFRAMEWORKS_BUILD_DIR = $(PODS_CONFIGURATION_BUILD_DIR)/XCFrameworkIntermediates 11 | USE_RECURSIVE_SCRIPT_INPUTS_IN_SCRIPT_PHASES = YES 12 | -------------------------------------------------------------------------------- /Pods/Target Support Files/Pods-clean_architecture_mvvm_baseTests/Pods-clean_architecture_mvvm_baseTests.modulemap: -------------------------------------------------------------------------------- 1 | framework module Pods_clean_architecture_mvvm_baseTests { 2 | umbrella header "Pods-clean_architecture_mvvm_baseTests-umbrella.h" 3 | 4 | export * 5 | module * { export * } 6 | } 7 | -------------------------------------------------------------------------------- /Pods/Target Support Files/Pods-clean_architecture_mvvm_baseTests/Pods-clean_architecture_mvvm_baseTests.release.xcconfig: -------------------------------------------------------------------------------- 1 | CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = NO 2 | FRAMEWORK_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/Alamofire" 3 | GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 4 | HEADER_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/Alamofire/Alamofire.framework/Headers" 5 | OTHER_LDFLAGS = $(inherited) -framework "Alamofire" -framework "CFNetwork" 6 | PODS_BUILD_DIR = ${BUILD_DIR} 7 | PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME) 8 | PODS_PODFILE_DIR_PATH = ${SRCROOT}/. 9 | PODS_ROOT = ${SRCROOT}/Pods 10 | PODS_XCFRAMEWORKS_BUILD_DIR = $(PODS_CONFIGURATION_BUILD_DIR)/XCFrameworkIntermediates 11 | USE_RECURSIVE_SCRIPT_INPUTS_IN_SCRIPT_PHASES = YES 12 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Swift Clean Architecture MVVM 2 | This is an example of clean architecture and MVVM pattern written in swift 3 | 4 | First of all thanks to all of those who made our lives easier 5 | Thanks to uncle bob for the clean architecture 6 | 7 | Thanks to Mocky[https://designer.mocky.io] for creating the mocking APIs 8 | 9 | 10 | # Generics 11 | I've tried to use generics to make every things follows the same patterns. 12 | 13 | The base view controller will contain a view model that will be a generic based on the base view model 14 | 15 | The base view model will have a usecase which is the parent use case for all usecases in the project 16 | 17 | The networking class have only one method that will call our API using #Alamofire and for parsing it will take a generic model that is Codable 18 | 19 | 20 | # The Domain layer 21 | Contains all the use cases, Entities and the defintion of our repository for the project 22 | we have a single use that will represent one task that should be done 23 | we also have usecase which group all the usecases that is related to one feature 24 | 25 | within the single use cases, I have intended to convert the completion handler to combine, this is only to show the two ways of dealing with our requests although we can stick to combine within the networking layer or stick to completion handler 26 | 27 | 28 | # The Data layer 29 | Contains the data sources that implement the protocl of data source 30 | for now there is only remote data source that will fetch the content from the API, but when creating the local data source we should implement the protocol of the data source. 31 | 32 | we also have the repository implementation within this layer that will be responsible of declaring the implementation from the domain layer 33 | and for sure will be responsible of fetching the data and handling the data from local/remote sources 34 | 35 | The difference between the model in this layer and the entity in the domain layer is that the model here will be used to parse the json Object from the API, whereas the entity in the domain will be used to communicate with the presentation layer as it will be easier after converting the model to entity 36 | 37 | 38 | # The Presentation layer 39 | Contains all the views, storyboards, custom views, custom cells and the view controllers 40 | each view model will be a child for BaseViewModel, and each view controller will be a child for BaseViewController where we should declare the viewmodel that will be used 41 | 42 | # To DO: 43 | - Enhance the API class to inculde all the cases 44 | - Add more tests 45 | - Replace Completion handler with Combine and keep the refrence for completion handle 46 | - Add Swift UI examples 47 | 48 | -------------------------------------------------------------------------------- /clean_architecture_mvvm_base.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /clean_architecture_mvvm_base.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /clean_architecture_mvvm_base.xcodeproj/project.xcworkspace/xcuserdata/dani.xcuserdatad/UserInterfaceState.xcuserstate: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DaniEid/swift_clean_architecture_mvvm_base/205d01ed49eed33e610a152b8041150a3693b174/clean_architecture_mvvm_base.xcodeproj/project.xcworkspace/xcuserdata/dani.xcuserdatad/UserInterfaceState.xcuserstate -------------------------------------------------------------------------------- /clean_architecture_mvvm_base.xcodeproj/xcuserdata/dani.xcuserdatad/xcschemes/xcschememanagement.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | SchemeUserState 6 | 7 | clean_architecture_mvvm_base.xcscheme_^#shared#^_ 8 | 9 | orderHint 10 | 4 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /clean_architecture_mvvm_base.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /clean_architecture_mvvm_base.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /clean_architecture_mvvm_base.xcworkspace/xcuserdata/dani.xcuserdatad/UserInterfaceState.xcuserstate: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DaniEid/swift_clean_architecture_mvvm_base/205d01ed49eed33e610a152b8041150a3693b174/clean_architecture_mvvm_base.xcworkspace/xcuserdata/dani.xcuserdatad/UserInterfaceState.xcuserstate -------------------------------------------------------------------------------- /clean_architecture_mvvm_base/Application/AppDelegate.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.swift 3 | // TestApp 4 | // 5 | // Created by Dani on 16/11/2021. 6 | // 7 | 8 | import UIKit 9 | 10 | @main 11 | class AppDelegate: UIResponder, UIApplicationDelegate { 12 | 13 | 14 | 15 | func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { 16 | // Override point for customization after application launch. 17 | return true 18 | } 19 | 20 | // MARK: UISceneSession Lifecycle 21 | 22 | func application(_ application: UIApplication, configurationForConnecting connectingSceneSession: UISceneSession, options: UIScene.ConnectionOptions) -> UISceneConfiguration { 23 | // Called when a new scene session is being created. 24 | // Use this method to select a configuration to create the new scene with. 25 | return UISceneConfiguration(name: "Default Configuration", sessionRole: connectingSceneSession.role) 26 | } 27 | 28 | func application(_ application: UIApplication, didDiscardSceneSessions sceneSessions: Set) { 29 | // Called when the user discards a scene session. 30 | // If any sessions were discarded while the application was not running, this will be called shortly after application:didFinishLaunchingWithOptions. 31 | // Use this method to release any resources that were specific to the discarded scenes, as they will not return. 32 | } 33 | 34 | 35 | } 36 | 37 | -------------------------------------------------------------------------------- /clean_architecture_mvvm_base/Application/Architecture/BaseViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // BaseViewController.swift 3 | // TestApp 4 | // 5 | // Created by Dani on 18/11/2021. 6 | // 7 | 8 | import Foundation 9 | import UIKit 10 | 11 | 12 | class BaseViewController: UIViewController { 13 | var viewModel: T? 14 | } 15 | -------------------------------------------------------------------------------- /clean_architecture_mvvm_base/Application/Architecture/BaseViewModel.swift: -------------------------------------------------------------------------------- 1 | // 2 | // BaseViewController.swift 3 | // TestApp 4 | // 5 | // Created by Dani on 18/11/2021. 6 | // 7 | 8 | import Foundation 9 | import UIKit 10 | 11 | 12 | protocol BaseViewModelProtocol { 13 | associatedtype U 14 | } 15 | 16 | class BaseViewModel: BaseViewModelProtocol{ 17 | typealias U = UseCase 18 | 19 | var useCase: U? 20 | var showError: Bool = false 21 | var isLoading: Bool = false 22 | 23 | init(useCase: U) { 24 | self.useCase = useCase 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /clean_architecture_mvvm_base/Application/SceneDelegate.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SceneDelegate.swift 3 | // TestApp 4 | // 5 | // Created by Dani on 16/11/2021. 6 | // 7 | 8 | import UIKit 9 | 10 | class SceneDelegate: UIResponder, UIWindowSceneDelegate { 11 | 12 | var window: UIWindow? 13 | 14 | 15 | func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) { 16 | // Use this method to optionally configure and attach the UIWindow `window` to the provided UIWindowScene `scene`. 17 | // If using a storyboard, the `window` property will automatically be initialized and attached to the scene. 18 | // This delegate does not imply the connecting scene or session are new (see `application:configurationForConnectingSceneSession` instead). 19 | guard let _ = (scene as? UIWindowScene) else { return } 20 | } 21 | 22 | func sceneDidDisconnect(_ scene: UIScene) { 23 | // Called as the scene is being released by the system. 24 | // This occurs shortly after the scene enters the background, or when its session is discarded. 25 | // Release any resources associated with this scene that can be re-created the next time the scene connects. 26 | // The scene may re-connect later, as its session was not necessarily discarded (see `application:didDiscardSceneSessions` instead). 27 | } 28 | 29 | func sceneDidBecomeActive(_ scene: UIScene) { 30 | // Called when the scene has moved from an inactive state to an active state. 31 | // Use this method to restart any tasks that were paused (or not yet started) when the scene was inactive. 32 | } 33 | 34 | func sceneWillResignActive(_ scene: UIScene) { 35 | // Called when the scene will move from an active state to an inactive state. 36 | // This may occur due to temporary interruptions (ex. an incoming phone call). 37 | } 38 | 39 | func sceneWillEnterForeground(_ scene: UIScene) { 40 | // Called as the scene transitions from the background to the foreground. 41 | // Use this method to undo the changes made on entering the background. 42 | } 43 | 44 | func sceneDidEnterBackground(_ scene: UIScene) { 45 | // Called as the scene transitions from the foreground to the background. 46 | // Use this method to save data, release shared resources, and store enough scene-specific state information 47 | // to restore the scene back to its current state. 48 | } 49 | 50 | 51 | } 52 | 53 | -------------------------------------------------------------------------------- /clean_architecture_mvvm_base/Data/DataSource/CarTypeDataSource.swift: -------------------------------------------------------------------------------- 1 | // 2 | // CarTypeDataSource.swift 3 | // TestApp 4 | // 5 | // Created by Dani on 17/11/2021. 6 | // 7 | 8 | import Foundation 9 | import Alamofire 10 | 11 | ///This will be our generic data source 12 | ///each data source for car type should implement this data source and fulfill its functions implementation 13 | protocol CarTypeDataSource { 14 | 15 | func getManufacturer(manufacturerArgs: ManufacturerArgs, completion: @escaping (_ result: CarTypesResponse?, _ error: ErrorResponse?) -> Void) 16 | 17 | func getMainTypes(mainTypeArgs: MainTypeArgs, completion: @escaping (Response?, ErrorResponse?) -> Void) 18 | } 19 | -------------------------------------------------------------------------------- /clean_architecture_mvvm_base/Data/Models/Request/MainTypeRequest.swift: -------------------------------------------------------------------------------- 1 | // 2 | // MainTypeRequest.swift 3 | // TestApp 4 | // 5 | // Created by Dani on 18/11/2021. 6 | // 7 | 8 | import Foundation 9 | 10 | struct MainTypeArgs: PagingRequest, Codable { 11 | 12 | var page, pageSize: Int 13 | var manufacturer: String 14 | 15 | } 16 | -------------------------------------------------------------------------------- /clean_architecture_mvvm_base/Data/Models/Request/ManufacturerTypesRequest.swift: -------------------------------------------------------------------------------- 1 | // 2 | // CarTypesRequest.swift 3 | // TestApp 4 | // 5 | // Created by Dani on 16/11/2021. 6 | // 7 | 8 | import Foundation 9 | 10 | struct ManufacturerArgs: PagingRequest, Codable { 11 | 12 | var page, pageSize: Int 13 | } 14 | -------------------------------------------------------------------------------- /clean_architecture_mvvm_base/Data/Models/Request/PagingRequest.swift: -------------------------------------------------------------------------------- 1 | // 2 | // PagingRequest.swift 3 | // TestApp 4 | // 5 | // Created by Dani on 18/11/2021. 6 | // 7 | 8 | import Foundation 9 | 10 | protocol PagingRequest: Codable { 11 | 12 | var page: Int { get set} 13 | var pageSize: Int { get set} 14 | 15 | 16 | } 17 | -------------------------------------------------------------------------------- /clean_architecture_mvvm_base/Data/Models/Response/CarTypesResponse.swift: -------------------------------------------------------------------------------- 1 | // 2 | // CarTypes.swift 3 | // TestApp 4 | // 5 | // Created by Dani on 16/11/2021. 6 | // 7 | 8 | import Foundation 9 | 10 | 11 | struct CarTypesResponse: Response { 12 | var page, pageSize, totalPageCount: Int 13 | var wkda: [String: String] 14 | } 15 | 16 | -------------------------------------------------------------------------------- /clean_architecture_mvvm_base/Data/Models/Response/ErrorResponse.swift: -------------------------------------------------------------------------------- 1 | // 2 | // GenericResponse.swift 3 | // TestApp 4 | // 5 | // Created by Dani on 16/11/2021. 6 | // 7 | 8 | import Foundation 9 | 10 | ///will Hold the error response with suitable message 11 | struct ErrorResponse: Encodable, Error { 12 | 13 | let message: String 14 | 15 | init(message: String) { 16 | self.message = message 17 | } 18 | 19 | } 20 | -------------------------------------------------------------------------------- /clean_architecture_mvvm_base/Data/Remote/EndPoints.swift: -------------------------------------------------------------------------------- 1 | // 2 | // EndPoints.swift 3 | // TestApp 4 | // 5 | // Created by Dani on 17/11/2021. 6 | // 7 | 8 | import Foundation 9 | 10 | /// will be responsible for holding the end points name 11 | /// also will help to separate the endpoints by its controller/feature 12 | struct EndPoints { 13 | static let endPointVersion = "/v3" 14 | 15 | 16 | ///Holdinh all APIs for car types 17 | struct CartTypes { 18 | ///we should add the name of the controller here 19 | ///for now we don't have controller name 20 | static let endPointController = "/" 21 | 22 | static var manufacturer: String { get { return "\(endPointVersion)\(endPointController)17448d3b-d6cf-40f0-a580-dc000247f4ab" } } 23 | 24 | static var mainTypes: String { get { return "\(endPointVersion)\(endPointController)b7915703-4927-4336-8e1a-8a596b2db5f1" } } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /clean_architecture_mvvm_base/Data/Remote/Services/CarTypes.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Services.swift 3 | // TestApp 4 | // 5 | // Created by Dani on 16/11/2021. 6 | // 7 | 8 | import Foundation 9 | 10 | // MARK: - Networking call within this class will happen 11 | struct CarTypeRemoteService: CarTypeDataSource { 12 | 13 | func getMainTypes(mainTypeArgs: MainTypeArgs, completion: @escaping (Response?, ErrorResponse?) -> Void) { 14 | 15 | ///check the args if it's valid 16 | guard let params = mainTypeArgs.asDictionary() else { completion(nil, ErrorResponse(message: "args_error".localized)) 17 | return 18 | } 19 | 20 | /// here we create the event that represent the endpoint 21 | let event = API(path: EndPoints.CartTypes.mainTypes , 22 | method: .get, 23 | queryParameters: params 24 | ) 25 | 26 | ///here we call the request with our type and completion handler 27 | BaseNetworking.shared.request(CarTypesResponse.self, 28 | endPoint:event,completion: completion); 29 | } 30 | 31 | 32 | func getManufacturer(manufacturerArgs: ManufacturerArgs,completion: @escaping (_ result: CarTypesResponse?, _ error: ErrorResponse?) -> Void) { 33 | 34 | ///check the args if it's valid 35 | guard let params = manufacturerArgs.asDictionary() else { completion(nil, ErrorResponse(message: "args_error".localized)) 36 | return 37 | } 38 | 39 | /// here we create the event that represent the endpoint 40 | let event = API(path: EndPoints.CartTypes.manufacturer , 41 | method: .get, 42 | queryParameters: params 43 | ) 44 | 45 | ///here we call the request with our type and completion handler 46 | BaseNetworking.shared.request(CarTypesResponse.self, 47 | endPoint:event,completion: completion); 48 | 49 | } 50 | 51 | 52 | 53 | } 54 | -------------------------------------------------------------------------------- /clean_architecture_mvvm_base/Data/Repository/CarTypeRepositoryIMP.swift: -------------------------------------------------------------------------------- 1 | // 2 | // CarTypeRepositoryIMP.swift 3 | // TestApp 4 | // 5 | // Created by Dani on 17/11/2021. 6 | // 7 | 8 | import Foundation 9 | import Alamofire 10 | 11 | ///Thtis class will be reponsible for implementing the Repository from the Domain Layer 12 | final class CarTypeRepositoryIMPL { 13 | 14 | //this will represent the networking service for car type 15 | //we can have another one for local if we had local storage 16 | private let remoteDateSource: CarTypeDataSource 17 | 18 | init(remote: CarTypeDataSource) { 19 | self.remoteDateSource = remote 20 | } 21 | } 22 | 23 | extension CarTypeRepositoryIMPL: CarTypeRepository{ 24 | 25 | 26 | func getManufacturer(page: Int, completion: @escaping (_ result: Response?, _ error: ErrorResponse?) -> Void) { 27 | ///Here we can check for local or remote data source 28 | let args = ManufacturerArgs(page: page, pageSize: Constants.pageSize) 29 | remoteDateSource.getManufacturer(manufacturerArgs: args, completion: completion) 30 | } 31 | 32 | 33 | func getMainTypes(page: Int, manufacturerID: String, completion: @escaping (Response?, ErrorResponse?) -> Void) { 34 | ///Here we can check for local or remote data source 35 | let args = MainTypeArgs(page: page, pageSize: Constants.pageSize, manufacturer: manufacturerID) 36 | remoteDateSource.getMainTypes(mainTypeArgs: args, completion: completion) 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /clean_architecture_mvvm_base/Domain/Entities/MainTypes/MainTypeEntity.swift: -------------------------------------------------------------------------------- 1 | // 2 | // MainTypeEntity.swift 3 | // TestApp 4 | // 5 | // Created by Dani on 20/11/2021. 6 | // 7 | 8 | import Foundation 9 | 10 | 11 | struct MainTypeEntity:Codable { 12 | 13 | let name: String 14 | 15 | init(name:String){ 16 | self.name = name 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /clean_architecture_mvvm_base/Domain/Entities/MainTypes/MainTypesEntity.swift: -------------------------------------------------------------------------------- 1 | // 2 | // MainTypesEntity.swift 3 | // TestApp 4 | // 5 | // Created by Dani on 20/11/2021. 6 | // 7 | 8 | import Foundation 9 | 10 | struct MainTypesEntity:Response{ 11 | var mainTypes: [MainTypeEntity] 12 | 13 | var totalPageCount: Int 14 | 15 | init(mainTypes: [MainTypeEntity], totalPageCount: Int) { 16 | self.mainTypes = mainTypes 17 | self.totalPageCount = totalPageCount 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /clean_architecture_mvvm_base/Domain/Entities/Manufacturer/ManufacturerEntity.swift: -------------------------------------------------------------------------------- 1 | // 2 | // CarTypeEntity.swift 3 | // TestApp 4 | // 5 | // Created by Dani on 19/11/2021. 6 | // 7 | 8 | import Foundation 9 | 10 | struct ManufacturerEntity:Codable { 11 | let id: String 12 | let name: String 13 | 14 | init(id: String, name:String){ 15 | self.id = id 16 | self.name = name 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /clean_architecture_mvvm_base/Domain/Entities/Manufacturer/ManufacturersEntity.swift: -------------------------------------------------------------------------------- 1 | // 2 | // CarTypesEntity.swift 3 | // TestApp 4 | // 5 | // Created by Dani on 19/11/2021. 6 | // 7 | 8 | import Foundation 9 | 10 | struct ManufacturesEntity:Response{ 11 | var manufacturers: [ManufacturerEntity] 12 | 13 | var totalPageCount: Int 14 | 15 | init(manufacturers: [ManufacturerEntity], totalPageCount: Int) { 16 | self.manufacturers = manufacturers 17 | self.totalPageCount = totalPageCount 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /clean_architecture_mvvm_base/Domain/Entities/Response.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Response.swift 3 | // TestApp 4 | // 5 | // Created by Dani on 17/11/2021. 6 | // 7 | 8 | import Foundation 9 | 10 | //This will act like the base response object 11 | protocol Response: Codable{} 12 | -------------------------------------------------------------------------------- /clean_architecture_mvvm_base/Domain/Repository/CarTypeRepository.swift: -------------------------------------------------------------------------------- 1 | // 2 | // CarTypeRepository.swift 3 | // TestApp 4 | // 5 | // Created by Dani on 17/11/2021. 6 | // 7 | 8 | import Foundation 9 | 10 | // this will be our abstract class for defining car type repository 11 | protocol CarTypeRepository { 12 | 13 | //we should add the implementation for this 14 | //we don't know where it's going to fetch data local or remote 15 | //for now we only care for remote 16 | func getManufacturer(page: Int, completion: @escaping (_ result: Response?, _ error: ErrorResponse?) -> Void) 17 | 18 | func getMainTypes(page: Int, manufacturerID: String, completion: @escaping (_ result: Response?, _ error: ErrorResponse?) -> Void) 19 | } 20 | -------------------------------------------------------------------------------- /clean_architecture_mvvm_base/Domain/UseCase/CarType/CarTypeUseCase.swift: -------------------------------------------------------------------------------- 1 | // 2 | // CarTypeUseCase.swift 3 | // TestApp 4 | // 5 | // Created by Dani on 17/11/2021. 6 | // 7 | 8 | import Foundation 9 | import UIKit 10 | 11 | ///this will hold all the use cases that is related to this use case 12 | class CarTypeUseCase: UseCase { 13 | 14 | var fetchManufacturer: FetchManufacturerUseCase? 15 | var fetchMainTypes: FetchMainTypes? 16 | 17 | } 18 | -------------------------------------------------------------------------------- /clean_architecture_mvvm_base/Domain/UseCase/CarType/FetchMainTypes.swift: -------------------------------------------------------------------------------- 1 | // 2 | // FetchMainTypes.swift 3 | // TestApp 4 | // 5 | // Created by Dani on 17/11/2021. 6 | // 7 | 8 | import Foundation 9 | import Combine 10 | 11 | /// this will be responsible for doing one task only 12 | class FetchMainTypes: SingleUseCase { 13 | 14 | let repository: CarTypeRepository 15 | var pageNumber: Int 16 | let manufacturerID: String 17 | 18 | init(repository: CarTypeRepository, pageNumber: Int, manufacturerID: String ) { 19 | self.repository = repository 20 | self.pageNumber = pageNumber 21 | self.manufacturerID = manufacturerID 22 | } 23 | 24 | 25 | func start() -> Future { 26 | Future { [weak self] promise in 27 | self?.repository.getMainTypes(page: self?.pageNumber ?? 0, manufacturerID: self?.manufacturerID ?? "") { result, error in 28 | 29 | if result != nil { 30 | 31 | if let data = result as? CarTypesResponse { 32 | var mainTypes: [MainTypeEntity] = [] 33 | for (_, name) in data.wkda { 34 | mainTypes.append( MainTypeEntity(name: name)) 35 | } 36 | let mainTypesEntity = MainTypesEntity(mainTypes: mainTypes, totalPageCount: data.totalPageCount) 37 | 38 | promise(Result.success(mainTypesEntity)) 39 | } 40 | } 41 | else { 42 | promise(Result.failure(error!)) 43 | } 44 | } 45 | } 46 | } 47 | 48 | 49 | } 50 | -------------------------------------------------------------------------------- /clean_architecture_mvvm_base/Domain/UseCase/CarType/FetchManufacturer.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Manufacturer.swift 3 | // TestApp 4 | // 5 | // Created by Dani on 17/11/2021. 6 | // 7 | 8 | import Foundation 9 | import Combine 10 | 11 | 12 | /// this will be responsible for doing one task only 13 | class FetchManufacturerUseCase: SingleUseCase { 14 | 15 | let repository: CarTypeRepository 16 | var pageNumber: Int 17 | 18 | init(repository: CarTypeRepository, pageNumber: Int) { 19 | self.repository = repository 20 | self.pageNumber = pageNumber 21 | } 22 | 23 | 24 | func start() -> Future { 25 | Future { [weak self] promise in 26 | self?.repository.getManufacturer(page: self?.pageNumber ?? 0) { result, error in 27 | 28 | if result != nil { 29 | 30 | if let data = result as? CarTypesResponse { 31 | var manufacturers: [ManufacturerEntity] = [] 32 | for (id, name) in data.wkda { 33 | 34 | manufacturers.append( ManufacturerEntity(id: id, name: name)) 35 | } 36 | 37 | let manufacturersEntity = ManufacturesEntity(manufacturers: manufacturers, totalPageCount: data.totalPageCount) 38 | 39 | promise(Result.success(manufacturersEntity)) 40 | } 41 | 42 | } 43 | else { 44 | promise(Result.failure(error!)) 45 | } 46 | } 47 | } 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /clean_architecture_mvvm_base/Domain/UseCase/UseCase.swift: -------------------------------------------------------------------------------- 1 | // 2 | // UseCase.swift 3 | // TestApp 4 | // 5 | // Created by Dani on 17/11/2021. 6 | // 7 | 8 | import Foundation 9 | import Combine 10 | 11 | ///Abstraction layer to represent the top usecase for feature 12 | protocol UseCase { 13 | 14 | } 15 | 16 | 17 | ///this will be as our base use case for having all use cases fetch result function 18 | protocol SingleUseCase{ 19 | func start() -> Future 20 | } 21 | 22 | 23 | -------------------------------------------------------------------------------- /clean_architecture_mvvm_base/Extensions/CodableExtension.swift: -------------------------------------------------------------------------------- 1 | // 2 | // CodableExtension.swift 3 | // TestApp 4 | // 5 | // Created by Dani on 16/11/2021. 6 | // 7 | 8 | import Foundation 9 | 10 | // MARK: - extension on encodale to help building it 11 | extension Encodable { 12 | func asDictionary() -> [String: Any]? { 13 | let data = try? JSONEncoder().encode(self) 14 | if(data == nil) { 15 | return nil 16 | } 17 | guard let dictionary = try? JSONSerialization.jsonObject(with: data!, options: .allowFragments) as? [String: Any] else { 18 | return nil 19 | } 20 | return dictionary 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /clean_architecture_mvvm_base/Extensions/StringExtension.swift: -------------------------------------------------------------------------------- 1 | // 2 | // StringExtension.swift 3 | // TestApp 4 | // 5 | // Created by Dani on 16/11/2021. 6 | // 7 | import Foundation 8 | import UIKit 9 | 10 | // MARK: - Contains Helper functions that is regularly used on a String 11 | public extension String { 12 | 13 | /// check if sub string exist 14 | func contains(subString: String) -> Bool { 15 | return (self.range(of: subString) != nil) ? true : false 16 | } 17 | 18 | /// Get the width for string 19 | func widthOfString(usingFont font: UIFont) -> CGFloat { 20 | let fontAttributes = [NSAttributedString.Key.font: font] 21 | let size = self.size(withAttributes: fontAttributes) 22 | return size.width 23 | } 24 | 25 | /// Localized current string key 26 | var localized: String { 27 | return NSLocalizedString(self, comment: "") 28 | } 29 | 30 | /// Localized text using string key 31 | static func localized(_ key:String) -> String { 32 | return NSLocalizedString(key, comment: "") 33 | } 34 | 35 | /// Remove spaces and new lines 36 | var trimed :String { 37 | return self.trimmingCharacters(in: CharacterSet.whitespacesAndNewlines) 38 | } 39 | 40 | } 41 | 42 | -------------------------------------------------------------------------------- /clean_architecture_mvvm_base/Extensions/UITableViewExtension.swift: -------------------------------------------------------------------------------- 1 | // 2 | // UITableViewExtension.swift 3 | // TestApp 4 | // 5 | // Created by Dani on 20/11/2021. 6 | // 7 | 8 | import Foundation 9 | import UIKit 10 | 11 | extension UITableView { 12 | 13 | 14 | func addSpinner(){ 15 | let spinner = UIActivityIndicatorView(style: UIActivityIndicatorView.Style.medium) 16 | spinner.startAnimating() 17 | spinner.frame = CGRect(x: CGFloat(0), y: CGFloat(0), width: self.bounds.width, height: CGFloat(44)) 18 | 19 | self.tableFooterView = spinner 20 | } 21 | 22 | 23 | } 24 | -------------------------------------------------------------------------------- /clean_architecture_mvvm_base/Extensions/UIView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // UIView.swift 3 | // TestApp 4 | // 5 | // Created by Dani on 19/11/2021. 6 | // 7 | 8 | import Foundation 9 | import UIKit 10 | 11 | 12 | extension UIView { 13 | func showLoader() { 14 | let blurLoader = BlurLoader(frame: frame) 15 | blurLoader.alpha = 0 16 | self.addSubview(blurLoader) 17 | 18 | UIView.animate(withDuration: 0.25) { () -> Void in 19 | blurLoader.alpha = 1.0 20 | } 21 | } 22 | 23 | func hideLoader() { 24 | 25 | for subView in subviews { 26 | if(subView is BlurLoader) { 27 | UIView.animate(withDuration: 0.25) { () -> Void in 28 | subView.alpha = 0.0 29 | } 30 | if(self.layer.animationKeys()?.count == 0) { 31 | subView.removeFromSuperview() 32 | } 33 | } 34 | } 35 | 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /clean_architecture_mvvm_base/Extensions/UIViewControllerExtension.swift: -------------------------------------------------------------------------------- 1 | // 2 | // UIViewControllerExtension.swift 3 | // TestApp 4 | // 5 | // Created by Dani on 20/11/2021. 6 | // 7 | 8 | import Foundation 9 | import UIKit 10 | 11 | 12 | extension UIViewController { 13 | 14 | func showAlert(title: String, message: String, completion: @escaping (UIAlertAction) -> Void) { 15 | let alertController = UIAlertController(title: title, message: 16 | message, preferredStyle: .alert) 17 | 18 | alertController.addAction(UIAlertAction(title: "submit_button_title".localized, style: .default, handler: completion)) 19 | 20 | self.present(alertController, animated: true, completion: nil) 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /clean_architecture_mvvm_base/Infrastructure/Enums/Environment.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Environment.swift 3 | // TestApp 4 | // 5 | // Created by Dani on 16/11/2021. 6 | // 7 | import Foundation 8 | 9 | //This enum will be responsbile for shared info based on the scheme that we have 10 | // shared info can be: 11 | // API keys 12 | // URL 13 | // Third parties Keys 14 | public enum Environment { 15 | case test 16 | case live 17 | 18 | /// Application Base URL 19 | var baseURL: String { 20 | switch self { 21 | case .test: 22 | return "run.mocky.io" 23 | case .live: 24 | //for now we don't have any live url 25 | return "run.mocky.io" 26 | 27 | } 28 | } 29 | 30 | } 31 | 32 | -------------------------------------------------------------------------------- /clean_architecture_mvvm_base/Infrastructure/Enums/HTTPMethodType.swift: -------------------------------------------------------------------------------- 1 | // 2 | // HTTPMethodType.swift 3 | // TestApp 4 | // 5 | // Created by Dani on 16/11/2021. 6 | // 7 | 8 | import Foundation 9 | 10 | 11 | public enum HTTPMethodType: String { 12 | case get = "GET" 13 | case post = "POST" 14 | case put = "PUT" 15 | case patch = "PATCH" 16 | case delete = "DELETE" 17 | } 18 | -------------------------------------------------------------------------------- /clean_architecture_mvvm_base/Infrastructure/Networking/API.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Endpoint.swift 3 | // TestApp 4 | // 5 | // Created by Dani on 16/11/2021. 6 | // 7 | 8 | import Foundation 9 | import Alamofire 10 | 11 | 12 | // MARK: - API class will help to define the APIs in the remote networking when calling from the server 13 | public class API{ 14 | 15 | public let path: String 16 | public let method: HTTPMethod 17 | public let headerParamaters: [String: String] 18 | public let queryParameters: [String: Any] 19 | public let bodyParamaters: [String: Any] 20 | public let bodyEncoding: ParameterEncoding 21 | 22 | init(path: String, 23 | method: HTTPMethod, 24 | headerParamaters: [String: String] = [:], 25 | queryParameters: [String: Any] = [:], 26 | bodyParamatersEncodable: Encodable? = nil, 27 | bodyParamaters: [String: Any] = [:], 28 | bodyEncoding: ParameterEncoding = URLEncoding.default) { 29 | self.path = path 30 | self.method = method 31 | self.headerParamaters = headerParamaters 32 | self.queryParameters = queryParameters 33 | self.bodyParamaters = bodyParamaters 34 | self.bodyEncoding = bodyEncoding 35 | 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /clean_architecture_mvvm_base/Infrastructure/Networking/BaseNetworking.swift: -------------------------------------------------------------------------------- 1 | // 2 | // BaseNetworking.swift 3 | // TestApp 4 | // 5 | // Created by Dani on 16/11/2021. 6 | // 7 | 8 | import Foundation 9 | import Alamofire 10 | import Combine 11 | 12 | // MARK: - Generic base networking class that will send request to the server and parse the response 13 | class BaseNetworking { 14 | 15 | // MARK: Shared Instance 16 | public static let shared: BaseNetworking = BaseNetworking() 17 | 18 | 19 | ///To handle API calls and return the response 20 | func request(_ t: T.Type,endPoint: API, completion: @escaping (_ result: T?, _ error: ErrorResponse?) -> Void) { 21 | 22 | let url = buildRequestURL(endPoint: endPoint) 23 | 24 | if(url != nil) { 25 | ///herer we will call our API 26 | ///if it's sucess then return the object after parsing 27 | ///if it's failure then return error object 28 | AF.request(url!, 29 | method: endPoint.method, 30 | parameters: endPoint.bodyParamaters, 31 | encoding: endPoint.bodyEncoding 32 | ).validate().responseDecodable(of: T.self) 33 | { (response) in 34 | 35 | switch response.result { 36 | case .success(_): 37 | guard let response = response.value else 38 | { 39 | completion(nil, ErrorResponse(message: "parsing_error".localized)) 40 | return 41 | } 42 | print(response) 43 | completion(response, nil) 44 | 45 | case .failure(_): 46 | completion(nil, ErrorResponse(message: "server_error".localized)) 47 | 48 | } 49 | } 50 | 51 | }else { 52 | completion(nil, ErrorResponse(message: "url_error".localized)) 53 | } 54 | 55 | 56 | } 57 | 58 | 59 | /// in order to handle the get request with any query parameters 60 | private func buildRequestURL(endPoint: API) -> URL? { 61 | // var queryDictionary = endPoint.queryParameters 62 | 63 | let env = AppEnvironment.current 64 | 65 | 66 | var components = URLComponents() 67 | 68 | components.scheme = "https" 69 | components.host = env.baseURL 70 | components.path = endPoint.path 71 | 72 | ///if we have any query parameters we should add them here 73 | // components.queryItems = queryDictionary.map { 74 | // URLQueryItem(name: $0, value: "\($1)") 75 | // } 76 | return components.url 77 | } 78 | 79 | } 80 | 81 | -------------------------------------------------------------------------------- /clean_architecture_mvvm_base/Presentation/Base.lproj/Main.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | -------------------------------------------------------------------------------- /clean_architecture_mvvm_base/Presentation/CarTypes/Cell/ManufacturerCell.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ManufacturerCell.swift 3 | // TestApp 4 | // 5 | // Created by Dani on 19/11/2021. 6 | // 7 | 8 | import UIKit 9 | 10 | class ManufacturerCell: UITableViewCell { 11 | 12 | static let nibName = "ManufacturerCell" 13 | 14 | @IBOutlet weak var nameLabel: UILabel! 15 | 16 | override func awakeFromNib() { 17 | super.awakeFromNib() 18 | // Initialization code 19 | } 20 | 21 | override func setSelected(_ selected: Bool, animated: Bool) { 22 | super.setSelected(selected, animated: animated) 23 | 24 | // Configure the view for the selected state 25 | } 26 | 27 | func loadData(name: String, isEven: Bool){ 28 | nameLabel.text = name 29 | self.contentView.backgroundColor = isEven ? .systemGray4 : .systemGray6 30 | } 31 | 32 | } 33 | -------------------------------------------------------------------------------- /clean_architecture_mvvm_base/Presentation/CarTypes/Cell/ManufacturerCell.xib: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | -------------------------------------------------------------------------------- /clean_architecture_mvvm_base/Presentation/CarTypes/StoryBoard/CarTypesStoryBoard.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | -------------------------------------------------------------------------------- /clean_architecture_mvvm_base/Presentation/CarTypes/ViewController/MainTypes/MainTypesViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // MainTypesViewController.swift 3 | // TestApp 4 | // 5 | // Created by Dani on 20/11/2021. 6 | // 7 | 8 | import Foundation 9 | 10 | // 11 | // ManufacturerViewController.swift 12 | // TestApp 13 | // 14 | // Created by Dani on 18/11/2021. 15 | // 16 | 17 | import Foundation 18 | import UIKit 19 | import Combine 20 | 21 | class MainTypesViewController: BaseViewController { 22 | 23 | @IBOutlet weak var tableView: UITableView! { 24 | didSet { 25 | tableView.delegate = self 26 | tableView.dataSource = self 27 | tableView.addSpinner() 28 | } 29 | } 30 | 31 | var observers: [AnyCancellable] = [] 32 | 33 | override func viewDidLoad() { 34 | 35 | setupViewModelObserver() 36 | let manufacturerName = self.viewModel?.manufacturer?.name 37 | self.title = "\(manufacturerName ?? "") " + "main_types_screen_title".localized 38 | 39 | setupCells() 40 | 41 | super.viewDidLoad() 42 | } 43 | 44 | ///register cell within the table view 45 | func setupCells(){ 46 | tableView.register(UINib(nibName: ManufacturerCell.nibName, bundle: nil), forCellReuseIdentifier: ManufacturerCell.nibName) 47 | 48 | } 49 | 50 | 51 | 52 | ///this function will handle the response from combine and reflect the result on the UI 53 | func setupViewModelObserver() { 54 | 55 | if(viewModel != nil) { 56 | self.viewModel!.isLoading = true 57 | 58 | if(viewModel!.isLoading && viewModel!.pageNumber == 0) { 59 | view.showLoader() 60 | } 61 | 62 | self.viewModel!.fetchData()?.receive(on: DispatchQueue.main).sink( receiveCompletion: {[weak self] completion in 63 | 64 | self?.viewModel?.isLoading = false 65 | self?.view.hideLoader() 66 | self?.tableView.tableFooterView?.isHidden = true 67 | 68 | switch completion { 69 | case .finished: 70 | print("Finished calling)") 71 | case .failure(let error): 72 | print("Error calling \(error)") 73 | self?.showAlert(title: "error_title".localized, message: error.message, completion: {[weak self] action in 74 | self?.setupViewModelObserver() 75 | }) 76 | 77 | 78 | } 79 | }, receiveValue: {[weak self] response in 80 | print("Response Successfully parsed with \(String(describing: response))") 81 | let model = self?.viewModel 82 | if(response != nil) { 83 | model?.handleResponse(response: response!) 84 | 85 | } 86 | 87 | let pageNumber = model?.pageNumber ?? 0 88 | if(pageNumber > 0) { 89 | let newIndexPaths = self?.viewModel?.calculateIndexPathsToReload(from: (self?.viewModel!.currentCount)!) 90 | self?.onFetchCompleted(with: newIndexPaths) 91 | }else { 92 | self?.onFetchCompleted(with: .none) 93 | } 94 | }).store(in: &observers) 95 | } 96 | } 97 | 98 | func onFetchCompleted(with newIndexPathsToReload: [IndexPath]?) { 99 | // if we don't have anydata then reload all table and show the first result 100 | guard let newIndexPathsToReload = newIndexPathsToReload else { 101 | tableView.reloadData() 102 | return 103 | } 104 | // we avoid reloading all the table view 105 | self.tableView.insertRows(at: newIndexPathsToReload, with: .automatic) 106 | tableView.reloadRows(at: newIndexPathsToReload, with: .automatic) 107 | } 108 | } 109 | -------------------------------------------------------------------------------- /clean_architecture_mvvm_base/Presentation/CarTypes/ViewController/MainTypes/MainTypesViewControllerExtension.swift: -------------------------------------------------------------------------------- 1 | // 2 | // MainTypesViewControllerExtension.swift 3 | // TestApp 4 | // 5 | // Created by Dani on 20/11/2021. 6 | // 7 | 8 | import Foundation 9 | import UIKit 10 | 11 | extension MainTypesViewController: UITableViewDataSource, UITableViewDelegate{ 12 | 13 | func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { 14 | return viewModel?.currentCount ?? 0 15 | } 16 | 17 | func tableView(_ tableView: UITableView, willDisplay cell: UITableViewCell, forRowAt indexPath: IndexPath) { 18 | let currentCount = viewModel?.currentCount ?? 0 19 | if(indexPath.row == currentCount - 1) { 20 | if(viewModel!.shouldFetchNextPage) { 21 | viewModel!.pageNumber += 1 22 | setupViewModelObserver() 23 | self.tableView.tableFooterView?.isHidden = false 24 | } 25 | } 26 | } 27 | 28 | func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { 29 | if let cell = tableView.dequeueReusableCell(withIdentifier: ManufacturerCell.nibName) as? ManufacturerCell { 30 | cell.loadData(name: viewModel!.itemName(index: indexPath.row), isEven: indexPath.row % 2 == 0 ) 31 | return cell 32 | } 33 | return UITableViewCell() 34 | } 35 | 36 | func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { 37 | let model = viewModel?.itemAt(index: indexPath.row)?.name ?? "" 38 | let manufacturerName = viewModel?.manufacturer?.name ?? "" 39 | let message = "\(manufacturerName) , \(model)" 40 | 41 | self.showAlert(title: "show_type_alert_title".localized, message: message) { action in 42 | 43 | } 44 | } 45 | 46 | 47 | } 48 | 49 | -------------------------------------------------------------------------------- /clean_architecture_mvvm_base/Presentation/CarTypes/ViewController/MainTypes/MainTypesViewModel.swift: -------------------------------------------------------------------------------- 1 | // 2 | // MainTypesViewModel.swift 3 | // TestApp 4 | // 5 | // Created by Dani on 20/11/2021. 6 | // 7 | 8 | import Foundation 9 | import Combine 10 | 11 | ///The view model responsible for car main types view controller 12 | class MainTypesViewModel: BaseViewModel{ 13 | 14 | var pageNumber = 0 15 | var apiResponse: MainTypesEntity? 16 | var manufacturer: ManufacturerEntity? 17 | 18 | var totalPages: Int { 19 | get { 20 | return apiResponse?.totalPageCount ?? 0 21 | } 22 | } 23 | 24 | var currentCount: Int { 25 | get { 26 | return apiResponse?.mainTypes.count ?? 0 27 | } 28 | } 29 | 30 | var oldCount: Int = 0 31 | 32 | 33 | var shouldFetchNextPage: Bool { 34 | get { 35 | return !isLoading && pageNumber < totalPages 36 | } 37 | } 38 | 39 | init(manufacturer: ManufacturerEntity) { 40 | self.manufacturer = manufacturer 41 | let repository = CarTypeRepositoryIMPL(remote: CarTypeRemoteService()) 42 | 43 | let carTypeUseCase = CarTypeUseCase() 44 | ///we will force unrwape the id beacuse it's impossible not to have value 45 | carTypeUseCase.fetchMainTypes = FetchMainTypes(repository: repository,pageNumber: pageNumber, manufacturerID: manufacturer.id) 46 | 47 | super.init(useCase: carTypeUseCase) 48 | } 49 | 50 | init(useCase: CarTypeUseCase) { 51 | super.init(useCase: useCase) 52 | } 53 | 54 | 55 | 56 | func fetchData() -> Future? { 57 | let viewModelUseCase = self.useCase as! CarTypeUseCase 58 | 59 | self.isLoading = true 60 | self.showError = false 61 | 62 | viewModelUseCase.fetchMainTypes?.pageNumber = pageNumber 63 | return viewModelUseCase.fetchMainTypes?.start() 64 | } 65 | 66 | 67 | 68 | ///to avoid reloading all the data 69 | func calculateIndexPathsToReload(from newCount: Int ) -> [IndexPath] { 70 | return PresentationHelper.calculateIndexPathsToReload(from: newCount, oldCount: oldCount, section: 0) 71 | } 72 | 73 | 74 | func handleResponse(response: Response) { 75 | if(apiResponse == nil) { 76 | apiResponse = response as? MainTypesEntity 77 | } else { 78 | let mainTypeResponse = response as! MainTypesEntity 79 | oldCount = currentCount 80 | apiResponse?.mainTypes.append(contentsOf: mainTypeResponse.mainTypes) 81 | } 82 | } 83 | 84 | func itemName(index: Int) -> String { 85 | return apiResponse?.mainTypes[index].name ?? "" 86 | } 87 | 88 | func itemAt(index: Int) -> MainTypeEntity? { 89 | return apiResponse?.mainTypes[index] 90 | } 91 | 92 | } 93 | -------------------------------------------------------------------------------- /clean_architecture_mvvm_base/Presentation/CarTypes/ViewController/Manufacturer/ManufacturerViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ManufacturerViewController.swift 3 | // TestApp 4 | // 5 | // Created by Dani on 18/11/2021. 6 | // 7 | 8 | import Foundation 9 | import UIKit 10 | import Combine 11 | 12 | class ManufacturerViewController: BaseViewController { 13 | 14 | @IBOutlet weak var tableView: UITableView! { 15 | didSet { 16 | tableView.delegate = self 17 | tableView.dataSource = self 18 | tableView.addSpinner() 19 | } 20 | } 21 | 22 | var observers: [AnyCancellable] = [] 23 | 24 | override func viewDidLoad() { 25 | self.title = "manufacturer_screen_title".localized 26 | self.viewModel = ManufacturerViewModel() 27 | setupViewModelObserver() 28 | setupCells() 29 | 30 | super.viewDidLoad() 31 | } 32 | 33 | ///register cell within the table view 34 | func setupCells(){ 35 | tableView.register(UINib(nibName: ManufacturerCell.nibName, bundle: nil), forCellReuseIdentifier: ManufacturerCell.nibName) 36 | 37 | } 38 | 39 | 40 | ///this function will handle the response from combine and reflect the result on the UI 41 | func setupViewModelObserver() { 42 | if(viewModel != nil) { 43 | self.viewModel!.isLoading = true 44 | if(viewModel!.isLoading && viewModel!.pageNumber == 0) { 45 | view.showLoader() 46 | } 47 | 48 | self.viewModel!.fetchData()?.receive(on: DispatchQueue.main).sink( receiveCompletion: {[weak self] completion in 49 | self?.viewModel?.isLoading = false 50 | self?.view.hideLoader() 51 | self?.tableView.tableFooterView?.isHidden = true 52 | 53 | switch completion { 54 | case .finished: 55 | print("Finished calling)") 56 | case .failure(let error): 57 | print("Error calling \(error)") 58 | self?.showAlert(title: "error_title".localized, message: error.message, completion: {[weak self] action in 59 | self?.setupViewModelObserver() 60 | }) 61 | 62 | } 63 | }, receiveValue: {[weak self] response in 64 | print("Response Successfully parsed with \(String(describing: response))") 65 | let model = self?.viewModel 66 | if(response != nil) { 67 | model?.handleResponse(response: response!) 68 | 69 | } 70 | let pageNumber = model?.pageNumber ?? 0 71 | if(pageNumber > 0) { 72 | let newIndexPaths = self?.viewModel?.calculateIndexPathsToReload(from: (self?.viewModel!.currentCount)!) 73 | self?.onFetchCompleted(with: newIndexPaths) 74 | }else { 75 | self?.onFetchCompleted(with: .none) 76 | } 77 | }).store(in: &observers) 78 | } 79 | 80 | } 81 | 82 | func onFetchCompleted(with newIndexPathsToReload: [IndexPath]?) { 83 | // if we don't have anydata then reload all table and show the first result 84 | guard let newIndexPathsToReload = newIndexPathsToReload else { 85 | tableView.reloadData() 86 | return 87 | } 88 | // we avoid reloading all the table view 89 | self.tableView.insertRows(at: newIndexPathsToReload, with: .automatic) 90 | tableView.reloadRows(at: newIndexPathsToReload, with: .automatic) 91 | 92 | // tableView.reloadData() 93 | } 94 | } 95 | -------------------------------------------------------------------------------- /clean_architecture_mvvm_base/Presentation/CarTypes/ViewController/Manufacturer/ManufacturerViewControllerExtension.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ManufacturerViewControllerExtension.swift 3 | // TestApp 4 | // 5 | // Created by Dani on 20/11/2021. 6 | // 7 | 8 | import Foundation 9 | import UIKit 10 | 11 | extension ManufacturerViewController: UITableViewDataSource, UITableViewDelegate{ 12 | 13 | func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { 14 | return viewModel?.currentCount ?? 0 15 | } 16 | 17 | func tableView(_ tableView: UITableView, willDisplay cell: UITableViewCell, forRowAt indexPath: IndexPath) { 18 | let currentCount = viewModel?.currentCount ?? 0 19 | if(indexPath.row == currentCount - 1) { 20 | if(viewModel!.shouldFetchNextPage) { 21 | viewModel!.pageNumber += 1 22 | setupViewModelObserver() 23 | self.tableView.tableFooterView?.isHidden = false 24 | } 25 | } 26 | } 27 | 28 | func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { 29 | if let cell = tableView.dequeueReusableCell(withIdentifier: ManufacturerCell.nibName) as? ManufacturerCell { 30 | cell.loadData(name: viewModel!.itemName(index: indexPath.row), isEven: indexPath.row % 2 == 0 ) 31 | return cell 32 | } 33 | return UITableViewCell() 34 | } 35 | 36 | func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { 37 | self.performSegue(withIdentifier: "ShowMainTypesSegue", sender: self) 38 | } 39 | 40 | override func prepare(for segue: UIStoryboardSegue, sender: Any?) { 41 | if let nextScene = segue.destination as? MainTypesViewController { 42 | 43 | // Pass the selected object to the new view controller. 44 | if let indexPath = self.tableView.indexPathForSelectedRow { 45 | let selectedManufacturer = viewModel?.itemAt(index: indexPath.row) 46 | if(selectedManufacturer != nil) { 47 | nextScene.viewModel = MainTypesViewModel(manufacturer: selectedManufacturer!) 48 | } 49 | } 50 | } 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /clean_architecture_mvvm_base/Presentation/CarTypes/ViewController/Manufacturer/ManufacturerViewModel.swift: -------------------------------------------------------------------------------- 1 | // 2 | // CarTypeViewModel.swift 3 | // TestApp 4 | // 5 | // Created by Dani on 18/11/2021. 6 | // 7 | 8 | import Foundation 9 | import Combine 10 | 11 | ///The view model responsible for Manufacturere view controller 12 | class ManufacturerViewModel: BaseViewModel{ 13 | 14 | var pageNumber = 0 15 | var apiResponse: ManufacturesEntity? 16 | 17 | var totalPages: Int { 18 | get { 19 | return apiResponse?.totalPageCount ?? 0 20 | } 21 | } 22 | 23 | var currentCount: Int { 24 | get { 25 | return apiResponse?.manufacturers.count ?? 0 26 | } 27 | } 28 | 29 | var oldCount: Int = 0 30 | 31 | 32 | var shouldFetchNextPage: Bool { 33 | get { 34 | return !isLoading && pageNumber < totalPages 35 | } 36 | } 37 | 38 | init() { 39 | let repository = CarTypeRepositoryIMPL(remote: CarTypeRemoteService()) 40 | 41 | let carTypeUseCase = CarTypeUseCase() 42 | carTypeUseCase.fetchManufacturer = FetchManufacturerUseCase(repository: repository, pageNumber: pageNumber) 43 | 44 | super.init(useCase: carTypeUseCase) 45 | } 46 | 47 | init(useCase: CarTypeUseCase) { 48 | super.init(useCase: useCase) 49 | } 50 | 51 | func fetchData() -> Future? { 52 | let viewModelUseCase = self.useCase as! CarTypeUseCase 53 | 54 | self.isLoading = true 55 | self.showError = false 56 | 57 | viewModelUseCase.fetchManufacturer?.pageNumber = pageNumber 58 | return viewModelUseCase.fetchManufacturer?.start() 59 | } 60 | 61 | 62 | 63 | ///to avoid reloading all the data 64 | func calculateIndexPathsToReload(from newCount: Int ) -> [IndexPath] { 65 | 66 | return PresentationHelper.calculateIndexPathsToReload(from: newCount, oldCount: oldCount, section: 0) 67 | 68 | } 69 | 70 | 71 | func handleResponse(response: Response) { 72 | if(apiResponse == nil) { 73 | apiResponse = response as? ManufacturesEntity 74 | } else { 75 | let manufacturerResponse = response as! ManufacturesEntity 76 | 77 | oldCount = currentCount 78 | 79 | apiResponse?.manufacturers.append(contentsOf: manufacturerResponse.manufacturers) 80 | 81 | } 82 | 83 | } 84 | 85 | func itemName(index: Int) -> String { 86 | return apiResponse?.manufacturers[index].name ?? "" 87 | } 88 | 89 | 90 | func itemAt(index: Int) -> ManufacturerEntity? { 91 | return apiResponse?.manufacturers[index] 92 | } 93 | 94 | } 95 | -------------------------------------------------------------------------------- /clean_architecture_mvvm_base/Presentation/Helpers/PresentationHelper.swift: -------------------------------------------------------------------------------- 1 | // 2 | // PresentationHelpers.swift 3 | // TestApp 4 | // 5 | // Created by Dani on 20/11/2021. 6 | // 7 | 8 | import Foundation 9 | import UIKit 10 | 11 | 12 | struct PresentationHelper { 13 | 14 | ///to avoid reloading all the data 15 | static func calculateIndexPathsToReload(from newCount: Int, oldCount: Int, section: Int ) -> [IndexPath] { 16 | let newDataCount = newCount - oldCount 17 | let startIndex = newCount - newDataCount 18 | let endIndex = startIndex + newDataCount 19 | return (startIndex.. { 13 | 14 | 15 | override func viewDidLoad() { 16 | super.viewDidLoad() 17 | // Do any additional setup after loading the view. 18 | DispatchQueue.main.asyncAfter(deadline: .now() + 3){ 19 | self.performSegue(withIdentifier: "CarTypeStoryBoardSegue", sender: self) 20 | } 21 | } 22 | 23 | 24 | } 25 | 26 | -------------------------------------------------------------------------------- /clean_architecture_mvvm_base/Presentation/SplashScreenViewModel.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SplashScreenViewModel.swift 3 | // TestApp 4 | // 5 | // Created by Dani on 18/11/2021. 6 | // 7 | 8 | import Foundation 9 | 10 | 11 | class SplashScreenViewModel: BaseViewModel { 12 | 13 | 14 | } 15 | -------------------------------------------------------------------------------- /clean_architecture_mvvm_base/Resources/AppEnvironment.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AppEnvironment.swift 3 | // TestApp 4 | // 5 | // Created by Dani on 16/11/2021. 6 | // 7 | 8 | import Foundation 9 | 10 | // MARK: - App Environment class containing the current evn and helpers checks 11 | public class AppEnvironment { 12 | 13 | public static var current: Environment { 14 | 15 | let appSchema = Bundle.main.infoDictionary?["App Environment"] as? String 16 | switch appSchema { 17 | case ".test": 18 | return .test 19 | default:// Release 20 | return .live 21 | } 22 | } 23 | 24 | public static var isLive: Bool { 25 | 26 | AppEnvironment.current == Environment.live 27 | } 28 | 29 | public static var isTest: Bool { 30 | 31 | AppEnvironment.current == Environment.test 32 | } 33 | 34 | } 35 | -------------------------------------------------------------------------------- /clean_architecture_mvvm_base/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 | -------------------------------------------------------------------------------- /clean_architecture_mvvm_base/Resources/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "iphone", 5 | "scale" : "2x", 6 | "size" : "20x20" 7 | }, 8 | { 9 | "idiom" : "iphone", 10 | "scale" : "3x", 11 | "size" : "20x20" 12 | }, 13 | { 14 | "idiom" : "iphone", 15 | "scale" : "2x", 16 | "size" : "29x29" 17 | }, 18 | { 19 | "idiom" : "iphone", 20 | "scale" : "3x", 21 | "size" : "29x29" 22 | }, 23 | { 24 | "idiom" : "iphone", 25 | "scale" : "2x", 26 | "size" : "40x40" 27 | }, 28 | { 29 | "idiom" : "iphone", 30 | "scale" : "3x", 31 | "size" : "40x40" 32 | }, 33 | { 34 | "idiom" : "iphone", 35 | "scale" : "2x", 36 | "size" : "60x60" 37 | }, 38 | { 39 | "idiom" : "iphone", 40 | "scale" : "3x", 41 | "size" : "60x60" 42 | }, 43 | { 44 | "idiom" : "ipad", 45 | "scale" : "1x", 46 | "size" : "20x20" 47 | }, 48 | { 49 | "idiom" : "ipad", 50 | "scale" : "2x", 51 | "size" : "20x20" 52 | }, 53 | { 54 | "idiom" : "ipad", 55 | "scale" : "1x", 56 | "size" : "29x29" 57 | }, 58 | { 59 | "idiom" : "ipad", 60 | "scale" : "2x", 61 | "size" : "29x29" 62 | }, 63 | { 64 | "idiom" : "ipad", 65 | "scale" : "1x", 66 | "size" : "40x40" 67 | }, 68 | { 69 | "idiom" : "ipad", 70 | "scale" : "2x", 71 | "size" : "40x40" 72 | }, 73 | { 74 | "idiom" : "ipad", 75 | "scale" : "1x", 76 | "size" : "76x76" 77 | }, 78 | { 79 | "idiom" : "ipad", 80 | "scale" : "2x", 81 | "size" : "76x76" 82 | }, 83 | { 84 | "idiom" : "ipad", 85 | "scale" : "2x", 86 | "size" : "83.5x83.5" 87 | }, 88 | { 89 | "idiom" : "ios-marketing", 90 | "scale" : "1x", 91 | "size" : "1024x1024" 92 | } 93 | ], 94 | "info" : { 95 | "author" : "xcode", 96 | "version" : 1 97 | } 98 | } 99 | -------------------------------------------------------------------------------- /clean_architecture_mvvm_base/Resources/Assets.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "author" : "xcode", 4 | "version" : 1 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /clean_architecture_mvvm_base/Resources/Assets.xcassets/logo.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x" 6 | }, 7 | { 8 | "idiom" : "universal", 9 | "scale" : "2x" 10 | }, 11 | { 12 | "idiom" : "universal", 13 | "scale" : "3x" 14 | } 15 | ], 16 | "info" : { 17 | "author" : "xcode", 18 | "version" : 1 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /clean_architecture_mvvm_base/Resources/Constants.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Constants.swift 3 | // TestApp 4 | // 5 | // Created by Dani on 17/11/2021. 6 | // 7 | 8 | import Foundation 9 | 10 | struct Constants { 11 | 12 | static let pageSize = 15 13 | 14 | } 15 | 16 | -------------------------------------------------------------------------------- /clean_architecture_mvvm_base/Resources/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | App Environment 6 | ${APP_ENVIRONMENT_SUFFIX} 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 | UISceneStoryboardFile 21 | Main 22 | 23 | 24 | 25 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /clean_architecture_mvvm_base/Resources/UI/Base.lproj/LaunchScreen.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | -------------------------------------------------------------------------------- /clean_architecture_mvvm_base/Resources/UI/Loader.swift: -------------------------------------------------------------------------------- 1 | // 2 | // loader.swift 3 | // TestApp 4 | // 5 | // Created by Dani on 19/11/2021. 6 | // 7 | 8 | import Foundation 9 | import UIKit 10 | 11 | class BlurLoader: UIView { 12 | 13 | var blurEffectView: UIVisualEffectView? 14 | 15 | override init(frame: CGRect) { 16 | let blurEffect = UIBlurEffect(style: .dark) 17 | let blurEffectView = UIVisualEffectView(effect: blurEffect) 18 | blurEffectView.frame = frame 19 | blurEffectView.autoresizingMask = [.flexibleWidth, .flexibleHeight] 20 | self.blurEffectView = blurEffectView 21 | super.init(frame: frame) 22 | addSubview(blurEffectView) 23 | addLoader() 24 | } 25 | 26 | required init?(coder aDecoder: NSCoder) { 27 | fatalError("init(coder:) has not been implemented") 28 | } 29 | 30 | private func addLoader() { 31 | guard let blurEffectView = blurEffectView else { return } 32 | let activityIndicator = UIActivityIndicatorView() 33 | activityIndicator.style = UIActivityIndicatorView.Style.large 34 | activityIndicator.frame = CGRect(x: 0, y: 0, width: 50, height: 50) 35 | blurEffectView.contentView.addSubview(activityIndicator) 36 | activityIndicator.center = blurEffectView.contentView.center 37 | activityIndicator.startAnimating() 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /clean_architecture_mvvm_base/Resources/en.lproj/Localizable.strings: -------------------------------------------------------------------------------- 1 | /* 2 | Localizable.strings 3 | TestApp 4 | 5 | Created by Dani on 16/11/2021. 6 | 7 | */ 8 | 9 | "on_boarding_welcome" = "Welcome"; 10 | "parsing_error" = "Could not parse the object"; 11 | "url_error" = "There is somethign wrong with requested URL"; 12 | "server_error" = "Could not connect to the server, please try again"; 13 | "args_error" = "Could not parse the arguments for the endpoint"; 14 | "manufacturer_screen_title" = "Manufacturer"; 15 | "main_types_screen_title" = "Types"; 16 | "submit_button_title" = "Ok"; 17 | "show_type_alert_title" = "You have selected"; 18 | "error_title" = "Error"; 19 | -------------------------------------------------------------------------------- /clean_architecture_mvvm_baseTests/clean_architecture_mvvm_baseTests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // clean_architecture_mvvm_baseTests.swift 3 | // clean_architecture_mvvm_baseTests 4 | // 5 | // Created by Dani on 27/11/2021. 6 | // 7 | 8 | import XCTest 9 | @testable import clean_architecture_mvvm_base 10 | 11 | class clean_architecture_mvvm_baseTests: 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 | } 25 | 26 | func testPerformanceExample() throws { 27 | // This is an example of a performance test case. 28 | self.measure { 29 | // Put the code you want to measure the time of here. 30 | } 31 | } 32 | 33 | } 34 | -------------------------------------------------------------------------------- /clean_architecture_mvvm_baseUITests/clean_architecture_mvvm_baseUITests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // clean_architecture_mvvm_baseUITests.swift 3 | // clean_architecture_mvvm_baseUITests 4 | // 5 | // Created by Dani on 27/11/2021. 6 | // 7 | 8 | import XCTest 9 | 10 | class clean_architecture_mvvm_baseUITests: 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 recording to get started writing UI tests. 31 | // Use XCTAssert and related functions to verify your tests produce the correct results. 32 | } 33 | 34 | func testLaunchPerformance() throws { 35 | if #available(macOS 10.15, iOS 13.0, tvOS 13.0, watchOS 7.0, *) { 36 | // This measures how long it takes to launch your application. 37 | measure(metrics: [XCTApplicationLaunchMetric()]) { 38 | XCUIApplication().launch() 39 | } 40 | } 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /clean_architecture_mvvm_baseUITests/clean_architecture_mvvm_baseUITestsLaunchTests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // clean_architecture_mvvm_baseUITestsLaunchTests.swift 3 | // clean_architecture_mvvm_baseUITests 4 | // 5 | // Created by Dani on 27/11/2021. 6 | // 7 | 8 | import XCTest 9 | 10 | class clean_architecture_mvvm_baseUITestsLaunchTests: 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 | --------------------------------------------------------------------------------