├── .gitignore ├── Examples ├── ExampleProjectCocoapods │ ├── .sourcery-code.yml │ ├── .sourcery-mocks.yml │ ├── Podfile │ ├── Podfile.lock │ ├── Scripts │ │ └── codegen.sh │ ├── SwiftSourceryTemplates.xcworkspace │ │ ├── contents.xcworkspacedata │ │ └── xcshareddata │ │ │ └── IDEWorkspaceChecks.plist │ └── SwiftSourceryTemplates │ │ ├── SwiftSourceryTemplates.xcodeproj │ │ ├── project.pbxproj │ │ └── xcshareddata │ │ │ └── xcschemes │ │ │ └── SwiftSourceryTemplates.xcscheme │ │ ├── SwiftSourceryTemplates │ │ ├── AppDelegate.swift │ │ ├── Assets.xcassets │ │ │ ├── AppIcon.appiconset │ │ │ │ └── Contents.json │ │ │ └── Contents.json │ │ ├── Base.lproj │ │ │ └── MainMenu.xib │ │ ├── Generated │ │ │ └── TypeErase.generated.swift │ │ ├── Info.plist │ │ ├── Protocols │ │ │ └── Protocols.swift │ │ ├── SwiftSourceryTemplates.entitlements │ │ └── Types │ │ │ └── Types.swift │ │ └── SwiftSourceryTemplatesTests │ │ ├── Info.plist │ │ ├── Mocks │ │ └── Mocks.generated.swift │ │ └── SwiftSourceryTemplatesTests.swift └── ExampleProjectSpm │ ├── Package.resolved │ ├── Package.swift │ ├── Sources │ ├── ExampleProjectSpm │ │ ├── .Sourcery.TypeErase.yml │ │ ├── Protocols │ │ │ └── Protocols.swift │ │ └── Types │ │ │ └── Types.swift │ └── ExampleProjectSpmTests │ │ ├── .Sourcery.Mocks.yml │ │ ├── SourceryAnnotations │ │ └── RIBs+SourceryAnnotations.swift │ │ ├── SwiftSourceryTemplatesMocksSpec.swift │ │ └── SwiftSourceryTemplatesTypeErasureSpec.swift │ └── test-ios.sh ├── LICENSE.txt ├── Package.swift ├── Plugins └── SourcerySwiftCodegenPlugin │ └── SourcerySwiftCodegenPlugin.swift ├── README.md ├── SwiftMockTemplates.podspec ├── docs └── img │ ├── command_invocation_log.png │ └── sourcery_target_config.png └── templates ├── Mocks.swifttemplate ├── Mocks ├── MockGenerator.swift ├── MockMethod.swift ├── MockVar.swift ├── SourceCode.swift └── SourceryRuntimeExtensions.swift ├── TypeErase.swifttemplate ├── Utility ├── Annotations.swift ├── Generics.swift ├── String.swift └── StringLettercase.swift └── _header.swifttemplate /.gitignore: -------------------------------------------------------------------------------- 1 | Examples/ExampleProjectCocoapods/Pods 2 | xcuserdata 3 | .SourceryCache 4 | .build 5 | .swiftpm 6 | /Packages 7 | .netrc 8 | .DS_Store 9 | -------------------------------------------------------------------------------- /Examples/ExampleProjectCocoapods/.sourcery-code.yml: -------------------------------------------------------------------------------- 1 | # project: 2 | # file: SwiftSourceryTemplates/SwiftSourceryTemplates.xcodeproj 3 | # target: 4 | # name: SwiftSourceryTemplates 5 | # module: SwiftSourceryTemplates 6 | sources: 7 | - SwiftSourceryTemplates 8 | templates: 9 | - ../../templates/TypeErase.swifttemplate 10 | output: 11 | SwiftSourceryTemplates/SwiftSourceryTemplates/Generated 12 | args: 13 | import: 14 | - RxSwift 15 | excludedSwiftLintRules: 16 | - function_body_length 17 | - line_length 18 | - vertical_whitespace 19 | cacheBasePath: 20 | .SourceryCache 21 | -------------------------------------------------------------------------------- /Examples/ExampleProjectCocoapods/.sourcery-mocks.yml: -------------------------------------------------------------------------------- 1 | project: 2 | file: SwiftSourceryTemplates/SwiftSourceryTemplates.xcodeproj 3 | target: 4 | name: SwiftSourceryTemplates 5 | module: SwiftSourceryTemplates 6 | templates: 7 | - ../../templates/Mocks.swifttemplate 8 | output: 9 | SwiftSourceryTemplates/SwiftSourceryTemplatesTests/Mocks 10 | args: 11 | testable: 12 | - SwiftSourceryTemplates 13 | import: 14 | - RxSwift 15 | - RxBlocking 16 | - RxTest 17 | - Alamofire 18 | excludedSwiftLintRules: 19 | - force_cast 20 | - function_body_length 21 | - line_length 22 | - vertical_whitespace 23 | -------------------------------------------------------------------------------- /Examples/ExampleProjectCocoapods/Podfile: -------------------------------------------------------------------------------- 1 | source 'https://cdn.cocoapods.org/' 2 | 3 | workspace 'SwiftSourceryTemplates.xcworkspace' 4 | 5 | target 'SwiftSourceryTemplates' do 6 | platform :osx, '10.13' 7 | use_frameworks! 8 | project 'SwiftSourceryTemplates/SwiftSourceryTemplates.xcodeproj' 9 | 10 | pod 'Alamofire', '~> 4.0' 11 | pod 'RxSwift' 12 | pod 'Sourcery', :git => 'https://github.com/krzysztofzablocki/Sourcery', :tag => '2.1.2' 13 | 14 | target 'SwiftSourceryTemplatesTests' do 15 | platform :osx, '10.13' 16 | inherit! :search_paths 17 | 18 | pod 'RxBlocking' 19 | pod 'RxTest' 20 | end 21 | end 22 | -------------------------------------------------------------------------------- /Examples/ExampleProjectCocoapods/Podfile.lock: -------------------------------------------------------------------------------- 1 | PODS: 2 | - Alamofire (4.9.1) 3 | - RxBlocking (6.6.0): 4 | - RxSwift (= 6.6.0) 5 | - RxSwift (6.6.0) 6 | - RxTest (6.6.0): 7 | - RxSwift (= 6.6.0) 8 | - Sourcery (2.1.2): 9 | - Sourcery/CLI-Only (= 2.1.2) 10 | - Sourcery/CLI-Only (2.1.2) 11 | 12 | DEPENDENCIES: 13 | - Alamofire (~> 4.0) 14 | - RxBlocking 15 | - RxSwift 16 | - RxTest 17 | - Sourcery (from `https://github.com/krzysztofzablocki/Sourcery`, tag `2.1.2`) 18 | 19 | SPEC REPOS: 20 | trunk: 21 | - Alamofire 22 | - RxBlocking 23 | - RxSwift 24 | - RxTest 25 | 26 | EXTERNAL SOURCES: 27 | Sourcery: 28 | :git: https://github.com/krzysztofzablocki/Sourcery 29 | :tag: 2.1.2 30 | 31 | CHECKOUT OPTIONS: 32 | Sourcery: 33 | :git: https://github.com/krzysztofzablocki/Sourcery 34 | :tag: 2.1.2 35 | 36 | SPEC CHECKSUMS: 37 | Alamofire: 85e8a02c69d6020a0d734f6054870d7ecb75cf18 38 | RxBlocking: fbd1f8501443024f686e556f36dac79b0d5f4d7c 39 | RxSwift: a4b44f7d24599f674deebd1818eab82e58410632 40 | RxTest: a23f26bb53a5e146a0a69db4f0fa0b69001ce7f4 41 | Sourcery: 9781f42e4e309d6693bee583d429b03967e12ed7 42 | 43 | PODFILE CHECKSUM: aa3a3439ee8b82f8455057331e92ae3bf7b8ccab 44 | 45 | COCOAPODS: 1.14.2 46 | -------------------------------------------------------------------------------- /Examples/ExampleProjectCocoapods/Scripts/codegen.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # the directory of the script. all locations are relative to the $DIR 4 | DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" 5 | PARENT_DIR="$DIR/.." 6 | CACHES_DIR="$PARENT_DIR/.SourceryCache" 7 | 8 | if [ "$1" == "--disableCache" ]; then 9 | echo "🧹 Cleaning caches dir..." 10 | rm -rf "$CACHES_DIR" 11 | fi 12 | 13 | SOURCERY_DIR="$PARENT_DIR/Pods/Sourcery" 14 | SOURCERY="$SOURCERY_DIR/bin/sourcery" 15 | 16 | echo "⚡️ Running codegen..." 17 | "$SOURCERY" --config "$PARENT_DIR"/.sourcery-code.yml $1 $2 18 | "$SOURCERY" --config "$PARENT_DIR"/.sourcery-mocks.yml $1 $2 19 | 20 | echo "✅ Dones" 21 | -------------------------------------------------------------------------------- /Examples/ExampleProjectCocoapods/SwiftSourceryTemplates.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /Examples/ExampleProjectCocoapods/SwiftSourceryTemplates.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /Examples/ExampleProjectCocoapods/SwiftSourceryTemplates/SwiftSourceryTemplates.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 54; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 140E6F6920FF6627008418F1 /* Protocols.swift in Sources */ = {isa = PBXBuildFile; fileRef = 140E6F6820FF6627008418F1 /* Protocols.swift */; }; 11 | 140E6F6C20FF6661008418F1 /* Types.swift in Sources */ = {isa = PBXBuildFile; fileRef = 140E6F6B20FF6661008418F1 /* Types.swift */; }; 12 | 1415F10220FF583700CBE106 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1415F10120FF583700CBE106 /* AppDelegate.swift */; }; 13 | 1415F10420FF583800CBE106 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 1415F10320FF583800CBE106 /* Assets.xcassets */; }; 14 | 1415F10720FF583800CBE106 /* MainMenu.xib in Resources */ = {isa = PBXBuildFile; fileRef = 1415F10520FF583800CBE106 /* MainMenu.xib */; }; 15 | 1415F11320FF583800CBE106 /* SwiftSourceryTemplatesTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1415F11220FF583800CBE106 /* SwiftSourceryTemplatesTests.swift */; }; 16 | 1415F11F20FF5AF400CBE106 /* TypeErase.generated.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1415F11E20FF5AF400CBE106 /* TypeErase.generated.swift */; }; 17 | 143CDB632158DA5F00C345B1 /* Mocks.generated.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1415F12120FF5AFB00CBE106 /* Mocks.generated.swift */; }; 18 | A3D0FA61B99D54EF2239F9E7 /* Pods_SwiftSourceryTemplates.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 1E5046829ACFBDBE51FCC68D /* Pods_SwiftSourceryTemplates.framework */; }; 19 | A51399099FC204276D34F886 /* Pods_SwiftSourceryTemplatesTests.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = E91253CD4368FBF69C7FFC00 /* Pods_SwiftSourceryTemplatesTests.framework */; }; 20 | /* End PBXBuildFile section */ 21 | 22 | /* Begin PBXContainerItemProxy section */ 23 | 1415F10F20FF583800CBE106 /* PBXContainerItemProxy */ = { 24 | isa = PBXContainerItemProxy; 25 | containerPortal = 1415F0F620FF583700CBE106 /* Project object */; 26 | proxyType = 1; 27 | remoteGlobalIDString = 1415F0FD20FF583700CBE106; 28 | remoteInfo = SwiftSourceryTemplates; 29 | }; 30 | /* End PBXContainerItemProxy section */ 31 | 32 | /* Begin PBXFileReference section */ 33 | 140E6F6820FF6627008418F1 /* Protocols.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Protocols.swift; sourceTree = ""; }; 34 | 140E6F6B20FF6661008418F1 /* Types.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Types.swift; sourceTree = ""; }; 35 | 1415F0FE20FF583700CBE106 /* SwiftSourceryTemplates.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = SwiftSourceryTemplates.app; sourceTree = BUILT_PRODUCTS_DIR; }; 36 | 1415F10120FF583700CBE106 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; 37 | 1415F10320FF583800CBE106 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 38 | 1415F10620FF583800CBE106 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Base.lproj/MainMenu.xib; sourceTree = ""; }; 39 | 1415F10820FF583800CBE106 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 40 | 1415F10920FF583800CBE106 /* SwiftSourceryTemplates.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = SwiftSourceryTemplates.entitlements; sourceTree = ""; }; 41 | 1415F10E20FF583800CBE106 /* SwiftSourceryTemplatesTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = SwiftSourceryTemplatesTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 42 | 1415F11220FF583800CBE106 /* SwiftSourceryTemplatesTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SwiftSourceryTemplatesTests.swift; sourceTree = ""; }; 43 | 1415F11420FF583800CBE106 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 44 | 1415F11E20FF5AF400CBE106 /* TypeErase.generated.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TypeErase.generated.swift; sourceTree = ""; }; 45 | 1415F12120FF5AFB00CBE106 /* Mocks.generated.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Mocks.generated.swift; sourceTree = ""; }; 46 | 1E5046829ACFBDBE51FCC68D /* Pods_SwiftSourceryTemplates.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_SwiftSourceryTemplates.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 47 | 26A5D53F86280C8D6399AFAB /* Pods-SwiftSourceryTemplates.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-SwiftSourceryTemplates.release.xcconfig"; path = "../Pods/Target Support Files/Pods-SwiftSourceryTemplates/Pods-SwiftSourceryTemplates.release.xcconfig"; sourceTree = ""; }; 48 | 62BCC70E6F64ED26E8F1AFAD /* Pods-SwiftSourceryTemplatesTests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-SwiftSourceryTemplatesTests.release.xcconfig"; path = "../Pods/Target Support Files/Pods-SwiftSourceryTemplatesTests/Pods-SwiftSourceryTemplatesTests.release.xcconfig"; sourceTree = ""; }; 49 | 90AA27DA09BD2D0CA17E26F1 /* Pods-SwiftSourceryTemplatesTests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-SwiftSourceryTemplatesTests.debug.xcconfig"; path = "../Pods/Target Support Files/Pods-SwiftSourceryTemplatesTests/Pods-SwiftSourceryTemplatesTests.debug.xcconfig"; sourceTree = ""; }; 50 | B01DD88ED4CE6A3558671FD7 /* Pods-SwiftSourceryTemplates.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-SwiftSourceryTemplates.debug.xcconfig"; path = "../Pods/Target Support Files/Pods-SwiftSourceryTemplates/Pods-SwiftSourceryTemplates.debug.xcconfig"; sourceTree = ""; }; 51 | E91253CD4368FBF69C7FFC00 /* Pods_SwiftSourceryTemplatesTests.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_SwiftSourceryTemplatesTests.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 52 | /* End PBXFileReference section */ 53 | 54 | /* Begin PBXFrameworksBuildPhase section */ 55 | 1415F0FB20FF583700CBE106 /* Frameworks */ = { 56 | isa = PBXFrameworksBuildPhase; 57 | buildActionMask = 2147483647; 58 | files = ( 59 | A3D0FA61B99D54EF2239F9E7 /* Pods_SwiftSourceryTemplates.framework in Frameworks */, 60 | ); 61 | runOnlyForDeploymentPostprocessing = 0; 62 | }; 63 | 1415F10B20FF583800CBE106 /* Frameworks */ = { 64 | isa = PBXFrameworksBuildPhase; 65 | buildActionMask = 2147483647; 66 | files = ( 67 | A51399099FC204276D34F886 /* Pods_SwiftSourceryTemplatesTests.framework in Frameworks */, 68 | ); 69 | runOnlyForDeploymentPostprocessing = 0; 70 | }; 71 | /* End PBXFrameworksBuildPhase section */ 72 | 73 | /* Begin PBXGroup section */ 74 | 140E6F5420FF65EA008418F1 /* Protocols */ = { 75 | isa = PBXGroup; 76 | children = ( 77 | 140E6F6820FF6627008418F1 /* Protocols.swift */, 78 | ); 79 | path = Protocols; 80 | sourceTree = ""; 81 | }; 82 | 140E6F6A20FF6656008418F1 /* Types */ = { 83 | isa = PBXGroup; 84 | children = ( 85 | 140E6F6B20FF6661008418F1 /* Types.swift */, 86 | ); 87 | path = Types; 88 | sourceTree = ""; 89 | }; 90 | 1415F0F520FF583700CBE106 = { 91 | isa = PBXGroup; 92 | children = ( 93 | 1415F10020FF583700CBE106 /* SwiftSourceryTemplates */, 94 | 1415F11120FF583800CBE106 /* SwiftSourceryTemplatesTests */, 95 | 1415F0FF20FF583700CBE106 /* Products */, 96 | 45EB43FE9FD178FF8E3A4B23 /* Pods */, 97 | 407E93FED513C2A668882DD9 /* Frameworks */, 98 | ); 99 | sourceTree = ""; 100 | }; 101 | 1415F0FF20FF583700CBE106 /* Products */ = { 102 | isa = PBXGroup; 103 | children = ( 104 | 1415F0FE20FF583700CBE106 /* SwiftSourceryTemplates.app */, 105 | 1415F10E20FF583800CBE106 /* SwiftSourceryTemplatesTests.xctest */, 106 | ); 107 | name = Products; 108 | sourceTree = ""; 109 | }; 110 | 1415F10020FF583700CBE106 /* SwiftSourceryTemplates */ = { 111 | isa = PBXGroup; 112 | children = ( 113 | 140E6F6A20FF6656008418F1 /* Types */, 114 | 140E6F5420FF65EA008418F1 /* Protocols */, 115 | 1415F11D20FF5AF400CBE106 /* Generated */, 116 | 1415F10120FF583700CBE106 /* AppDelegate.swift */, 117 | 1415F10320FF583800CBE106 /* Assets.xcassets */, 118 | 1415F10520FF583800CBE106 /* MainMenu.xib */, 119 | 1415F10820FF583800CBE106 /* Info.plist */, 120 | 1415F10920FF583800CBE106 /* SwiftSourceryTemplates.entitlements */, 121 | ); 122 | path = SwiftSourceryTemplates; 123 | sourceTree = ""; 124 | }; 125 | 1415F11120FF583800CBE106 /* SwiftSourceryTemplatesTests */ = { 126 | isa = PBXGroup; 127 | children = ( 128 | 1415F12020FF5AFB00CBE106 /* Mocks */, 129 | 1415F11220FF583800CBE106 /* SwiftSourceryTemplatesTests.swift */, 130 | 1415F11420FF583800CBE106 /* Info.plist */, 131 | ); 132 | path = SwiftSourceryTemplatesTests; 133 | sourceTree = ""; 134 | }; 135 | 1415F11D20FF5AF400CBE106 /* Generated */ = { 136 | isa = PBXGroup; 137 | children = ( 138 | 1415F11E20FF5AF400CBE106 /* TypeErase.generated.swift */, 139 | ); 140 | path = Generated; 141 | sourceTree = ""; 142 | }; 143 | 1415F12020FF5AFB00CBE106 /* Mocks */ = { 144 | isa = PBXGroup; 145 | children = ( 146 | 1415F12120FF5AFB00CBE106 /* Mocks.generated.swift */, 147 | ); 148 | path = Mocks; 149 | sourceTree = ""; 150 | }; 151 | 407E93FED513C2A668882DD9 /* Frameworks */ = { 152 | isa = PBXGroup; 153 | children = ( 154 | 1E5046829ACFBDBE51FCC68D /* Pods_SwiftSourceryTemplates.framework */, 155 | E91253CD4368FBF69C7FFC00 /* Pods_SwiftSourceryTemplatesTests.framework */, 156 | ); 157 | name = Frameworks; 158 | sourceTree = ""; 159 | }; 160 | 45EB43FE9FD178FF8E3A4B23 /* Pods */ = { 161 | isa = PBXGroup; 162 | children = ( 163 | B01DD88ED4CE6A3558671FD7 /* Pods-SwiftSourceryTemplates.debug.xcconfig */, 164 | 26A5D53F86280C8D6399AFAB /* Pods-SwiftSourceryTemplates.release.xcconfig */, 165 | 90AA27DA09BD2D0CA17E26F1 /* Pods-SwiftSourceryTemplatesTests.debug.xcconfig */, 166 | 62BCC70E6F64ED26E8F1AFAD /* Pods-SwiftSourceryTemplatesTests.release.xcconfig */, 167 | ); 168 | name = Pods; 169 | sourceTree = ""; 170 | }; 171 | /* End PBXGroup section */ 172 | 173 | /* Begin PBXNativeTarget section */ 174 | 1415F0FD20FF583700CBE106 /* SwiftSourceryTemplates */ = { 175 | isa = PBXNativeTarget; 176 | buildConfigurationList = 1415F11720FF583800CBE106 /* Build configuration list for PBXNativeTarget "SwiftSourceryTemplates" */; 177 | buildPhases = ( 178 | B7101ABADB70F626E71B10AB /* [CP] Check Pods Manifest.lock */, 179 | 1415F0FA20FF583700CBE106 /* Sources */, 180 | 1415F0FB20FF583700CBE106 /* Frameworks */, 181 | 1415F0FC20FF583700CBE106 /* Resources */, 182 | 5456F0F9C63A052288CF7334 /* [CP] Embed Pods Frameworks */, 183 | ); 184 | buildRules = ( 185 | ); 186 | dependencies = ( 187 | ); 188 | name = SwiftSourceryTemplates; 189 | productName = SwiftSourceryTemplates; 190 | productReference = 1415F0FE20FF583700CBE106 /* SwiftSourceryTemplates.app */; 191 | productType = "com.apple.product-type.application"; 192 | }; 193 | 1415F10D20FF583800CBE106 /* SwiftSourceryTemplatesTests */ = { 194 | isa = PBXNativeTarget; 195 | buildConfigurationList = 1415F11A20FF583800CBE106 /* Build configuration list for PBXNativeTarget "SwiftSourceryTemplatesTests" */; 196 | buildPhases = ( 197 | 8ECDDDC3BBE038601E2CF7DF /* [CP] Check Pods Manifest.lock */, 198 | 1415F10A20FF583800CBE106 /* Sources */, 199 | 1415F10B20FF583800CBE106 /* Frameworks */, 200 | 1415F10C20FF583800CBE106 /* Resources */, 201 | 5F818F4541ED8BA185970C8C /* [CP] Embed Pods Frameworks */, 202 | ); 203 | buildRules = ( 204 | ); 205 | dependencies = ( 206 | 1415F11020FF583800CBE106 /* PBXTargetDependency */, 207 | ); 208 | name = SwiftSourceryTemplatesTests; 209 | productName = SwiftSourceryTemplatesTests; 210 | productReference = 1415F10E20FF583800CBE106 /* SwiftSourceryTemplatesTests.xctest */; 211 | productType = "com.apple.product-type.bundle.unit-test"; 212 | }; 213 | /* End PBXNativeTarget section */ 214 | 215 | /* Begin PBXProject section */ 216 | 1415F0F620FF583700CBE106 /* Project object */ = { 217 | isa = PBXProject; 218 | attributes = { 219 | LastSwiftUpdateCheck = 0940; 220 | LastUpgradeCheck = 0940; 221 | ORGANIZATIONNAME = "AlbumPrinter BV"; 222 | TargetAttributes = { 223 | 1415F0FD20FF583700CBE106 = { 224 | CreatedOnToolsVersion = 9.4.1; 225 | LastSwiftMigration = 1020; 226 | }; 227 | 1415F10D20FF583800CBE106 = { 228 | CreatedOnToolsVersion = 9.4.1; 229 | LastSwiftMigration = 1020; 230 | TestTargetID = 1415F0FD20FF583700CBE106; 231 | }; 232 | }; 233 | }; 234 | buildConfigurationList = 1415F0F920FF583700CBE106 /* Build configuration list for PBXProject "SwiftSourceryTemplates" */; 235 | compatibilityVersion = "Xcode 9.3"; 236 | developmentRegion = en; 237 | hasScannedForEncodings = 0; 238 | knownRegions = ( 239 | en, 240 | Base, 241 | ); 242 | mainGroup = 1415F0F520FF583700CBE106; 243 | productRefGroup = 1415F0FF20FF583700CBE106 /* Products */; 244 | projectDirPath = ""; 245 | projectRoot = ""; 246 | targets = ( 247 | 1415F0FD20FF583700CBE106 /* SwiftSourceryTemplates */, 248 | 1415F10D20FF583800CBE106 /* SwiftSourceryTemplatesTests */, 249 | ); 250 | }; 251 | /* End PBXProject section */ 252 | 253 | /* Begin PBXResourcesBuildPhase section */ 254 | 1415F0FC20FF583700CBE106 /* Resources */ = { 255 | isa = PBXResourcesBuildPhase; 256 | buildActionMask = 2147483647; 257 | files = ( 258 | 1415F10420FF583800CBE106 /* Assets.xcassets in Resources */, 259 | 1415F10720FF583800CBE106 /* MainMenu.xib in Resources */, 260 | ); 261 | runOnlyForDeploymentPostprocessing = 0; 262 | }; 263 | 1415F10C20FF583800CBE106 /* Resources */ = { 264 | isa = PBXResourcesBuildPhase; 265 | buildActionMask = 2147483647; 266 | files = ( 267 | ); 268 | runOnlyForDeploymentPostprocessing = 0; 269 | }; 270 | /* End PBXResourcesBuildPhase section */ 271 | 272 | /* Begin PBXShellScriptBuildPhase section */ 273 | 5456F0F9C63A052288CF7334 /* [CP] Embed Pods Frameworks */ = { 274 | isa = PBXShellScriptBuildPhase; 275 | buildActionMask = 2147483647; 276 | files = ( 277 | ); 278 | inputFileListPaths = ( 279 | "${PODS_ROOT}/Target Support Files/Pods-SwiftSourceryTemplates/Pods-SwiftSourceryTemplates-frameworks-${CONFIGURATION}-input-files.xcfilelist", 280 | ); 281 | name = "[CP] Embed Pods Frameworks"; 282 | outputFileListPaths = ( 283 | "${PODS_ROOT}/Target Support Files/Pods-SwiftSourceryTemplates/Pods-SwiftSourceryTemplates-frameworks-${CONFIGURATION}-output-files.xcfilelist", 284 | ); 285 | runOnlyForDeploymentPostprocessing = 0; 286 | shellPath = /bin/sh; 287 | shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-SwiftSourceryTemplates/Pods-SwiftSourceryTemplates-frameworks.sh\"\n"; 288 | showEnvVarsInLog = 0; 289 | }; 290 | 5F818F4541ED8BA185970C8C /* [CP] Embed Pods Frameworks */ = { 291 | isa = PBXShellScriptBuildPhase; 292 | buildActionMask = 2147483647; 293 | files = ( 294 | ); 295 | inputFileListPaths = ( 296 | "${PODS_ROOT}/Target Support Files/Pods-SwiftSourceryTemplatesTests/Pods-SwiftSourceryTemplatesTests-frameworks-${CONFIGURATION}-input-files.xcfilelist", 297 | ); 298 | name = "[CP] Embed Pods Frameworks"; 299 | outputFileListPaths = ( 300 | "${PODS_ROOT}/Target Support Files/Pods-SwiftSourceryTemplatesTests/Pods-SwiftSourceryTemplatesTests-frameworks-${CONFIGURATION}-output-files.xcfilelist", 301 | ); 302 | runOnlyForDeploymentPostprocessing = 0; 303 | shellPath = /bin/sh; 304 | shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-SwiftSourceryTemplatesTests/Pods-SwiftSourceryTemplatesTests-frameworks.sh\"\n"; 305 | showEnvVarsInLog = 0; 306 | }; 307 | 8ECDDDC3BBE038601E2CF7DF /* [CP] Check Pods Manifest.lock */ = { 308 | isa = PBXShellScriptBuildPhase; 309 | buildActionMask = 2147483647; 310 | files = ( 311 | ); 312 | inputPaths = ( 313 | "${PODS_PODFILE_DIR_PATH}/Podfile.lock", 314 | "${PODS_ROOT}/Manifest.lock", 315 | ); 316 | name = "[CP] Check Pods Manifest.lock"; 317 | outputPaths = ( 318 | "$(DERIVED_FILE_DIR)/Pods-SwiftSourceryTemplatesTests-checkManifestLockResult.txt", 319 | ); 320 | runOnlyForDeploymentPostprocessing = 0; 321 | shellPath = /bin/sh; 322 | shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; 323 | showEnvVarsInLog = 0; 324 | }; 325 | B7101ABADB70F626E71B10AB /* [CP] Check Pods Manifest.lock */ = { 326 | isa = PBXShellScriptBuildPhase; 327 | buildActionMask = 2147483647; 328 | files = ( 329 | ); 330 | inputPaths = ( 331 | "${PODS_PODFILE_DIR_PATH}/Podfile.lock", 332 | "${PODS_ROOT}/Manifest.lock", 333 | ); 334 | name = "[CP] Check Pods Manifest.lock"; 335 | outputPaths = ( 336 | "$(DERIVED_FILE_DIR)/Pods-SwiftSourceryTemplates-checkManifestLockResult.txt", 337 | ); 338 | runOnlyForDeploymentPostprocessing = 0; 339 | shellPath = /bin/sh; 340 | shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; 341 | showEnvVarsInLog = 0; 342 | }; 343 | /* End PBXShellScriptBuildPhase section */ 344 | 345 | /* Begin PBXSourcesBuildPhase section */ 346 | 1415F0FA20FF583700CBE106 /* Sources */ = { 347 | isa = PBXSourcesBuildPhase; 348 | buildActionMask = 2147483647; 349 | files = ( 350 | 140E6F6920FF6627008418F1 /* Protocols.swift in Sources */, 351 | 1415F10220FF583700CBE106 /* AppDelegate.swift in Sources */, 352 | 1415F11F20FF5AF400CBE106 /* TypeErase.generated.swift in Sources */, 353 | 140E6F6C20FF6661008418F1 /* Types.swift in Sources */, 354 | ); 355 | runOnlyForDeploymentPostprocessing = 0; 356 | }; 357 | 1415F10A20FF583800CBE106 /* Sources */ = { 358 | isa = PBXSourcesBuildPhase; 359 | buildActionMask = 2147483647; 360 | files = ( 361 | 143CDB632158DA5F00C345B1 /* Mocks.generated.swift in Sources */, 362 | 1415F11320FF583800CBE106 /* SwiftSourceryTemplatesTests.swift in Sources */, 363 | ); 364 | runOnlyForDeploymentPostprocessing = 0; 365 | }; 366 | /* End PBXSourcesBuildPhase section */ 367 | 368 | /* Begin PBXTargetDependency section */ 369 | 1415F11020FF583800CBE106 /* PBXTargetDependency */ = { 370 | isa = PBXTargetDependency; 371 | target = 1415F0FD20FF583700CBE106 /* SwiftSourceryTemplates */; 372 | targetProxy = 1415F10F20FF583800CBE106 /* PBXContainerItemProxy */; 373 | }; 374 | /* End PBXTargetDependency section */ 375 | 376 | /* Begin PBXVariantGroup section */ 377 | 1415F10520FF583800CBE106 /* MainMenu.xib */ = { 378 | isa = PBXVariantGroup; 379 | children = ( 380 | 1415F10620FF583800CBE106 /* Base */, 381 | ); 382 | name = MainMenu.xib; 383 | sourceTree = ""; 384 | }; 385 | /* End PBXVariantGroup section */ 386 | 387 | /* Begin XCBuildConfiguration section */ 388 | 1415F11520FF583800CBE106 /* Debug */ = { 389 | isa = XCBuildConfiguration; 390 | buildSettings = { 391 | ALWAYS_SEARCH_USER_PATHS = NO; 392 | CLANG_ANALYZER_NONNULL = YES; 393 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 394 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; 395 | CLANG_CXX_LIBRARY = "libc++"; 396 | CLANG_ENABLE_MODULES = YES; 397 | CLANG_ENABLE_OBJC_ARC = YES; 398 | CLANG_ENABLE_OBJC_WEAK = YES; 399 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 400 | CLANG_WARN_BOOL_CONVERSION = YES; 401 | CLANG_WARN_COMMA = YES; 402 | CLANG_WARN_CONSTANT_CONVERSION = YES; 403 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 404 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 405 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 406 | CLANG_WARN_EMPTY_BODY = YES; 407 | CLANG_WARN_ENUM_CONVERSION = YES; 408 | CLANG_WARN_INFINITE_RECURSION = YES; 409 | CLANG_WARN_INT_CONVERSION = YES; 410 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 411 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 412 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 413 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 414 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 415 | CLANG_WARN_STRICT_PROTOTYPES = YES; 416 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 417 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; 418 | CLANG_WARN_UNREACHABLE_CODE = YES; 419 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 420 | CODE_SIGN_IDENTITY = "-"; 421 | COPY_PHASE_STRIP = NO; 422 | DEBUG_INFORMATION_FORMAT = dwarf; 423 | ENABLE_STRICT_OBJC_MSGSEND = YES; 424 | ENABLE_TESTABILITY = YES; 425 | GCC_C_LANGUAGE_STANDARD = gnu11; 426 | GCC_DYNAMIC_NO_PIC = NO; 427 | GCC_NO_COMMON_BLOCKS = YES; 428 | GCC_OPTIMIZATION_LEVEL = 0; 429 | GCC_PREPROCESSOR_DEFINITIONS = ( 430 | "DEBUG=1", 431 | "$(inherited)", 432 | ); 433 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 434 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 435 | GCC_WARN_UNDECLARED_SELECTOR = YES; 436 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 437 | GCC_WARN_UNUSED_FUNCTION = YES; 438 | GCC_WARN_UNUSED_VARIABLE = YES; 439 | MACOSX_DEPLOYMENT_TARGET = 10.13; 440 | MTL_ENABLE_DEBUG_INFO = YES; 441 | ONLY_ACTIVE_ARCH = YES; 442 | SDKROOT = macosx; 443 | SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; 444 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 445 | }; 446 | name = Debug; 447 | }; 448 | 1415F11620FF583800CBE106 /* Release */ = { 449 | isa = XCBuildConfiguration; 450 | buildSettings = { 451 | ALWAYS_SEARCH_USER_PATHS = NO; 452 | CLANG_ANALYZER_NONNULL = YES; 453 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 454 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; 455 | CLANG_CXX_LIBRARY = "libc++"; 456 | CLANG_ENABLE_MODULES = YES; 457 | CLANG_ENABLE_OBJC_ARC = YES; 458 | CLANG_ENABLE_OBJC_WEAK = YES; 459 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 460 | CLANG_WARN_BOOL_CONVERSION = YES; 461 | CLANG_WARN_COMMA = YES; 462 | CLANG_WARN_CONSTANT_CONVERSION = YES; 463 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 464 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 465 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 466 | CLANG_WARN_EMPTY_BODY = YES; 467 | CLANG_WARN_ENUM_CONVERSION = YES; 468 | CLANG_WARN_INFINITE_RECURSION = YES; 469 | CLANG_WARN_INT_CONVERSION = YES; 470 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 471 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 472 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 473 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 474 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 475 | CLANG_WARN_STRICT_PROTOTYPES = YES; 476 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 477 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; 478 | CLANG_WARN_UNREACHABLE_CODE = YES; 479 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 480 | CODE_SIGN_IDENTITY = "-"; 481 | COPY_PHASE_STRIP = NO; 482 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 483 | ENABLE_NS_ASSERTIONS = NO; 484 | ENABLE_STRICT_OBJC_MSGSEND = YES; 485 | GCC_C_LANGUAGE_STANDARD = gnu11; 486 | GCC_NO_COMMON_BLOCKS = YES; 487 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 488 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 489 | GCC_WARN_UNDECLARED_SELECTOR = YES; 490 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 491 | GCC_WARN_UNUSED_FUNCTION = YES; 492 | GCC_WARN_UNUSED_VARIABLE = YES; 493 | MACOSX_DEPLOYMENT_TARGET = 10.13; 494 | MTL_ENABLE_DEBUG_INFO = NO; 495 | SDKROOT = macosx; 496 | SWIFT_COMPILATION_MODE = wholemodule; 497 | SWIFT_OPTIMIZATION_LEVEL = "-O"; 498 | }; 499 | name = Release; 500 | }; 501 | 1415F11820FF583800CBE106 /* Debug */ = { 502 | isa = XCBuildConfiguration; 503 | baseConfigurationReference = B01DD88ED4CE6A3558671FD7 /* Pods-SwiftSourceryTemplates.debug.xcconfig */; 504 | buildSettings = { 505 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 506 | CODE_SIGN_ENTITLEMENTS = SwiftSourceryTemplates/SwiftSourceryTemplates.entitlements; 507 | CODE_SIGN_IDENTITY = "Mac Developer"; 508 | CODE_SIGN_STYLE = Automatic; 509 | COMBINE_HIDPI_IMAGES = YES; 510 | DEVELOPMENT_TEAM = Q369AB8V5C; 511 | INFOPLIST_FILE = SwiftSourceryTemplates/Info.plist; 512 | LD_RUNPATH_SEARCH_PATHS = ( 513 | "$(inherited)", 514 | "@executable_path/../Frameworks", 515 | ); 516 | PRODUCT_BUNDLE_IDENTIFIER = com.popappfactory.SwiftSourceryTemplates; 517 | PRODUCT_NAME = "$(TARGET_NAME)"; 518 | PROVISIONING_PROFILE_SPECIFIER = ""; 519 | SWIFT_VERSION = 5.0; 520 | }; 521 | name = Debug; 522 | }; 523 | 1415F11920FF583800CBE106 /* Release */ = { 524 | isa = XCBuildConfiguration; 525 | baseConfigurationReference = 26A5D53F86280C8D6399AFAB /* Pods-SwiftSourceryTemplates.release.xcconfig */; 526 | buildSettings = { 527 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 528 | CODE_SIGN_ENTITLEMENTS = SwiftSourceryTemplates/SwiftSourceryTemplates.entitlements; 529 | CODE_SIGN_IDENTITY = "Mac Developer"; 530 | CODE_SIGN_STYLE = Automatic; 531 | COMBINE_HIDPI_IMAGES = YES; 532 | DEVELOPMENT_TEAM = Q369AB8V5C; 533 | INFOPLIST_FILE = SwiftSourceryTemplates/Info.plist; 534 | LD_RUNPATH_SEARCH_PATHS = ( 535 | "$(inherited)", 536 | "@executable_path/../Frameworks", 537 | ); 538 | PRODUCT_BUNDLE_IDENTIFIER = com.popappfactory.SwiftSourceryTemplates; 539 | PRODUCT_NAME = "$(TARGET_NAME)"; 540 | PROVISIONING_PROFILE_SPECIFIER = ""; 541 | SWIFT_VERSION = 5.0; 542 | }; 543 | name = Release; 544 | }; 545 | 1415F11B20FF583800CBE106 /* Debug */ = { 546 | isa = XCBuildConfiguration; 547 | baseConfigurationReference = 90AA27DA09BD2D0CA17E26F1 /* Pods-SwiftSourceryTemplatesTests.debug.xcconfig */; 548 | buildSettings = { 549 | BUNDLE_LOADER = "$(TEST_HOST)"; 550 | CODE_SIGN_IDENTITY = "Mac Developer"; 551 | CODE_SIGN_STYLE = Automatic; 552 | COMBINE_HIDPI_IMAGES = YES; 553 | DEVELOPMENT_TEAM = Q369AB8V5C; 554 | INFOPLIST_FILE = SwiftSourceryTemplatesTests/Info.plist; 555 | LD_RUNPATH_SEARCH_PATHS = ( 556 | "$(inherited)", 557 | "@executable_path/../Frameworks", 558 | "@loader_path/../Frameworks", 559 | ); 560 | PRODUCT_BUNDLE_IDENTIFIER = com.albumprinter.SwiftSourceryTemplatesTests; 561 | PRODUCT_NAME = "$(TARGET_NAME)"; 562 | PROVISIONING_PROFILE_SPECIFIER = ""; 563 | SWIFT_VERSION = 5.0; 564 | TEST_HOST = "$(BUILT_PRODUCTS_DIR)/SwiftSourceryTemplates.app/Contents/MacOS/SwiftSourceryTemplates"; 565 | }; 566 | name = Debug; 567 | }; 568 | 1415F11C20FF583800CBE106 /* Release */ = { 569 | isa = XCBuildConfiguration; 570 | baseConfigurationReference = 62BCC70E6F64ED26E8F1AFAD /* Pods-SwiftSourceryTemplatesTests.release.xcconfig */; 571 | buildSettings = { 572 | BUNDLE_LOADER = "$(TEST_HOST)"; 573 | CODE_SIGN_IDENTITY = "Mac Developer"; 574 | CODE_SIGN_STYLE = Automatic; 575 | COMBINE_HIDPI_IMAGES = YES; 576 | DEVELOPMENT_TEAM = Q369AB8V5C; 577 | INFOPLIST_FILE = SwiftSourceryTemplatesTests/Info.plist; 578 | LD_RUNPATH_SEARCH_PATHS = ( 579 | "$(inherited)", 580 | "@executable_path/../Frameworks", 581 | "@loader_path/../Frameworks", 582 | ); 583 | PRODUCT_BUNDLE_IDENTIFIER = com.albumprinter.SwiftSourceryTemplatesTests; 584 | PRODUCT_NAME = "$(TARGET_NAME)"; 585 | PROVISIONING_PROFILE_SPECIFIER = ""; 586 | SWIFT_VERSION = 5.0; 587 | TEST_HOST = "$(BUILT_PRODUCTS_DIR)/SwiftSourceryTemplates.app/Contents/MacOS/SwiftSourceryTemplates"; 588 | }; 589 | name = Release; 590 | }; 591 | /* End XCBuildConfiguration section */ 592 | 593 | /* Begin XCConfigurationList section */ 594 | 1415F0F920FF583700CBE106 /* Build configuration list for PBXProject "SwiftSourceryTemplates" */ = { 595 | isa = XCConfigurationList; 596 | buildConfigurations = ( 597 | 1415F11520FF583800CBE106 /* Debug */, 598 | 1415F11620FF583800CBE106 /* Release */, 599 | ); 600 | defaultConfigurationIsVisible = 0; 601 | defaultConfigurationName = Release; 602 | }; 603 | 1415F11720FF583800CBE106 /* Build configuration list for PBXNativeTarget "SwiftSourceryTemplates" */ = { 604 | isa = XCConfigurationList; 605 | buildConfigurations = ( 606 | 1415F11820FF583800CBE106 /* Debug */, 607 | 1415F11920FF583800CBE106 /* Release */, 608 | ); 609 | defaultConfigurationIsVisible = 0; 610 | defaultConfigurationName = Release; 611 | }; 612 | 1415F11A20FF583800CBE106 /* Build configuration list for PBXNativeTarget "SwiftSourceryTemplatesTests" */ = { 613 | isa = XCConfigurationList; 614 | buildConfigurations = ( 615 | 1415F11B20FF583800CBE106 /* Debug */, 616 | 1415F11C20FF583800CBE106 /* Release */, 617 | ); 618 | defaultConfigurationIsVisible = 0; 619 | defaultConfigurationName = Release; 620 | }; 621 | /* End XCConfigurationList section */ 622 | }; 623 | rootObject = 1415F0F620FF583700CBE106 /* Project object */; 624 | } 625 | -------------------------------------------------------------------------------- /Examples/ExampleProjectCocoapods/SwiftSourceryTemplates/SwiftSourceryTemplates.xcodeproj/xcshareddata/xcschemes/SwiftSourceryTemplates.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 37 | 38 | 39 | 40 | 42 | 48 | 49 | 50 | 51 | 52 | 62 | 64 | 70 | 71 | 72 | 73 | 79 | 81 | 87 | 88 | 89 | 90 | 92 | 93 | 96 | 97 | 98 | -------------------------------------------------------------------------------- /Examples/ExampleProjectCocoapods/SwiftSourceryTemplates/SwiftSourceryTemplates/AppDelegate.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.swift 3 | // SwiftSourceryTemplates 4 | // 5 | // Created by Ivan Misuno on 18/07/2018. 6 | // Copyright © 2018 AlbumPrinter BV. All rights reserved. 7 | // 8 | 9 | import Cocoa 10 | 11 | @NSApplicationMain 12 | class AppDelegate: NSObject, NSApplicationDelegate { 13 | 14 | @IBOutlet weak var window: NSWindow! 15 | 16 | 17 | func applicationDidFinishLaunching(_ aNotification: Notification) { 18 | // Insert code here to initialize your application 19 | } 20 | 21 | func applicationWillTerminate(_ aNotification: Notification) { 22 | // Insert code here to tear down your application 23 | } 24 | 25 | 26 | } 27 | 28 | -------------------------------------------------------------------------------- /Examples/ExampleProjectCocoapods/SwiftSourceryTemplates/SwiftSourceryTemplates/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "mac", 5 | "size" : "16x16", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "mac", 10 | "size" : "16x16", 11 | "scale" : "2x" 12 | }, 13 | { 14 | "idiom" : "mac", 15 | "size" : "32x32", 16 | "scale" : "1x" 17 | }, 18 | { 19 | "idiom" : "mac", 20 | "size" : "32x32", 21 | "scale" : "2x" 22 | }, 23 | { 24 | "idiom" : "mac", 25 | "size" : "128x128", 26 | "scale" : "1x" 27 | }, 28 | { 29 | "idiom" : "mac", 30 | "size" : "128x128", 31 | "scale" : "2x" 32 | }, 33 | { 34 | "idiom" : "mac", 35 | "size" : "256x256", 36 | "scale" : "1x" 37 | }, 38 | { 39 | "idiom" : "mac", 40 | "size" : "256x256", 41 | "scale" : "2x" 42 | }, 43 | { 44 | "idiom" : "mac", 45 | "size" : "512x512", 46 | "scale" : "1x" 47 | }, 48 | { 49 | "idiom" : "mac", 50 | "size" : "512x512", 51 | "scale" : "2x" 52 | } 53 | ], 54 | "info" : { 55 | "version" : 1, 56 | "author" : "xcode" 57 | } 58 | } -------------------------------------------------------------------------------- /Examples/ExampleProjectCocoapods/SwiftSourceryTemplates/SwiftSourceryTemplates/Assets.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "version" : 1, 4 | "author" : "xcode" 5 | } 6 | } -------------------------------------------------------------------------------- /Examples/ExampleProjectCocoapods/SwiftSourceryTemplates/SwiftSourceryTemplates/Generated/TypeErase.generated.swift: -------------------------------------------------------------------------------- 1 | // Generated using Sourcery 2.1.2 — https://github.com/krzysztofzablocki/Sourcery 2 | // DO NOT EDIT 3 | //swiftlint:disable function_body_length 4 | //swiftlint:disable line_length 5 | //swiftlint:disable vertical_whitespace 6 | 7 | import RxSwift 8 | 9 | // MARK: - Type erasure for `ErrorPopoverPresentable` 10 | 11 | private class _AnyErrorPopoverPresentableBase: ErrorPopoverPresentable { 12 | init() { 13 | guard type(of: self) != _AnyErrorPopoverPresentableBase.self else { 14 | fatalError("_AnyErrorPopoverPresentableBase instances can not be created; create a subclass instance instead") 15 | } 16 | } 17 | 18 | func show(relativeTo positioningRect: NSRect, of positioningView: NSView, preferredEdge: NSRectEdge) -> Observable { 19 | fatalError("Must override") 20 | } 21 | func setActionSink(_ actionSink: AnyObject?) -> Void { 22 | fatalError("Must override") 23 | } 24 | } 25 | 26 | private final class _AnyErrorPopoverPresentableBox: _AnyErrorPopoverPresentableBase { 27 | private let concrete: Concrete 28 | typealias EventType = Concrete.EventType 29 | 30 | init(_ concrete: Concrete) { 31 | self.concrete = concrete 32 | } 33 | 34 | override func show(relativeTo positioningRect: NSRect, of positioningView: NSView, preferredEdge: NSRectEdge) -> Observable { 35 | return concrete.show(relativeTo: positioningRect, of: positioningView, preferredEdge: preferredEdge) 36 | } 37 | override func setActionSink(_ actionSink: AnyObject?) -> Void { 38 | return concrete.setActionSink(actionSink) 39 | } 40 | } 41 | 42 | final class AnyErrorPopoverPresentable: ErrorPopoverPresentable { 43 | private let box: _AnyErrorPopoverPresentableBase 44 | 45 | init(_ concrete: Concrete) where Concrete.EventType == EventType { 46 | self.box = _AnyErrorPopoverPresentableBox(concrete) 47 | } 48 | 49 | func show(relativeTo positioningRect: NSRect, of positioningView: NSView, preferredEdge: NSRectEdge) -> Observable { 50 | return box.show(relativeTo: positioningRect, of: positioningView, preferredEdge: preferredEdge) 51 | } 52 | func setActionSink(_ actionSink: AnyObject?) -> Void { 53 | return box.setActionSink(actionSink) 54 | } 55 | } 56 | 57 | // MARK: - Type erasure for `ErrorPopoverPresentableRawRepresentable` 58 | 59 | private class _AnyErrorPopoverPresentableRawRepresentableBase: ErrorPopoverPresentableRawRepresentable where EventType: Hashable, EventType: RawRepresentable { 60 | init() { 61 | guard type(of: self) != _AnyErrorPopoverPresentableRawRepresentableBase.self else { 62 | fatalError("_AnyErrorPopoverPresentableRawRepresentableBase instances can not be created; create a subclass instance instead") 63 | } 64 | } 65 | 66 | func show(relativeTo positioningRect: NSRect, of positioningView: NSView, preferredEdge: NSRectEdge) -> Observable { 67 | fatalError("Must override") 68 | } 69 | } 70 | 71 | private final class _AnyErrorPopoverPresentableRawRepresentableBox: _AnyErrorPopoverPresentableRawRepresentableBase { 72 | private let concrete: Concrete 73 | typealias EventType = Concrete.EventType 74 | 75 | init(_ concrete: Concrete) { 76 | self.concrete = concrete 77 | } 78 | 79 | override func show(relativeTo positioningRect: NSRect, of positioningView: NSView, preferredEdge: NSRectEdge) -> Observable { 80 | return concrete.show(relativeTo: positioningRect, of: positioningView, preferredEdge: preferredEdge) 81 | } 82 | } 83 | 84 | final class AnyErrorPopoverPresentableRawRepresentable: ErrorPopoverPresentableRawRepresentable where EventType: Hashable, EventType: RawRepresentable { 85 | private let box: _AnyErrorPopoverPresentableRawRepresentableBase 86 | 87 | init(_ concrete: Concrete) where Concrete.EventType == EventType { 88 | self.box = _AnyErrorPopoverPresentableRawRepresentableBox(concrete) 89 | } 90 | 91 | func show(relativeTo positioningRect: NSRect, of positioningView: NSView, preferredEdge: NSRectEdge) -> Observable { 92 | return box.show(relativeTo: positioningRect, of: positioningView, preferredEdge: preferredEdge) 93 | } 94 | } 95 | 96 | // MARK: - Type erasure for `Interactable` 97 | 98 | private class _AnyInteractableBase: Interactable { 99 | init() { 100 | guard type(of: self) != _AnyInteractableBase.self else { 101 | fatalError("_AnyInteractableBase instances can not be created; create a subclass instance instead") 102 | } 103 | } 104 | } 105 | 106 | private final class _AnyInteractableBox: _AnyInteractableBase { 107 | private let concrete: Concrete 108 | 109 | init(_ concrete: Concrete) { 110 | self.concrete = concrete 111 | } 112 | } 113 | 114 | public final class AnyInteractable: Interactable { 115 | private let box: _AnyInteractableBase 116 | 117 | public init(_ concrete: Concrete) { 118 | self.box = _AnyInteractableBox(concrete) 119 | } 120 | } 121 | 122 | // MARK: - Type erasure for `Routing` 123 | 124 | private class _AnyRoutingBase: Routing where InteractorType: Interactable { 125 | init() { 126 | guard type(of: self) != _AnyRoutingBase.self else { 127 | fatalError("_AnyRoutingBase instances can not be created; create a subclass instance instead") 128 | } 129 | } 130 | 131 | var interactor: InteractorType { 132 | get { fatalError("Must override") } 133 | 134 | } 135 | } 136 | 137 | private final class _AnyRoutingBox: _AnyRoutingBase { 138 | private let concrete: Concrete 139 | typealias InteractorType = Concrete.InteractorType 140 | 141 | init(_ concrete: Concrete) { 142 | self.concrete = concrete 143 | } 144 | 145 | override var interactor: InteractorType { 146 | get { return concrete.interactor } 147 | 148 | } 149 | } 150 | 151 | public final class AnyRouting: Routing where InteractorType: Interactable { 152 | private let box: _AnyRoutingBase 153 | 154 | public init(_ concrete: Concrete) where Concrete.InteractorType == InteractorType { 155 | self.box = _AnyRoutingBox(concrete) 156 | } 157 | 158 | public var interactor: InteractorType { 159 | get { return box.interactor } 160 | 161 | } 162 | } 163 | 164 | // MARK: - Type erasure for `SomeRouting` 165 | 166 | private class _AnySomeRoutingBase: SomeRouting where InteractorType: SomeInteractable { 167 | init() { 168 | guard type(of: self) != _AnySomeRoutingBase.self else { 169 | fatalError("_AnySomeRoutingBase instances can not be created; create a subclass instance instead") 170 | } 171 | } 172 | 173 | var interactor: InteractorType { 174 | get { fatalError("Must override") } 175 | 176 | } 177 | } 178 | 179 | private final class _AnySomeRoutingBox: _AnySomeRoutingBase { 180 | private let concrete: Concrete 181 | typealias InteractorType = Concrete.InteractorType 182 | 183 | init(_ concrete: Concrete) { 184 | self.concrete = concrete 185 | } 186 | 187 | override var interactor: InteractorType { 188 | get { return concrete.interactor } 189 | 190 | } 191 | } 192 | 193 | public final class AnySomeRouting: SomeRouting where InteractorType: SomeInteractable { 194 | private let box: _AnySomeRoutingBase 195 | 196 | public init(_ concrete: Concrete) where Concrete.InteractorType == InteractorType { 197 | self.box = _AnySomeRoutingBox(concrete) 198 | } 199 | 200 | public var interactor: InteractorType { 201 | get { return box.interactor } 202 | 203 | } 204 | } 205 | 206 | // MARK: - Type erasure for `ThrowingGenericBuildable` 207 | 208 | private class _AnyThrowingGenericBuildableBase: ThrowingGenericBuildable { 209 | init() { 210 | guard type(of: self) != _AnyThrowingGenericBuildableBase.self else { 211 | fatalError("_AnyThrowingGenericBuildableBase instances can not be created; create a subclass instance instead") 212 | } 213 | } 214 | 215 | func build() throws -> AnyErrorPopoverPresentable { 216 | fatalError("Must override") 217 | } 218 | } 219 | 220 | private final class _AnyThrowingGenericBuildableBox: _AnyThrowingGenericBuildableBase { 221 | private let concrete: Concrete 222 | typealias EventType = Concrete.EventType 223 | 224 | init(_ concrete: Concrete) { 225 | self.concrete = concrete 226 | } 227 | 228 | override func build() throws -> AnyErrorPopoverPresentable { 229 | return try concrete.build() 230 | } 231 | } 232 | 233 | final class AnyThrowingGenericBuildable: ThrowingGenericBuildable { 234 | private let box: _AnyThrowingGenericBuildableBase 235 | 236 | init(_ concrete: Concrete) where Concrete.EventType == EventType { 237 | self.box = _AnyThrowingGenericBuildableBox(concrete) 238 | } 239 | 240 | func build() throws -> AnyErrorPopoverPresentable { 241 | return try box.build() 242 | } 243 | } 244 | -------------------------------------------------------------------------------- /Examples/ExampleProjectCocoapods/SwiftSourceryTemplates/SwiftSourceryTemplates/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIconFile 10 | 11 | CFBundleIdentifier 12 | $(PRODUCT_BUNDLE_IDENTIFIER) 13 | CFBundleInfoDictionaryVersion 14 | 6.0 15 | CFBundleName 16 | $(PRODUCT_NAME) 17 | CFBundlePackageType 18 | APPL 19 | CFBundleShortVersionString 20 | 1.0 21 | CFBundleVersion 22 | 1 23 | LSMinimumSystemVersion 24 | $(MACOSX_DEPLOYMENT_TARGET) 25 | NSHumanReadableCopyright 26 | Copyright © Ivan Misuno. All rights reserved. 27 | NSMainNibFile 28 | MainMenu 29 | NSPrincipalClass 30 | NSApplication 31 | 32 | 33 | -------------------------------------------------------------------------------- /Examples/ExampleProjectCocoapods/SwiftSourceryTemplates/SwiftSourceryTemplates/Protocols/Protocols.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Protocols.swift 3 | // SwiftSourceryTemplates 4 | // 5 | // Created by Ivan Misuno on 18/07/2018. 6 | // Copyright © 2018 AlbumPrinter BV. All rights reserved. 7 | // 8 | 9 | import RxSwift 10 | import Alamofire // for `Result` 11 | 12 | // sourcery: CreateMock 13 | protocol DataSource { 14 | var bindingTarget: AnyObserver { get } 15 | func bindStreams() -> Disposable 16 | } 17 | 18 | // sourcery: CreateMock 19 | protocol UploadProgressing { 20 | var progress: Observable> { get } 21 | func fileProgress(_ source: UploadAPI.LocalFile.Source) -> Observable 22 | } 23 | 24 | // sourcery: CreateMock 25 | protocol FileService { 26 | func upload(fileUrl: URL) -> Single<[FilePart]> 27 | } 28 | 29 | // sourcery: CreateMock 30 | protocol MutableUploadProgressing: UploadProgressing { 31 | func setInputFiles(localFiles: [UploadAPI.LocalFile]) 32 | func uploadIdRetrieved(localFile: UploadAPI.LocalFile, uploadId: UploadAPI.UploadId) 33 | func filePartsCreated(uploadId: UploadAPI.UploadId, fileParts: [FilePart]) 34 | func filePartProgressed(uploadId: UploadAPI.UploadId, filePart: FilePart, progress: RxProgress) 35 | func downloadUrlsRetrieved() 36 | } 37 | 38 | // sourcery: CreateMock 39 | protocol ThumbCreating { 40 | func createThumbImage(for pictureUrl: URL, fitting size: CGSize) throws -> NSImage 41 | func createThumbJpegData(for pictureUrl: URL, fitting size: CGSize, compression: Double) throws -> Data 42 | } 43 | 44 | // sourcery: CreateMock 45 | protocol ImageAttributeProviding { 46 | func imagePixelSize(source: UploadAPI.LocalFile.Source) throws -> CGSize 47 | } 48 | 49 | // sourcery: CreateMock 50 | protocol AlbumPageSizeProviderDelegate: AnyObject { 51 | var onComplete: AnyObserver<()> { get } 52 | } 53 | 54 | // sourcery: CreateMock 55 | protocol AlbumPageSizeProviding { 56 | var delegate: AlbumPageSizeProviderDelegate? { get set } 57 | var pageSize: CGSize { get } 58 | } 59 | 60 | // sourcery: CreateMock 61 | protocol ExifImageAttributeProviding { 62 | func dateTaken(fileUrl: URL) -> Date? 63 | } 64 | 65 | // sourcery: CreateMock 66 | protocol ProtocolWithCollections { 67 | var items: Set { get } 68 | var data: Array { get } 69 | var mapping: Dictionary { get } 70 | 71 | func getItems() -> Set 72 | func getData() -> Array 73 | func getMapping() -> Dictionary 74 | } 75 | 76 | /// sourcery: CreateMock 77 | protocol DuplicateGenericTypeNames { 78 | // sourcery: generictype = T 79 | func action( 80 | // sourcery: annotatedGenericTypes = "{T}" 81 | _ a: T) 82 | // sourcery: generictype = T 83 | func action2( 84 | // sourcery: annotatedGenericTypes = "{T}" 85 | _ a: T) 86 | } 87 | 88 | /// sourcery: CreateMock 89 | protocol ErrorPopoverBuildable { 90 | // sourcery: generictype = T1 91 | func buildDefaultPopoverPresenter(title: String) -> AnyErrorPopoverPresentable 92 | // sourcery: generictype = T2 93 | func buildPopoverPresenter( 94 | title: String, 95 | // sourcery: annotatedGenericTypes = "[(title: String, identifier: {T2}, handler: ()->())]" 96 | buttons: [(title: String, identifier: T2, handler: ()->())]) -> AnyErrorPopoverPresentable 97 | } 98 | 99 | /// sourcery: CreateMock 100 | protocol ErrorPopoverBuildableRawRepresentable { 101 | // sourcery: generictype = "T: RawRepresentable, T: Hashable" 102 | func buildPopoverPresenter( 103 | title: String, 104 | // sourcery: annotatedGenericTypes = "[(title: String, identifier: {T}, handler: ()->())]" 105 | buttons: [(title: String, identifier: T, handler: ()->())]) -> AnyErrorPopoverPresentableRawRepresentable where T: RawRepresentable, T: Hashable 106 | } 107 | 108 | /// sourcery: CreateMock 109 | protocol ErrorPresenting { 110 | // sourcery: genericType = "T: RawRepresentable, T: Hashable" 111 | func presentErrorPopover( 112 | // sourcery: genericTypePlaceholder = "AnyErrorPopoverPresentable<{T}>" 113 | _ popoverPresenter: AnyErrorPopoverPresentable) -> Observable where T: RawRepresentable, T: Hashable 114 | } 115 | 116 | // sourcery: CreateMock 117 | // sourcery: TypeErase 118 | public protocol Interactable: AnyObject { 119 | } 120 | 121 | // sourcery: CreateMock 122 | // sourcery: TypeErase 123 | // sourcery: associatedtype = "InteractorType: Interactable" 124 | public protocol Routing: AnyObject { 125 | associatedtype InteractorType: Interactable 126 | var interactor: InteractorType { get } 127 | } 128 | 129 | // sourcery: CreateMock 130 | public protocol SomeInteractable: Interactable { 131 | } 132 | 133 | // sourcery: CreateMock 134 | // sourcery: TypeErase 135 | // sourcery: associatedtype = "InteractorType: SomeInteractable" 136 | public protocol SomeRouting: Routing where InteractorType: SomeInteractable { 137 | } 138 | 139 | /// sourcery: CreateMock 140 | /// sourcery: TypeErase 141 | /// sourcery: associatedtype = EventType 142 | protocol ErrorPopoverPresentable { 143 | associatedtype EventType 144 | func show(relativeTo positioningRect: NSRect, of positioningView: NSView, preferredEdge: NSRectEdge) -> Observable 145 | func setActionSink(_ actionSink: AnyObject?) 146 | } 147 | 148 | /// sourcery: CreateMock 149 | /// sourcery: TypeErase 150 | /// sourcery: associatedtype = "EventType: RawRepresentable, EventType: Hashable" 151 | protocol ErrorPopoverPresentableRawRepresentable { 152 | associatedtype EventType: RawRepresentable, Hashable 153 | func show(relativeTo positioningRect: NSRect, of positioningView: NSView, preferredEdge: NSRectEdge) -> Observable 154 | } 155 | 156 | /// sourcery: CreateMock 157 | /// sourcery: TypeErase 158 | /// sourcery: associatedtype = "EventType" 159 | protocol ThrowingGenericBuildable { 160 | associatedtype EventType 161 | func build() throws -> AnyErrorPopoverPresentable 162 | } 163 | 164 | /// sourcery: CreateMock 165 | protocol TipsManaging: AnyObject { 166 | var tips: [String: String] { get } 167 | var tipsOptional: [String: String]? { get } 168 | var tupleVariable: (String, Int) { get } 169 | var tupleVariable2: (String?, Int?) { get } 170 | var tupleOptional: (String, Int)? { get } 171 | var arrayVariable: [Double] { get } 172 | } 173 | 174 | /// sourcery: CreateMock 175 | @objc protocol LegacyProtocol: NSObjectProtocol { 176 | var tips: [String: String] { get } 177 | func compare(_ lhs: CGSize, _ rhs: CGSize) -> ComparisonResult 178 | } 179 | 180 | /// sourcery: CreateMock 181 | protocol DuplicateRequirements { 182 | var tips: [String: String] { get } 183 | func updateTips(_ tips: [Tip]) 184 | func updateTips(with tips: AnySequence) throws 185 | } 186 | 187 | /// sourcery: CreateMock 188 | protocol DuplicateRequirementsSameLevel { 189 | func update(cropRect: CGRect) 190 | func update(cropHandles: [CGPoint]) 191 | func update(effects: [String]) 192 | } 193 | 194 | /// sourcery: CreateMock 195 | protocol MutableTipsManaging: TipsManaging, DuplicateRequirements { 196 | func updateTips(_ tips: [Tip]) 197 | func updateTips(with tips: AnySequence) throws 198 | } 199 | 200 | /// sourcery: CreateMock 201 | protocol TipsAccessing { 202 | /// sourcery: handler 203 | var tip: Tip { get } 204 | } 205 | 206 | /// sourcery: CreateMock 207 | protocol MutableTipsAccessing { 208 | /// sourcery: handler 209 | var tip: Tip { get set } 210 | } 211 | 212 | /// sourcery: CreateMock 213 | protocol TipsManagerBuilding { 214 | func build() -> TipsManaging & MutableTipsManaging 215 | } 216 | 217 | /// sourcery: CreateMock 218 | protocol ObjectManupulating { 219 | @discardableResult 220 | func removeObject() -> Int 221 | 222 | @discardableResult 223 | func removeObject(where matchPredicate: @escaping (Any) throws -> (Bool)) rethrows -> Int 224 | 225 | @discardableResult 226 | func removeObject(_ object: @autoclosure () throws -> Any) rethrows -> Int 227 | } 228 | 229 | /// sourcery: CreateMock 230 | @objc public protocol AppKitEvent { 231 | // sourcery: const, init 232 | var locationInWindow: NSPoint { get } 233 | // sourcery: const, init 234 | var modifierFlags: NSEvent.ModifierFlags { get } 235 | } 236 | 237 | enum ShapeType: CaseIterable { 238 | case circle 239 | case rect 240 | case tri 241 | } 242 | 243 | typealias Shape1 = ( 244 | type: ShapeType, 245 | origin: CGPoint, 246 | size: CGFloat, 247 | linearSpeed: CGPoint, 248 | angle: CGFloat, 249 | angularSpeed: CGFloat, 250 | strokeColor: NSColor, 251 | fillColor: NSColor 252 | ) 253 | 254 | /// sourcery: CreateMock 255 | protocol ShapesAccessing { 256 | var shapes: Observable<[Shape1]> { get } 257 | } 258 | 259 | /// sourcery: CreateMock 260 | protocol ProtocolWithExtensions { 261 | var protocolRequirement: Int { get } 262 | func requiredInProtocol() 263 | } 264 | 265 | extension ProtocolWithExtensions { 266 | var implementedInExtension: Int { 267 | return 5 268 | } 269 | 270 | func extended(completionCallback callback: (() -> ())?) { 271 | callback?() 272 | } 273 | } 274 | 275 | /// sourcery: CreateMock 276 | // protocol EdgeCases { 277 | // func functionMissingArgumentLabel(_: Int) 278 | // } 279 | 280 | typealias RealmNotificationInteropBlock = (_ notification: Notification, _ realmInterop: RealmInterop) -> () 281 | class Object {} 282 | class List {} 283 | class Results {} 284 | struct NotificationToken {} 285 | 286 | /// sourcery: CreateMock 287 | protocol RealmInterop: AnyObject 288 | { 289 | func write(_ block: (() throws -> Void)) throws 290 | func beginWrite() 291 | func commitWrite(withoutNotifying tokens: [NotificationToken]) throws 292 | func cancelWrite() 293 | var isInWriteTransaction: Bool { get } 294 | 295 | /// sourcery: methodName = addObject 296 | func add(_ object: Object, update: Bool) 297 | 298 | /// sourcery: methodName = addObjects 299 | /// sourcery: genericType = "S: Sequence, S.Iterator.Element: Object" 300 | func add( 301 | // sourcery: annotatedGenericTypes = "{S}" 302 | _ objects: S, 303 | update: Bool) where S: Sequence, S.Iterator.Element: Object 304 | 305 | /// sourcery: genericType = "Element: Object" 306 | func create( 307 | // sourcery: annotatedGenericTypes = "{Element}.Type" 308 | _ type: Element.Type, 309 | value: Any, 310 | update: Bool) -> Element where Element: Object 311 | 312 | /// sourcery: methodName = deleteObject 313 | func delete(_ object: Object) 314 | 315 | /// sourcery: methodName = deleteObjects 316 | /// sourcery: genericType = "S: Sequence, S.Iterator.Element: Object" 317 | func delete( 318 | // sourcery: annotatedGenericTypes = "{S}" 319 | _ objects: S) where S: Sequence, S.Iterator.Element: Object 320 | 321 | /// sourcery: methodName = deleteList 322 | /// sourcery: genericType = "Element: Object" 323 | func delete( 324 | // sourcery: annotatedGenericTypes = "List<{Element}>" 325 | _ objects: List) where Element: Object 326 | 327 | /// sourcery: methodName = deleteResults 328 | /// sourcery: genericType = "Element: Object" 329 | func delete( 330 | // sourcery: annotatedGenericTypes = "Results<{Element}>" 331 | _ objects: Results) where Element: Object 332 | 333 | func deleteAll() 334 | 335 | /// sourcery: genericType = "Element: Object" 336 | func objects( 337 | // sourcery: annotatedGenericTypes = "{Element}.Type" 338 | _ type: Element.Type) -> Results where Element: Object 339 | 340 | /// sourcery: genericType = "Element: Object, KeyType" 341 | func object( 342 | // sourcery: annotatedGenericTypes = "{Element}.Type" 343 | ofType type: Element.Type, 344 | // sourcery: annotatedGenericTypes = "{KeyType}" 345 | forPrimaryKey key: KeyType) -> Element? where Element: Object 346 | 347 | func observe(_ block: @escaping RealmNotificationInteropBlock) -> NotificationToken 348 | var autorefresh: Bool { get set } 349 | func refresh() -> Bool 350 | func invalidate() 351 | func writeCopy(toFile fileURL: URL, encryptionKey: Data?) throws 352 | } 353 | -------------------------------------------------------------------------------- /Examples/ExampleProjectCocoapods/SwiftSourceryTemplates/SwiftSourceryTemplates/SwiftSourceryTemplates.entitlements: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | com.apple.security.app-sandbox 6 | 7 | com.apple.security.files.user-selected.read-only 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /Examples/ExampleProjectCocoapods/SwiftSourceryTemplates/SwiftSourceryTemplates/Types/Types.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Types.swift 3 | // SwiftSourceryTemplates 4 | // 5 | // Created by Ivan Misuno on 18/07/2018. 6 | // Copyright © 2018 AlbumPrinter BV. All rights reserved. 7 | // 8 | 9 | import AppKit // for `NSColor` 10 | 11 | struct UploadAPI { 12 | typealias UploadId = String 13 | 14 | struct LocalFile { 15 | enum Source { 16 | } 17 | } 18 | } 19 | 20 | struct FilePart { 21 | } 22 | 23 | struct RxProgress { 24 | } 25 | 26 | struct Tip { 27 | } 28 | -------------------------------------------------------------------------------- /Examples/ExampleProjectCocoapods/SwiftSourceryTemplates/SwiftSourceryTemplatesTests/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | BNDL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleVersion 20 | 1 21 | 22 | 23 | -------------------------------------------------------------------------------- /Examples/ExampleProjectCocoapods/SwiftSourceryTemplates/SwiftSourceryTemplatesTests/Mocks/Mocks.generated.swift: -------------------------------------------------------------------------------- 1 | // Generated using Sourcery 2.1.2 — https://github.com/krzysztofzablocki/Sourcery 2 | // DO NOT EDIT 3 | //swiftlint:disable force_cast 4 | //swiftlint:disable function_body_length 5 | //swiftlint:disable line_length 6 | //swiftlint:disable vertical_whitespace 7 | 8 | import Alamofire 9 | import RxBlocking 10 | import RxSwift 11 | import RxTest 12 | @testable import SwiftSourceryTemplates 13 | 14 | // MARK: - AlbumPageSizeProviderDelegate 15 | class AlbumPageSizeProviderDelegateMock: AlbumPageSizeProviderDelegate { 16 | 17 | // MARK: - Variables 18 | var onComplete: AnyObserver<()> { 19 | onCompleteGetCount += 1 20 | if let handler = onCompleteGetHandler { 21 | return handler() 22 | } 23 | return AnyObserver { [weak self] event in 24 | self?.onCompleteEventCallCount += 1 25 | self?.onCompleteEventHandler?(event) 26 | } 27 | } 28 | var onCompleteGetCount: Int = 0 29 | var onCompleteGetHandler: (() -> AnyObserver<()>)? = nil 30 | var onCompleteEventCallCount: Int = 0 31 | var onCompleteEventHandler: ((Event<()>) -> ())? = nil 32 | } 33 | 34 | // MARK: - AlbumPageSizeProviding 35 | class AlbumPageSizeProvidingMock: AlbumPageSizeProviding { 36 | 37 | // MARK: - Variables 38 | var delegate: AlbumPageSizeProviderDelegate? = nil { 39 | didSet { 40 | delegateSetCount += 1 41 | } 42 | } 43 | var delegateSetCount: Int = 0 44 | var pageSize: CGSize = CGSize.zero 45 | } 46 | 47 | // MARK: - AppKitEvent 48 | class AppKitEventMock: AppKitEvent { 49 | 50 | // MARK: - Variables 51 | let locationInWindow: NSPoint 52 | let modifierFlags: NSEvent.ModifierFlags 53 | 54 | // MARK: - Initializer 55 | init(locationInWindow: NSPoint = CGPoint.zero, modifierFlags: NSEvent.ModifierFlags) { 56 | self.locationInWindow = locationInWindow 57 | self.modifierFlags = modifierFlags 58 | } 59 | } 60 | 61 | // MARK: - DataSource 62 | class DataSourceMock: DataSource { 63 | 64 | // MARK: - Variables 65 | var bindingTarget: AnyObserver { 66 | bindingTargetGetCount += 1 67 | if let handler = bindingTargetGetHandler { 68 | return handler() 69 | } 70 | return AnyObserver { [weak self] event in 71 | self?.bindingTargetEventCallCount += 1 72 | self?.bindingTargetEventHandler?(event) 73 | } 74 | } 75 | var bindingTargetGetCount: Int = 0 76 | var bindingTargetGetHandler: (() -> AnyObserver)? = nil 77 | var bindingTargetEventCallCount: Int = 0 78 | var bindingTargetEventHandler: ((Event) -> ())? = nil 79 | 80 | // MARK: - Methods 81 | func bindStreams() -> Disposable { 82 | bindStreamsCallCount += 1 83 | if let __bindStreamsHandler = self.bindStreamsHandler { 84 | return __bindStreamsHandler() 85 | } 86 | return Disposables.create { [weak self] in 87 | self?.bindStreamsDisposeCallCount += 1 88 | self?.bindStreamsDisposeHandler?() 89 | } 90 | } 91 | var bindStreamsCallCount: Int = 0 92 | var bindStreamsHandler: (() -> (Disposable))? = nil 93 | var bindStreamsDisposeCallCount: Int = 0 94 | var bindStreamsDisposeHandler: (() -> ())? = nil 95 | } 96 | 97 | // MARK: - DuplicateGenericTypeNames 98 | class DuplicateGenericTypeNamesMock<_T>: DuplicateGenericTypeNames { 99 | 100 | // MARK: - Generic typealiases 101 | typealias T = _T 102 | 103 | // MARK: - Methods 104 | func action(_ a: T) { 105 | actionCallCount += 1 106 | if let __actionHandler = self.actionHandler { 107 | __actionHandler(a as! _T) 108 | } 109 | } 110 | var actionCallCount: Int = 0 111 | var actionHandler: ((_ a: T) -> ())? = nil 112 | func action2(_ a: T) { 113 | action2CallCount += 1 114 | if let __action2Handler = self.action2Handler { 115 | __action2Handler(a as! _T) 116 | } 117 | } 118 | var action2CallCount: Int = 0 119 | var action2Handler: ((_ a: T) -> ())? = nil 120 | } 121 | 122 | // MARK: - DuplicateRequirements 123 | class DuplicateRequirementsMock: DuplicateRequirements { 124 | 125 | // MARK: - Variables 126 | var tips: [String: String] = [:] 127 | 128 | // MARK: - Methods 129 | func updateTips(_ tips: [Tip]) { 130 | updateTipsCallCount += 1 131 | if let __updateTipsHandler = self.updateTipsHandler { 132 | __updateTipsHandler(tips) 133 | } 134 | } 135 | var updateTipsCallCount: Int = 0 136 | var updateTipsHandler: ((_ tips: [Tip]) -> ())? = nil 137 | func updateTips(with tips: AnySequence) throws { 138 | updateTipsWithTipsCallCount += 1 139 | if let __updateTipsWithTipsHandler = self.updateTipsWithTipsHandler { 140 | try __updateTipsWithTipsHandler(tips) 141 | } 142 | } 143 | var updateTipsWithTipsCallCount: Int = 0 144 | var updateTipsWithTipsHandler: ((_ tips: AnySequence) throws -> ())? = nil 145 | } 146 | 147 | // MARK: - DuplicateRequirementsSameLevel 148 | class DuplicateRequirementsSameLevelMock: DuplicateRequirementsSameLevel { 149 | 150 | // MARK: - Methods 151 | func update(cropHandles: [CGPoint]) { 152 | updateCropHandlesCallCount += 1 153 | if let __updateCropHandlesHandler = self.updateCropHandlesHandler { 154 | __updateCropHandlesHandler(cropHandles) 155 | } 156 | } 157 | var updateCropHandlesCallCount: Int = 0 158 | var updateCropHandlesHandler: ((_ cropHandles: [CGPoint]) -> ())? = nil 159 | func update(cropRect: CGRect) { 160 | updateCropRectCallCount += 1 161 | if let __updateCropRectHandler = self.updateCropRectHandler { 162 | __updateCropRectHandler(cropRect) 163 | } 164 | } 165 | var updateCropRectCallCount: Int = 0 166 | var updateCropRectHandler: ((_ cropRect: CGRect) -> ())? = nil 167 | func update(effects: [String]) { 168 | updateEffectsCallCount += 1 169 | if let __updateEffectsHandler = self.updateEffectsHandler { 170 | __updateEffectsHandler(effects) 171 | } 172 | } 173 | var updateEffectsCallCount: Int = 0 174 | var updateEffectsHandler: ((_ effects: [String]) -> ())? = nil 175 | } 176 | 177 | // MARK: - ErrorPopoverBuildable 178 | class ErrorPopoverBuildableMock<_T1, _T2>: ErrorPopoverBuildable { 179 | 180 | // MARK: - Generic typealiases 181 | typealias T1 = _T1 182 | typealias T2 = _T2 183 | 184 | // MARK: - Methods 185 | func buildDefaultPopoverPresenter(title: String) -> AnyErrorPopoverPresentable { 186 | buildDefaultPopoverPresenterCallCount += 1 187 | if let __buildDefaultPopoverPresenterHandler = self.buildDefaultPopoverPresenterHandler { 188 | return __buildDefaultPopoverPresenterHandler(title) as! AnyErrorPopoverPresentable 189 | } 190 | fatalError("buildDefaultPopoverPresenterHandler expected to be set.") 191 | } 192 | var buildDefaultPopoverPresenterCallCount: Int = 0 193 | var buildDefaultPopoverPresenterHandler: ((_ title: String) -> (AnyErrorPopoverPresentable))? = nil 194 | func buildPopoverPresenter(title: String, buttons: [(title: String, identifier: T2, handler: ()->())]) -> AnyErrorPopoverPresentable { 195 | buildPopoverPresenterCallCount += 1 196 | if let __buildPopoverPresenterHandler = self.buildPopoverPresenterHandler { 197 | return __buildPopoverPresenterHandler(title, buttons as! [(title: String, identifier: _T2, handler: ()->())]) as! AnyErrorPopoverPresentable 198 | } 199 | fatalError("buildPopoverPresenterHandler expected to be set.") 200 | } 201 | var buildPopoverPresenterCallCount: Int = 0 202 | var buildPopoverPresenterHandler: ((_ title: String, _ buttons: [(title: String, identifier: T2, handler: ()->())]) -> (AnyErrorPopoverPresentable))? = nil 203 | } 204 | 205 | // MARK: - ErrorPopoverBuildableRawRepresentable 206 | class ErrorPopoverBuildableRawRepresentableMock<_T>: ErrorPopoverBuildableRawRepresentable where _T: Hashable, _T: RawRepresentable { 207 | 208 | // MARK: - Generic typealiases 209 | typealias T = _T 210 | 211 | // MARK: - Methods 212 | func buildPopoverPresenter(title: String, buttons: [(title: String, identifier: T, handler: ()->())]) -> AnyErrorPopoverPresentableRawRepresentable where T: RawRepresentable, T: Hashable { 213 | buildPopoverPresenterCallCount += 1 214 | if let __buildPopoverPresenterHandler = self.buildPopoverPresenterHandler { 215 | return __buildPopoverPresenterHandler(title, buttons as! [(title: String, identifier: _T, handler: ()->())]) as! AnyErrorPopoverPresentableRawRepresentable 216 | } 217 | fatalError("buildPopoverPresenterHandler expected to be set.") 218 | } 219 | var buildPopoverPresenterCallCount: Int = 0 220 | var buildPopoverPresenterHandler: ((_ title: String, _ buttons: [(title: String, identifier: T, handler: ()->())]) -> (AnyErrorPopoverPresentableRawRepresentable))? = nil 221 | } 222 | 223 | // MARK: - ErrorPopoverPresentable 224 | class ErrorPopoverPresentableMock<_EventType>: ErrorPopoverPresentable { 225 | 226 | // MARK: - Generic typealiases 227 | typealias EventType = _EventType 228 | 229 | // MARK: - Methods 230 | func setActionSink(_ actionSink: AnyObject?) { 231 | setActionSinkCallCount += 1 232 | if let __setActionSinkHandler = self.setActionSinkHandler { 233 | __setActionSinkHandler(actionSink) 234 | } 235 | } 236 | var setActionSinkCallCount: Int = 0 237 | var setActionSinkHandler: ((_ actionSink: AnyObject?) -> ())? = nil 238 | func show(relativeTo positioningRect: NSRect, of positioningView: NSView, preferredEdge: NSRectEdge) -> Observable { 239 | showCallCount += 1 240 | if let __showHandler = self.showHandler { 241 | return __showHandler(positioningRect, positioningView, preferredEdge) 242 | } 243 | return showSubject.asObservable() 244 | } 245 | var showCallCount: Int = 0 246 | var showHandler: ((_ positioningRect: NSRect, _ positioningView: NSView, _ preferredEdge: NSRectEdge) -> (Observable))? = nil 247 | lazy var showSubject = PublishSubject() 248 | } 249 | 250 | // MARK: - ErrorPopoverPresentableRawRepresentable 251 | class ErrorPopoverPresentableRawRepresentableMock<_EventType>: ErrorPopoverPresentableRawRepresentable where _EventType: Hashable, _EventType: RawRepresentable { 252 | 253 | // MARK: - Generic typealiases 254 | typealias EventType = _EventType 255 | 256 | // MARK: - Methods 257 | func show(relativeTo positioningRect: NSRect, of positioningView: NSView, preferredEdge: NSRectEdge) -> Observable { 258 | showCallCount += 1 259 | if let __showHandler = self.showHandler { 260 | return __showHandler(positioningRect, positioningView, preferredEdge) 261 | } 262 | return showSubject.asObservable() 263 | } 264 | var showCallCount: Int = 0 265 | var showHandler: ((_ positioningRect: NSRect, _ positioningView: NSView, _ preferredEdge: NSRectEdge) -> (Observable))? = nil 266 | lazy var showSubject = PublishSubject() 267 | } 268 | 269 | // MARK: - ErrorPresenting 270 | class ErrorPresentingMock<_T>: ErrorPresenting where _T: Hashable, _T: RawRepresentable { 271 | 272 | // MARK: - Generic typealiases 273 | typealias T = _T 274 | 275 | // MARK: - Methods 276 | func presentErrorPopover(_ popoverPresenter: AnyErrorPopoverPresentable) -> Observable where T: RawRepresentable, T: Hashable { 277 | presentErrorPopoverCallCount += 1 278 | if let __presentErrorPopoverHandler = self.presentErrorPopoverHandler { 279 | return __presentErrorPopoverHandler(popoverPresenter as! AnyErrorPopoverPresentable<_T>) as! Observable 280 | } 281 | return presentErrorPopoverSubject.asObservable() as! Observable 282 | } 283 | var presentErrorPopoverCallCount: Int = 0 284 | var presentErrorPopoverHandler: ((_ popoverPresenter: AnyErrorPopoverPresentable) -> (Observable))? = nil 285 | lazy var presentErrorPopoverSubject = PublishSubject() 286 | } 287 | 288 | // MARK: - ExifImageAttributeProviding 289 | class ExifImageAttributeProvidingMock: ExifImageAttributeProviding { 290 | 291 | // MARK: - Methods 292 | func dateTaken(fileUrl: URL) -> Date? { 293 | dateTakenCallCount += 1 294 | if let __dateTakenHandler = self.dateTakenHandler { 295 | return __dateTakenHandler(fileUrl) 296 | } 297 | return nil 298 | } 299 | var dateTakenCallCount: Int = 0 300 | var dateTakenHandler: ((_ fileUrl: URL) -> (Date?))? = nil 301 | } 302 | 303 | // MARK: - FileService 304 | class FileServiceMock: FileService { 305 | 306 | // MARK: - Methods 307 | func upload(fileUrl: URL) -> Single<[FilePart]> { 308 | uploadCallCount += 1 309 | if let __uploadHandler = self.uploadHandler { 310 | return __uploadHandler(fileUrl) 311 | } 312 | return Single.create { (observer: @escaping (SingleEvent<[SwiftSourceryTemplates.FilePart]>) -> ()) -> Disposable in 313 | return self.uploadSubject.subscribe { (event: Event<[SwiftSourceryTemplates.FilePart]>) in 314 | switch event { 315 | case .next(let element): 316 | observer(.success(element)) 317 | case .error(let error): 318 | observer(.failure(error)) 319 | default: 320 | break 321 | } 322 | } 323 | } 324 | } 325 | var uploadCallCount: Int = 0 326 | var uploadHandler: ((_ fileUrl: URL) -> (Single<[FilePart]>))? = nil 327 | lazy var uploadSubject = PublishSubject<[SwiftSourceryTemplates.FilePart]>() 328 | } 329 | 330 | // MARK: - ImageAttributeProviding 331 | class ImageAttributeProvidingMock: ImageAttributeProviding { 332 | 333 | // MARK: - Methods 334 | func imagePixelSize(source: UploadAPI.LocalFile.Source) throws -> CGSize { 335 | imagePixelSizeCallCount += 1 336 | if let __imagePixelSizeHandler = self.imagePixelSizeHandler { 337 | return try __imagePixelSizeHandler(source) 338 | } 339 | return CGSize.zero 340 | } 341 | var imagePixelSizeCallCount: Int = 0 342 | var imagePixelSizeHandler: ((_ source: UploadAPI.LocalFile.Source) throws -> (CGSize))? = nil 343 | } 344 | 345 | // MARK: - Interactable 346 | class InteractableMock: Interactable { 347 | } 348 | 349 | // MARK: - LegacyProtocol 350 | class LegacyProtocolMock: NSObject, LegacyProtocol { 351 | 352 | // MARK: - Variables 353 | var tips: [String: String] = [:] 354 | 355 | // MARK: - Methods 356 | func compare(_ lhs: CGSize, _ rhs: CGSize) -> ComparisonResult { 357 | compareCallCount += 1 358 | if let __compareHandler = self.compareHandler { 359 | return __compareHandler(lhs, rhs) 360 | } 361 | fatalError("compareHandler expected to be set.") 362 | } 363 | var compareCallCount: Int = 0 364 | var compareHandler: ((_ lhs: CGSize, _ rhs: CGSize) -> (ComparisonResult))? = nil 365 | } 366 | 367 | // MARK: - MutableTipsAccessing 368 | class MutableTipsAccessingMock: MutableTipsAccessing { 369 | 370 | // MARK: - Variables 371 | var tip: Tip { 372 | get { 373 | tipGetCount += 1 374 | if let handler = tipGetHandler { 375 | return handler() 376 | } 377 | fatalError("`tipGetHandler` must be set!") 378 | } 379 | set { 380 | tipSetCount += 1 381 | } 382 | } 383 | var tipGetCount: Int = 0 384 | var tipGetHandler: (() -> Tip)? = nil 385 | var tipSetCount: Int = 0 386 | } 387 | 388 | // MARK: - MutableTipsManaging 389 | class MutableTipsManagingMock: MutableTipsManaging { 390 | 391 | // MARK: - Variables 392 | var arrayVariable: [Double] = [] 393 | var tips: [String: String] = [:] 394 | var tipsOptional: [String: String]? = nil 395 | var tupleOptional: (String, Int)? = nil 396 | var tupleVariable: (String, Int) = ("", 0) 397 | var tupleVariable2: (String?, Int?) = (nil, nil) 398 | 399 | // MARK: - Methods 400 | func updateTips(_ tips: [Tip]) { 401 | updateTipsCallCount += 1 402 | if let __updateTipsHandler = self.updateTipsHandler { 403 | __updateTipsHandler(tips) 404 | } 405 | } 406 | var updateTipsCallCount: Int = 0 407 | var updateTipsHandler: ((_ tips: [Tip]) -> ())? = nil 408 | func updateTips(with tips: AnySequence) throws { 409 | updateTipsWithTipsCallCount += 1 410 | if let __updateTipsWithTipsHandler = self.updateTipsWithTipsHandler { 411 | try __updateTipsWithTipsHandler(tips) 412 | } 413 | } 414 | var updateTipsWithTipsCallCount: Int = 0 415 | var updateTipsWithTipsHandler: ((_ tips: AnySequence) throws -> ())? = nil 416 | } 417 | 418 | // MARK: - MutableUploadProgressing 419 | class MutableUploadProgressingMock: MutableUploadProgressing { 420 | 421 | // MARK: - Variables 422 | var progress: Observable> { 423 | progressGetCount += 1 424 | if let handler = progressGetHandler { 425 | return handler() 426 | } 427 | return progressSubject.asObservable() 428 | } 429 | var progressGetCount: Int = 0 430 | var progressGetHandler: (() -> Observable>)? = nil 431 | lazy var progressSubject = PublishSubject>() 432 | 433 | // MARK: - Methods 434 | func downloadUrlsRetrieved() { 435 | downloadUrlsRetrievedCallCount += 1 436 | if let __downloadUrlsRetrievedHandler = self.downloadUrlsRetrievedHandler { 437 | __downloadUrlsRetrievedHandler() 438 | } 439 | } 440 | var downloadUrlsRetrievedCallCount: Int = 0 441 | var downloadUrlsRetrievedHandler: (() -> ())? = nil 442 | func filePartProgressed(uploadId: UploadAPI.UploadId, filePart: FilePart, progress: RxProgress) { 443 | filePartProgressedCallCount += 1 444 | if let __filePartProgressedHandler = self.filePartProgressedHandler { 445 | __filePartProgressedHandler(uploadId, filePart, progress) 446 | } 447 | } 448 | var filePartProgressedCallCount: Int = 0 449 | var filePartProgressedHandler: ((_ uploadId: UploadAPI.UploadId, _ filePart: FilePart, _ progress: RxProgress) -> ())? = nil 450 | func filePartsCreated(uploadId: UploadAPI.UploadId, fileParts: [FilePart]) { 451 | filePartsCreatedCallCount += 1 452 | if let __filePartsCreatedHandler = self.filePartsCreatedHandler { 453 | __filePartsCreatedHandler(uploadId, fileParts) 454 | } 455 | } 456 | var filePartsCreatedCallCount: Int = 0 457 | var filePartsCreatedHandler: ((_ uploadId: UploadAPI.UploadId, _ fileParts: [FilePart]) -> ())? = nil 458 | func fileProgress(_ source: UploadAPI.LocalFile.Source) -> Observable { 459 | fileProgressCallCount += 1 460 | if let __fileProgressHandler = self.fileProgressHandler { 461 | return __fileProgressHandler(source) 462 | } 463 | return fileProgressSubject.asObservable() 464 | } 465 | var fileProgressCallCount: Int = 0 466 | var fileProgressHandler: ((_ source: UploadAPI.LocalFile.Source) -> (Observable))? = nil 467 | lazy var fileProgressSubject = PublishSubject() 468 | func setInputFiles(localFiles: [UploadAPI.LocalFile]) { 469 | setInputFilesCallCount += 1 470 | if let __setInputFilesHandler = self.setInputFilesHandler { 471 | __setInputFilesHandler(localFiles) 472 | } 473 | } 474 | var setInputFilesCallCount: Int = 0 475 | var setInputFilesHandler: ((_ localFiles: [UploadAPI.LocalFile]) -> ())? = nil 476 | func uploadIdRetrieved(localFile: UploadAPI.LocalFile, uploadId: UploadAPI.UploadId) { 477 | uploadIdRetrievedCallCount += 1 478 | if let __uploadIdRetrievedHandler = self.uploadIdRetrievedHandler { 479 | __uploadIdRetrievedHandler(localFile, uploadId) 480 | } 481 | } 482 | var uploadIdRetrievedCallCount: Int = 0 483 | var uploadIdRetrievedHandler: ((_ localFile: UploadAPI.LocalFile, _ uploadId: UploadAPI.UploadId) -> ())? = nil 484 | } 485 | 486 | // MARK: - ObjectManupulating 487 | class ObjectManupulatingMock: ObjectManupulating { 488 | 489 | // MARK: - Methods 490 | func removeObject() -> Int { 491 | removeObjectCallCount += 1 492 | if let __removeObjectHandler = self.removeObjectHandler { 493 | return __removeObjectHandler() 494 | } 495 | return 0 496 | } 497 | var removeObjectCallCount: Int = 0 498 | var removeObjectHandler: (() -> (Int))? = nil 499 | func removeObject(_ object: () throws -> Any) rethrows -> Int { 500 | removeObjectObjectCallCount += 1 501 | if let __removeObjectObjectHandler = self.removeObjectObjectHandler { 502 | return try! __removeObjectObjectHandler(object) 503 | } 504 | return 0 505 | } 506 | var removeObjectObjectCallCount: Int = 0 507 | var removeObjectObjectHandler: ((_ object: () throws -> Any) throws -> (Int))? = nil 508 | func removeObject(where matchPredicate: (Any) throws -> Bool) rethrows -> Int { 509 | removeObjectWhereMatchPredicateCallCount += 1 510 | if let __removeObjectWhereMatchPredicateHandler = self.removeObjectWhereMatchPredicateHandler { 511 | return try! __removeObjectWhereMatchPredicateHandler(matchPredicate) 512 | } 513 | return 0 514 | } 515 | var removeObjectWhereMatchPredicateCallCount: Int = 0 516 | var removeObjectWhereMatchPredicateHandler: ((_ matchPredicate: (Any) throws -> Bool) throws -> (Int))? = nil 517 | } 518 | 519 | // MARK: - ProtocolWithCollections 520 | class ProtocolWithCollectionsMock: ProtocolWithCollections { 521 | 522 | // MARK: - Variables 523 | var data: Array = [] 524 | var items: Set = Set() 525 | var mapping: Dictionary = [:] 526 | 527 | // MARK: - Methods 528 | func getData() -> Array { 529 | getDataCallCount += 1 530 | if let __getDataHandler = self.getDataHandler { 531 | return __getDataHandler() 532 | } 533 | return [] 534 | } 535 | var getDataCallCount: Int = 0 536 | var getDataHandler: (() -> (Array))? = nil 537 | func getItems() -> Set { 538 | getItemsCallCount += 1 539 | if let __getItemsHandler = self.getItemsHandler { 540 | return __getItemsHandler() 541 | } 542 | return Set() 543 | } 544 | var getItemsCallCount: Int = 0 545 | var getItemsHandler: (() -> (Set))? = nil 546 | func getMapping() -> Dictionary { 547 | getMappingCallCount += 1 548 | if let __getMappingHandler = self.getMappingHandler { 549 | return __getMappingHandler() 550 | } 551 | return [:] 552 | } 553 | var getMappingCallCount: Int = 0 554 | var getMappingHandler: (() -> (Dictionary))? = nil 555 | } 556 | 557 | // MARK: - ProtocolWithExtensions 558 | class ProtocolWithExtensionsMock: ProtocolWithExtensions { 559 | 560 | // MARK: - Variables 561 | var protocolRequirement: Int = 0 562 | 563 | // MARK: - Methods 564 | func requiredInProtocol() { 565 | requiredInProtocolCallCount += 1 566 | if let __requiredInProtocolHandler = self.requiredInProtocolHandler { 567 | __requiredInProtocolHandler() 568 | } 569 | } 570 | var requiredInProtocolCallCount: Int = 0 571 | var requiredInProtocolHandler: (() -> ())? = nil 572 | } 573 | 574 | // MARK: - RealmInterop 575 | class RealmInteropMock<_Element, _KeyType, _S>: RealmInterop where _Element: Object, _S.Iterator.Element: Object, _S: Sequence { 576 | 577 | // MARK: - Generic typealiases 578 | typealias Element = _Element 579 | typealias KeyType = _KeyType 580 | typealias S = _S 581 | 582 | // MARK: - Variables 583 | var autorefresh: Bool = false { 584 | didSet { 585 | autorefreshSetCount += 1 586 | } 587 | } 588 | var autorefreshSetCount: Int = 0 589 | var isInWriteTransaction: Bool = false 590 | 591 | // MARK: - Methods 592 | func add(_ object: Object, update: Bool) { 593 | addObjectCallCount += 1 594 | if let __addObjectHandler = self.addObjectHandler { 595 | __addObjectHandler(object, update) 596 | } 597 | } 598 | var addObjectCallCount: Int = 0 599 | var addObjectHandler: ((_ object: Object, _ update: Bool) -> ())? = nil 600 | func add(_ objects: S, update: Bool) { 601 | addObjectsCallCount += 1 602 | if let __addObjectsHandler = self.addObjectsHandler { 603 | __addObjectsHandler(objects as! _S, update) 604 | } 605 | } 606 | var addObjectsCallCount: Int = 0 607 | var addObjectsHandler: ((_ objects: S, _ update: Bool) -> ())? = nil 608 | func beginWrite() { 609 | beginWriteCallCount += 1 610 | if let __beginWriteHandler = self.beginWriteHandler { 611 | __beginWriteHandler() 612 | } 613 | } 614 | var beginWriteCallCount: Int = 0 615 | var beginWriteHandler: (() -> ())? = nil 616 | func cancelWrite() { 617 | cancelWriteCallCount += 1 618 | if let __cancelWriteHandler = self.cancelWriteHandler { 619 | __cancelWriteHandler() 620 | } 621 | } 622 | var cancelWriteCallCount: Int = 0 623 | var cancelWriteHandler: (() -> ())? = nil 624 | func commitWrite(withoutNotifying tokens: [NotificationToken]) throws { 625 | commitWriteCallCount += 1 626 | if let __commitWriteHandler = self.commitWriteHandler { 627 | try __commitWriteHandler(tokens) 628 | } 629 | } 630 | var commitWriteCallCount: Int = 0 631 | var commitWriteHandler: ((_ tokens: [NotificationToken]) throws -> ())? = nil 632 | func create(_ type: Element.Type, value: Any, update: Bool) -> Element where Element: Object { 633 | createCallCount += 1 634 | if let __createHandler = self.createHandler { 635 | return __createHandler(type as! _Element.Type, value, update) as! Element 636 | } 637 | fatalError("createHandler expected to be set.") 638 | } 639 | var createCallCount: Int = 0 640 | var createHandler: ((_ type: Element.Type, _ value: Any, _ update: Bool) -> (Element))? = nil 641 | func deleteAll() { 642 | deleteAllCallCount += 1 643 | if let __deleteAllHandler = self.deleteAllHandler { 644 | __deleteAllHandler() 645 | } 646 | } 647 | var deleteAllCallCount: Int = 0 648 | var deleteAllHandler: (() -> ())? = nil 649 | func delete(_ objects: List) { 650 | deleteListCallCount += 1 651 | if let __deleteListHandler = self.deleteListHandler { 652 | __deleteListHandler(objects as! List<_Element>) 653 | } 654 | } 655 | var deleteListCallCount: Int = 0 656 | var deleteListHandler: ((_ objects: List) -> ())? = nil 657 | func delete(_ object: Object) { 658 | deleteObjectCallCount += 1 659 | if let __deleteObjectHandler = self.deleteObjectHandler { 660 | __deleteObjectHandler(object) 661 | } 662 | } 663 | var deleteObjectCallCount: Int = 0 664 | var deleteObjectHandler: ((_ object: Object) -> ())? = nil 665 | func delete(_ objects: S) { 666 | deleteObjectsCallCount += 1 667 | if let __deleteObjectsHandler = self.deleteObjectsHandler { 668 | __deleteObjectsHandler(objects as! _S) 669 | } 670 | } 671 | var deleteObjectsCallCount: Int = 0 672 | var deleteObjectsHandler: ((_ objects: S) -> ())? = nil 673 | func delete(_ objects: Results) { 674 | deleteResultsCallCount += 1 675 | if let __deleteResultsHandler = self.deleteResultsHandler { 676 | __deleteResultsHandler(objects as! Results<_Element>) 677 | } 678 | } 679 | var deleteResultsCallCount: Int = 0 680 | var deleteResultsHandler: ((_ objects: Results) -> ())? = nil 681 | func invalidate() { 682 | invalidateCallCount += 1 683 | if let __invalidateHandler = self.invalidateHandler { 684 | __invalidateHandler() 685 | } 686 | } 687 | var invalidateCallCount: Int = 0 688 | var invalidateHandler: (() -> ())? = nil 689 | func object(ofType type: Element.Type, forPrimaryKey key: KeyType) -> Element? where Element: Object { 690 | objectCallCount += 1 691 | if let __objectHandler = self.objectHandler { 692 | return __objectHandler(type as! _Element.Type, key as! _KeyType) as! Element? 693 | } 694 | return nil 695 | } 696 | var objectCallCount: Int = 0 697 | var objectHandler: ((_ type: Element.Type, _ key: KeyType) -> (Element?))? = nil 698 | func objects(_ type: Element.Type) -> Results where Element: Object { 699 | objectsCallCount += 1 700 | if let __objectsHandler = self.objectsHandler { 701 | return __objectsHandler(type as! _Element.Type) as! Results 702 | } 703 | fatalError("objectsHandler expected to be set.") 704 | } 705 | var objectsCallCount: Int = 0 706 | var objectsHandler: ((_ type: Element.Type) -> (Results))? = nil 707 | func observe(_ block: RealmNotificationInteropBlock) -> NotificationToken { 708 | observeCallCount += 1 709 | if let __observeHandler = self.observeHandler { 710 | return __observeHandler(block) 711 | } 712 | fatalError("observeHandler expected to be set.") 713 | } 714 | var observeCallCount: Int = 0 715 | var observeHandler: ((_ block: RealmNotificationInteropBlock) -> (NotificationToken))? = nil 716 | func refresh() -> Bool { 717 | refreshCallCount += 1 718 | if let __refreshHandler = self.refreshHandler { 719 | return __refreshHandler() 720 | } 721 | return false 722 | } 723 | var refreshCallCount: Int = 0 724 | var refreshHandler: (() -> (Bool))? = nil 725 | func write(_ block: () throws -> Void) throws { 726 | writeCallCount += 1 727 | if let __writeHandler = self.writeHandler { 728 | try __writeHandler(block) 729 | } 730 | } 731 | var writeCallCount: Int = 0 732 | var writeHandler: ((_ block: () throws -> Void) throws -> ())? = nil 733 | func writeCopy(toFile fileURL: URL, encryptionKey: Data?) throws { 734 | writeCopyCallCount += 1 735 | if let __writeCopyHandler = self.writeCopyHandler { 736 | try __writeCopyHandler(fileURL, encryptionKey) 737 | } 738 | } 739 | var writeCopyCallCount: Int = 0 740 | var writeCopyHandler: ((_ fileURL: URL, _ encryptionKey: Data?) throws -> ())? = nil 741 | } 742 | 743 | // MARK: - Routing 744 | class RoutingMock<_InteractorType>: Routing where _InteractorType: Interactable { 745 | 746 | // MARK: - Generic typealiases 747 | typealias InteractorType = _InteractorType 748 | 749 | // MARK: - Variables 750 | var interactor: InteractorType 751 | 752 | // MARK: - Initializer 753 | init(interactor: InteractorType) { 754 | self.interactor = interactor 755 | } 756 | } 757 | 758 | // MARK: - ShapesAccessing 759 | class ShapesAccessingMock: ShapesAccessing { 760 | 761 | // MARK: - Variables 762 | var shapes: Observable<[Shape1]> { 763 | shapesGetCount += 1 764 | if let handler = shapesGetHandler { 765 | return handler() 766 | } 767 | return shapesSubject.asObservable() 768 | } 769 | var shapesGetCount: Int = 0 770 | var shapesGetHandler: (() -> Observable<[Shape1]>)? = nil 771 | lazy var shapesSubject = PublishSubject<[Shape1]>() 772 | } 773 | 774 | // MARK: - SomeInteractable 775 | class SomeInteractableMock: SomeInteractable { 776 | } 777 | 778 | // MARK: - SomeRouting 779 | class SomeRoutingMock<_InteractorType>: SomeRouting where _InteractorType: SomeInteractable { 780 | 781 | // MARK: - Generic typealiases 782 | typealias InteractorType = _InteractorType 783 | 784 | // MARK: - Variables 785 | var interactor: InteractorType 786 | 787 | // MARK: - Initializer 788 | init(interactor: InteractorType) { 789 | self.interactor = interactor 790 | } 791 | } 792 | 793 | // MARK: - ThrowingGenericBuildable 794 | class ThrowingGenericBuildableMock<_EventType>: ThrowingGenericBuildable { 795 | 796 | // MARK: - Generic typealiases 797 | typealias EventType = _EventType 798 | 799 | // MARK: - Methods 800 | func build() throws -> AnyErrorPopoverPresentable { 801 | buildCallCount += 1 802 | if let __buildHandler = self.buildHandler { 803 | return try __buildHandler() 804 | } 805 | fatalError("buildHandler expected to be set.") 806 | } 807 | var buildCallCount: Int = 0 808 | var buildHandler: (() throws -> (AnyErrorPopoverPresentable))? = nil 809 | } 810 | 811 | // MARK: - ThumbCreating 812 | class ThumbCreatingMock: ThumbCreating { 813 | 814 | // MARK: - Methods 815 | func createThumbImage(for pictureUrl: URL, fitting size: CGSize) throws -> NSImage { 816 | createThumbImageCallCount += 1 817 | if let __createThumbImageHandler = self.createThumbImageHandler { 818 | return try __createThumbImageHandler(pictureUrl, size) 819 | } 820 | fatalError("createThumbImageHandler expected to be set.") 821 | } 822 | var createThumbImageCallCount: Int = 0 823 | var createThumbImageHandler: ((_ pictureUrl: URL, _ size: CGSize) throws -> (NSImage))? = nil 824 | func createThumbJpegData(for pictureUrl: URL, fitting size: CGSize, compression: Double) throws -> Data { 825 | createThumbJpegDataCallCount += 1 826 | if let __createThumbJpegDataHandler = self.createThumbJpegDataHandler { 827 | return try __createThumbJpegDataHandler(pictureUrl, size, compression) 828 | } 829 | fatalError("createThumbJpegDataHandler expected to be set.") 830 | } 831 | var createThumbJpegDataCallCount: Int = 0 832 | var createThumbJpegDataHandler: ((_ pictureUrl: URL, _ size: CGSize, _ compression: Double) throws -> (Data))? = nil 833 | } 834 | 835 | // MARK: - TipsAccessing 836 | class TipsAccessingMock: TipsAccessing { 837 | 838 | // MARK: - Variables 839 | var tip: Tip { 840 | tipGetCount += 1 841 | if let handler = tipGetHandler { 842 | return handler() 843 | } 844 | fatalError("`tipGetHandler` must be set!") 845 | } 846 | var tipGetCount: Int = 0 847 | var tipGetHandler: (() -> Tip)? = nil 848 | } 849 | 850 | // MARK: - TipsManagerBuilding 851 | class TipsManagerBuildingMock: TipsManagerBuilding { 852 | 853 | // MARK: - Methods 854 | func build() -> TipsManaging & MutableTipsManaging { 855 | buildCallCount += 1 856 | if let __buildHandler = self.buildHandler { 857 | return __buildHandler() 858 | } 859 | fatalError("buildHandler expected to be set.") 860 | } 861 | var buildCallCount: Int = 0 862 | var buildHandler: (() -> (TipsManaging & MutableTipsManaging))? = nil 863 | } 864 | 865 | // MARK: - TipsManaging 866 | class TipsManagingMock: TipsManaging { 867 | 868 | // MARK: - Variables 869 | var arrayVariable: [Double] = [] 870 | var tips: [String: String] = [:] 871 | var tipsOptional: [String: String]? = nil 872 | var tupleOptional: (String, Int)? = nil 873 | var tupleVariable: (String, Int) = ("", 0) 874 | var tupleVariable2: (String?, Int?) = (nil, nil) 875 | } 876 | 877 | // MARK: - UploadProgressing 878 | class UploadProgressingMock: UploadProgressing { 879 | 880 | // MARK: - Variables 881 | var progress: Observable> { 882 | progressGetCount += 1 883 | if let handler = progressGetHandler { 884 | return handler() 885 | } 886 | return progressSubject.asObservable() 887 | } 888 | var progressGetCount: Int = 0 889 | var progressGetHandler: (() -> Observable>)? = nil 890 | lazy var progressSubject = PublishSubject>() 891 | 892 | // MARK: - Methods 893 | func fileProgress(_ source: UploadAPI.LocalFile.Source) -> Observable { 894 | fileProgressCallCount += 1 895 | if let __fileProgressHandler = self.fileProgressHandler { 896 | return __fileProgressHandler(source) 897 | } 898 | return fileProgressSubject.asObservable() 899 | } 900 | var fileProgressCallCount: Int = 0 901 | var fileProgressHandler: ((_ source: UploadAPI.LocalFile.Source) -> (Observable))? = nil 902 | lazy var fileProgressSubject = PublishSubject() 903 | } 904 | -------------------------------------------------------------------------------- /Examples/ExampleProjectCocoapods/SwiftSourceryTemplates/SwiftSourceryTemplatesTests/SwiftSourceryTemplatesTests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SwiftSourceryTemplatesTests.swift 3 | // SwiftSourceryTemplatesTests 4 | // 5 | // Created by Ivan Misuno on 18/07/2018. 6 | // Copyright © 2018 AlbumPrinter BV. All rights reserved. 7 | // 8 | 9 | import XCTest 10 | @testable import SwiftSourceryTemplates 11 | 12 | class SwiftSourceryTemplatesTests: XCTestCase { 13 | 14 | override func setUp() { 15 | super.setUp() 16 | // Put setup code here. This method is called before the invocation of each test method in the class. 17 | } 18 | 19 | override func tearDown() { 20 | // Put teardown code here. This method is called after the invocation of each test method in the class. 21 | super.tearDown() 22 | } 23 | 24 | func testExample() { 25 | // This is an example of a functional test case. 26 | // Use XCTAssert and related functions to verify your tests produce the correct results. 27 | } 28 | 29 | func testPerformanceExample() { 30 | // This is an example of a performance test case. 31 | self.measure { 32 | // Put the code you want to measure the time of here. 33 | } 34 | } 35 | 36 | } 37 | -------------------------------------------------------------------------------- /Examples/ExampleProjectSpm/Package.resolved: -------------------------------------------------------------------------------- 1 | { 2 | "pins" : [ 3 | { 4 | "identity" : "alamofire", 5 | "kind" : "remoteSourceControl", 6 | "location" : "https://github.com/Alamofire/Alamofire.git", 7 | "state" : { 8 | "revision" : "747c8db8d57b68d5e35275f10c92d55f982adbd4", 9 | "version" : "4.9.1" 10 | } 11 | }, 12 | { 13 | "identity" : "cwlcatchexception", 14 | "kind" : "remoteSourceControl", 15 | "location" : "https://github.com/mattgallagher/CwlCatchException.git", 16 | "state" : { 17 | "revision" : "3b123999de19bf04905bc1dfdb76f817b0f2cc00", 18 | "version" : "2.1.2" 19 | } 20 | }, 21 | { 22 | "identity" : "cwlpreconditiontesting", 23 | "kind" : "remoteSourceControl", 24 | "location" : "https://github.com/mattgallagher/CwlPreconditionTesting.git", 25 | "state" : { 26 | "revision" : "dc9af4781f2afdd1e68e90f80b8603be73ea7abc", 27 | "version" : "2.2.0" 28 | } 29 | }, 30 | { 31 | "identity" : "nimble", 32 | "kind" : "remoteSourceControl", 33 | "location" : "https://github.com/Quick/Nimble.git", 34 | "state" : { 35 | "revision" : "d616f15123bfb36db1b1075153f73cf40605b39d", 36 | "version" : "13.0.0" 37 | } 38 | }, 39 | { 40 | "identity" : "quick", 41 | "kind" : "remoteSourceControl", 42 | "location" : "https://github.com/Quick/Quick.git", 43 | "state" : { 44 | "revision" : "ef9aaf3f634b3a1ab6f54f1173fe2400b36e7cb8", 45 | "version" : "7.3.0" 46 | } 47 | }, 48 | { 49 | "identity" : "ribs", 50 | "kind" : "remoteSourceControl", 51 | "location" : "https://github.com/uber/RIBs.git", 52 | "state" : { 53 | "revision" : "c9b4c012ef7280f15e52813fd8765989ae6d5b49", 54 | "version" : "0.16.1" 55 | } 56 | }, 57 | { 58 | "identity" : "rxswift", 59 | "kind" : "remoteSourceControl", 60 | "location" : "https://github.com/ReactiveX/RxSwift.git", 61 | "state" : { 62 | "revision" : "9dcaa4b333db437b0fbfaf453fad29069044a8b4", 63 | "version" : "6.6.0" 64 | } 65 | } 66 | ], 67 | "version" : 2 68 | } 69 | -------------------------------------------------------------------------------- /Examples/ExampleProjectSpm/Package.swift: -------------------------------------------------------------------------------- 1 | // swift-tools-version: 5.9 2 | import PackageDescription 3 | 4 | let package = Package( 5 | name: "ExampleProjectSpm", 6 | platforms: [ 7 | .iOS(.v13) 8 | ], 9 | products: [ 10 | .library(name: "ExampleProjectSpm", targets: ["ExampleProjectSpm"]), 11 | ], 12 | dependencies: [ 13 | .package(url: "https://github.com/Alamofire/Alamofire.git", "4.0.0"..<"5.0.0"), 14 | .package(url: "https://github.com/uber/RIBs.git", from: "0.16.1"), 15 | .package(url: "https://github.com/ReactiveX/RxSwift.git", from: "6.6.0"), 16 | .package(url: "https://github.com/Quick/Quick.git", from: "7.3.0"), 17 | .package(url: "https://github.com/Quick/Nimble.git", from: "13.0.0"), 18 | .package(name: "swift-sourcery-templates", path: "../.."), 19 | ], 20 | targets: [ 21 | .target( 22 | name: "ExampleProjectSpm", 23 | dependencies: [ 24 | .product(name: "Alamofire", package: "Alamofire"), 25 | .product(name: "RIBs", package: "RIBs"), 26 | .product(name: "RxSwift", package: "RxSwift"), 27 | ], 28 | plugins: [ 29 | .plugin(name: "SourcerySwiftCodegenPlugin", package: "swift-sourcery-templates") 30 | ] 31 | ), 32 | .testTarget( 33 | name: "ExampleProjectSpmTests", 34 | dependencies: [ 35 | .product(name: "RIBs", package: "RIBs"), 36 | .product(name: "RxBlocking", package: "RxSwift"), 37 | .product(name: "RxTest", package: "RxSwift"), 38 | .product(name: "Quick", package: "Quick"), 39 | .product(name: "Nimble", package: "Nimble"), 40 | .target(name: "ExampleProjectSpm"), 41 | ], 42 | plugins: [ 43 | .plugin(name: "SourcerySwiftCodegenPlugin", package: "swift-sourcery-templates") 44 | ] 45 | ), 46 | ] 47 | ) 48 | -------------------------------------------------------------------------------- /Examples/ExampleProjectSpm/Sources/ExampleProjectSpm/.Sourcery.TypeErase.yml: -------------------------------------------------------------------------------- 1 | # package: 2 | # path: ../.. #${SOURCERY_PACKAGE_PATH} 3 | # target: 4 | # - ExampleProjectSpm 5 | sources: 6 | - ${SOURCERY_TARGET_ExampleProjectSpm} 7 | output: ${SOURCERY_OUTPUT_DIR} 8 | templates: 9 | - ${GIT_ROOT}/templates/TypeErase.swifttemplate 10 | args: 11 | import: 12 | - RIBs 13 | - RxSwift 14 | - UIKit 15 | excludedSwiftLintRules: 16 | - function_body_length 17 | - line_length 18 | - vertical_whitespace 19 | -------------------------------------------------------------------------------- /Examples/ExampleProjectSpm/Sources/ExampleProjectSpm/Protocols/Protocols.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Protocols.swift 3 | // SwiftSourceryTemplates 4 | // 5 | // Created by Ivan Misuno on 18/07/2018. 6 | // Copyright © 2018 AlbumPrinter BV. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import UIKit 11 | import RxSwift 12 | import Alamofire // for `Result` 13 | import SwiftUI 14 | import Combine 15 | 16 | // sourcery: CreateMock 17 | protocol DataSource { 18 | var bindingTarget: AnyObserver { get } 19 | func bindStreams() -> Disposable 20 | } 21 | 22 | // sourcery: CreateMock 23 | protocol UploadProgressing { 24 | var progress: Observable> { get } 25 | func fileProgress(_ source: UploadAPI.LocalFile.Source) -> Observable 26 | } 27 | 28 | // sourcery: CreateMock 29 | protocol FileService { 30 | func upload(fileUrl: URL) -> Single<[FilePart]> 31 | } 32 | 33 | // sourcery: CreateMock 34 | protocol MutableUploadProgressing: UploadProgressing { 35 | func setInputFiles(localFiles: [UploadAPI.LocalFile]) 36 | func uploadIdRetrieved(localFile: UploadAPI.LocalFile, uploadId: UploadAPI.UploadId) 37 | func filePartsCreated(uploadId: UploadAPI.UploadId, fileParts: [FilePart]) 38 | func filePartProgressed(uploadId: UploadAPI.UploadId, filePart: FilePart, progress: RxProgress) 39 | func downloadUrlsRetrieved() 40 | } 41 | 42 | // sourcery: CreateMock 43 | protocol ThumbCreating { 44 | func createThumbImage(for pictureUrl: URL, fitting size: CGSize) throws -> UIImage 45 | func createThumbJpegData(for pictureUrl: URL, fitting size: CGSize, compression: Double) throws -> Data 46 | } 47 | 48 | // sourcery: CreateMock 49 | protocol ImageAttributeProviding { 50 | func imagePixelSize(source: UploadAPI.LocalFile.Source) throws -> CGSize 51 | } 52 | 53 | // sourcery: CreateMock 54 | protocol AlbumPageSizeProviderDelegate: AnyObject { 55 | var onComplete: AnyObserver<()> { get } 56 | } 57 | 58 | // sourcery: CreateMock 59 | protocol AlbumPageSizeProviding { 60 | var delegate: AlbumPageSizeProviderDelegate? { get set } 61 | var pageSize: CGSize { get } 62 | } 63 | 64 | // sourcery: CreateMock 65 | protocol ExifImageAttributeProviding { 66 | func dateTaken(fileUrl: URL) -> Date? 67 | } 68 | 69 | // sourcery: CreateMock 70 | protocol ProtocolWithCollections { 71 | var items: Set { get } 72 | var data: Array { get } 73 | var mapping: Dictionary { get } 74 | 75 | func getItems() -> Set 76 | func getData() -> Array 77 | func getMapping() -> Dictionary 78 | } 79 | 80 | /// sourcery: CreateMock 81 | protocol DuplicateGenericTypeNames { 82 | // sourcery: generictype = T 83 | func action( 84 | // sourcery: annotatedGenericTypes = "{T}" 85 | _ a: T) 86 | // sourcery: generictype = T 87 | func action2( 88 | // sourcery: annotatedGenericTypes = "{T}" 89 | _ a: T) 90 | } 91 | 92 | /// sourcery: CreateMock 93 | protocol ErrorPopoverBuildable { 94 | // sourcery: generictype = T1 95 | func buildDefaultPopoverPresenter(title: String) -> AnyErrorPopoverPresentable 96 | // sourcery: generictype = T2 97 | func buildPopoverPresenter( 98 | title: String, 99 | // sourcery: annotatedGenericTypes = "[(title: String, identifier: {T2}, handler: ()->())]" 100 | buttons: [(title: String, identifier: T2, handler: ()->())]) -> AnyErrorPopoverPresentable 101 | } 102 | 103 | /// sourcery: CreateMock 104 | protocol ErrorPopoverBuildableRawRepresentable { 105 | // sourcery: generictype = "T: RawRepresentable, T: Hashable" 106 | func buildPopoverPresenter( 107 | title: String, 108 | // sourcery: annotatedGenericTypes = "[(title: String, identifier: {T}, handler: ()->())]" 109 | buttons: [(title: String, identifier: T, handler: ()->())]) -> AnyErrorPopoverPresentableRawRepresentable where T: RawRepresentable, T: Hashable 110 | } 111 | 112 | /// sourcery: CreateMock 113 | protocol ErrorPresenting { 114 | // sourcery: genericType = "T: RawRepresentable, T: Hashable" 115 | func presentErrorPopover( 116 | // sourcery: genericTypePlaceholder = "AnyErrorPopoverPresentable<{T}>" 117 | _ popoverPresenter: AnyErrorPopoverPresentable) -> Observable where T: RawRepresentable, T: Hashable 118 | } 119 | 120 | /// sourcery: CreateMock 121 | /// sourcery: TypeErase 122 | /// sourcery: associatedtype = EventType 123 | protocol ErrorPopoverPresentable { 124 | associatedtype EventType 125 | //func show(relativeTo positioningRect: CGRect, of positioningView: UIView, preferredEdge: CGRectEdge) -> Observable 126 | func setActionSink(_ actionSink: AnyObject?) 127 | } 128 | 129 | /// sourcery: CreateMock 130 | /// sourcery: TypeErase 131 | /// sourcery: associatedtype = "EventType: RawRepresentable, EventType: Hashable" 132 | protocol ErrorPopoverPresentableRawRepresentable { 133 | associatedtype EventType: RawRepresentable, Hashable 134 | //func show(relativeTo positioningRect: CGRect, of positioningView: UIView, preferredEdge: CGRectEdge) -> Observable 135 | } 136 | 137 | /// sourcery: CreateMock 138 | /// sourcery: TypeErase 139 | /// sourcery: associatedtype = "EventType" 140 | protocol ThrowingGenericBuildable { 141 | associatedtype EventType 142 | func build() throws -> AnyErrorPopoverPresentable 143 | } 144 | 145 | /// sourcery: CreateMock 146 | protocol TipsManaging: AnyObject { 147 | var tips: [String: String] { get } 148 | var tipsOptional: [String: String]? { get } 149 | var tupleVariable: (String, Int) { get } 150 | var tupleVariable2: (String?, Int?) { get } 151 | var tupleOptional: (String, Int)? { get } 152 | var arrayVariable: [Double] { get } 153 | } 154 | 155 | /// sourcery: CreateMock 156 | @objc protocol LegacyProtocol: NSObjectProtocol { 157 | var tips: [String: String] { get } 158 | func compare(_ lhs: CGSize, _ rhs: CGSize) -> ComparisonResult 159 | } 160 | 161 | /// sourcery: CreateMock 162 | protocol DuplicateRequirements { 163 | var tips: [String: String] { get } 164 | func updateTips(_ tips: [Tip]) 165 | func updateTips(with tips: AnySequence) throws 166 | } 167 | 168 | /// sourcery: CreateMock 169 | protocol DuplicateRequirementsSameLevel { 170 | func update(cropRect: CGRect) 171 | func update(cropHandles: [CGPoint]) 172 | func update(effects: [String]) 173 | } 174 | 175 | /// sourcery: CreateMock 176 | protocol MutableTipsManaging: TipsManaging, DuplicateRequirements { 177 | func updateTips(_ tips: [Tip]) 178 | func updateTips(with tips: AnySequence) throws 179 | } 180 | 181 | /// sourcery: CreateMock 182 | protocol TipsAccessing { 183 | /// sourcery: handler 184 | var tip: Tip { get } 185 | } 186 | 187 | /// sourcery: CreateMock 188 | protocol MutableTipsAccessing { 189 | /// sourcery: handler 190 | var tip: Tip { get set } 191 | } 192 | 193 | /// sourcery: CreateMock 194 | protocol TipsManagerBuilding { 195 | func build() -> TipsManaging & MutableTipsManaging 196 | } 197 | 198 | /// sourcery: CreateMock 199 | protocol ObjectManupulating { 200 | @discardableResult 201 | func removeObject() -> Int 202 | 203 | @discardableResult 204 | func removeObject(where matchPredicate: @escaping (Any) throws -> (Bool)) rethrows -> Int 205 | 206 | @discardableResult 207 | func removeObject(_ object: @autoclosure () throws -> Any) rethrows -> Int 208 | } 209 | 210 | /// sourcery: CreateMock 211 | @objc public protocol UIKitEvent { 212 | // sourcery: const, init 213 | var locationInWindow: CGPoint { get } 214 | // sourcery: const, init 215 | var modifierFlags: UIEvent.EventType { get } 216 | } 217 | 218 | enum ShapeType: CaseIterable { 219 | case circle 220 | case rect 221 | case tri 222 | } 223 | 224 | typealias Shape1 = ( 225 | type: ShapeType, 226 | origin: CGPoint, 227 | size: CGFloat, 228 | linearSpeed: CGPoint, 229 | angle: CGFloat, 230 | angularSpeed: CGFloat, 231 | strokeColor: UIColor, 232 | fillColor: UIColor 233 | ) 234 | 235 | ///// sourcery: CreateMock 236 | //protocol ShapesAccessing { 237 | // var shapes: Observable<[Shape1]> { get } 238 | //} 239 | 240 | /// sourcery: CreateMock 241 | protocol ProtocolWithExtensions { 242 | var protocolRequirement: Int { get } 243 | func requiredInProtocol() 244 | } 245 | 246 | extension ProtocolWithExtensions { 247 | var implementedInExtension: Int { 248 | return 5 249 | } 250 | 251 | func extended(completionCallback callback: (() -> ())?) { 252 | callback?() 253 | } 254 | } 255 | 256 | /// sourcery: CreateMock 257 | // protocol EdgeCases { 258 | // func functionMissingArgumentLabel(_: Int) 259 | // } 260 | 261 | typealias RealmNotificationInteropBlock = (_ notification: Notification, _ realmInterop: RealmInterop) -> () 262 | class RLObject {} 263 | class RLList {} 264 | class RLResults {} 265 | struct RLNotificationToken {} 266 | 267 | /// sourcery: CreateMock 268 | protocol RealmInterop: AnyObject 269 | { 270 | func write(_ block: (() throws -> Void)) throws 271 | func beginWrite() 272 | func commitWrite(withoutNotifying tokens: [RLNotificationToken]) throws 273 | func cancelWrite() 274 | var isInWriteTransaction: Bool { get } 275 | 276 | /// sourcery: methodName = addObject 277 | func add(_ object: RLObject, update: Bool) 278 | 279 | /// sourcery: methodName = addObjects 280 | /// sourcery: genericType = "S: Sequence, S.Iterator.Element: RLObject" 281 | func add( 282 | // sourcery: annotatedGenericTypes = "{S}" 283 | _ objects: S, 284 | update: Bool) where S: Sequence, S.Iterator.Element: RLObject 285 | 286 | /// sourcery: genericType = "Element: RLObject" 287 | func create( 288 | // sourcery: annotatedGenericTypes = "{Element}.Type" 289 | _ type: Element.Type, 290 | value: Any, 291 | update: Bool) -> Element where Element: RLObject 292 | 293 | /// sourcery: methodName = deleteObject 294 | func delete(_ object: RLObject) 295 | 296 | /// sourcery: methodName = deleteObjects 297 | /// sourcery: genericType = "S: Sequence, S.Iterator.Element: RLObject" 298 | func delete( 299 | // sourcery: annotatedGenericTypes = "{S}" 300 | _ objects: S) where S: Sequence, S.Iterator.Element: RLObject 301 | 302 | /// sourcery: methodName = deleteList 303 | /// sourcery: genericType = "Element: RLObject" 304 | func delete( 305 | // sourcery: annotatedGenericTypes = "RLList<{Element}>" 306 | _ objects: RLList) where Element: RLObject 307 | 308 | /// sourcery: methodName = deleteResults 309 | /// sourcery: genericType = "Element: RLObject" 310 | func delete( 311 | // sourcery: annotatedGenericTypes = "RLResults<{Element}>" 312 | _ objects: RLResults) where Element: RLObject 313 | 314 | func deleteAll() 315 | 316 | /// sourcery: genericType = "Element: RLObject" 317 | func objects( 318 | // sourcery: annotatedGenericTypes = "{Element}.Type" 319 | _ type: Element.Type) -> RLResults where Element: RLObject 320 | 321 | /// sourcery: genericType = "Element: RLObject, KeyType" 322 | func object( 323 | // sourcery: annotatedGenericTypes = "{Element}.Type" 324 | ofType type: Element.Type, 325 | // sourcery: annotatedGenericTypes = "{KeyType}" 326 | forPrimaryKey key: KeyType) -> Element? where Element: RLObject 327 | 328 | func observe(_ block: @escaping RealmNotificationInteropBlock) -> RLNotificationToken 329 | var autorefresh: Bool { get set } 330 | func refresh() -> Bool 331 | func invalidate() 332 | func writeCopy(toFile fileURL: URL, encryptionKey: Data?) throws 333 | } 334 | 335 | /// sourcery: CreateMock 336 | /// sourcery: TypeErase 337 | /// sourcery: associatedType = "DocumentType: Codable" 338 | public protocol CodableDocumentStoring { 339 | associatedtype DocumentType: Codable 340 | 341 | var documentId: String { get } 342 | 343 | func get( 344 | /// sourcery: annotatedGenericTypes = "{DocumentType.Type}" 345 | as type: DocumentType.Type) -> Swift.Result 346 | 347 | // func observe( 348 | // /// sourcery: annotatedGenericTypes = "{DocumentType.Type}" 349 | // as type: DocumentType.Type) -> Observable 350 | 351 | func set( 352 | /// sourcery: annotatedGenericTypes = "{DocumentType}" 353 | _ data: DocumentType) -> Single 354 | 355 | func delete() -> Single 356 | } 357 | 358 | /// sourcery: CreateMock 359 | public protocol LearningSessionsCollectionStoring { 360 | func latestSession() -> Observable<(any CodableDocumentStoring)?> 361 | } 362 | 363 | /// sourcery: CreateMock 364 | public protocol SomeEntityBindable { 365 | func entityObserver() -> AnyObserver 366 | } 367 | 368 | /// sourcery: CreateMock 369 | /// sourcery: associatedtype = "TaskAuxilliaryView: View" 370 | public protocol MultiplicationByCountingTaskVariationBuildable { 371 | associatedtype TaskAuxilliaryView: View 372 | func taskAuxilliaryView( 373 | answer: CurrentValueSubject, 374 | cancellables: inout Set) -> TaskAuxilliaryView 375 | } 376 | -------------------------------------------------------------------------------- /Examples/ExampleProjectSpm/Sources/ExampleProjectSpm/Types/Types.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Types.swift 3 | // SwiftSourceryTemplates 4 | // 5 | // Created by Ivan Misuno on 18/07/2018. 6 | // Copyright © 2018 AlbumPrinter BV. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | struct UploadAPI { 12 | typealias UploadId = String 13 | 14 | struct LocalFile { 15 | enum Source { 16 | } 17 | } 18 | } 19 | 20 | struct FilePart { 21 | } 22 | 23 | struct RxProgress { 24 | } 25 | 26 | struct Tip { 27 | } 28 | -------------------------------------------------------------------------------- /Examples/ExampleProjectSpm/Sources/ExampleProjectSpmTests/.Sourcery.Mocks.yml: -------------------------------------------------------------------------------- 1 | # package: 2 | # path: ../.. #${SOURCERY_PACKAGE_PATH} 3 | # target: 4 | # - ExampleProjectSpm 5 | # - ExampleProjectSpmTests 6 | sources: 7 | - ${SOURCERY_TARGET_ExampleProjectSpm} 8 | - ${SOURCERY_TARGET_ExampleProjectSpm_DEP_RIBs_MODULE_RIBs} 9 | - ${SOURCERY_TARGET_ExampleProjectSpmTests}/SourceryAnnotations 10 | output: ${SOURCERY_OUTPUT_DIR} 11 | templates: 12 | - ${GIT_ROOT}/templates/Mocks.swifttemplate 13 | args: 14 | testable: 15 | - ExampleProjectSpm 16 | import: 17 | - Alamofire 18 | - Combine 19 | - RIBs 20 | - RxBlocking 21 | - RxSwift 22 | - RxTest 23 | - SwiftUI 24 | - UIKit 25 | excludedSwiftLintRules: 26 | - force_cast 27 | - function_body_length 28 | - line_length 29 | - vertical_whitespace 30 | -------------------------------------------------------------------------------- /Examples/ExampleProjectSpm/Sources/ExampleProjectSpmTests/SourceryAnnotations/RIBs+SourceryAnnotations.swift: -------------------------------------------------------------------------------- 1 | // 2 | // File.swift 3 | // 4 | // 5 | // Created by Ivan Misuno on 18/11/2023. 6 | // 7 | 8 | import RIBs 9 | 10 | /// sourcery: CreateMock 11 | extension Buildable {} 12 | 13 | /// sourcery: CreateMock 14 | extension InteractorScope {} 15 | 16 | /// sourcery: CreateMock 17 | extension Interactable {} 18 | 19 | /// sourcery: CreateMock 20 | extension LaunchRouting {} 21 | 22 | /// sourcery: CreateMock 23 | extension Presentable {} 24 | 25 | /// sourcery: CreateMock 26 | extension RouterLifecycle {} 27 | 28 | /// sourcery: CreateMock 29 | extension RouterScope {} 30 | 31 | /// sourcery: CreateMock 32 | extension Routing {} 33 | 34 | /// sourcery: CreateMock 35 | extension ViewControllable {} 36 | 37 | /// sourcery: CreateMock 38 | extension ViewableRouting {} 39 | -------------------------------------------------------------------------------- /Examples/ExampleProjectSpm/Sources/ExampleProjectSpmTests/SwiftSourceryTemplatesMocksSpec.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SwiftSourceryTemplatesTests.swift 3 | // SwiftSourceryTemplatesTests 4 | // 5 | // Created by Ivan Misuno on 18/07/2018. 6 | // Copyright © 2018 AlbumPrinter BV. All rights reserved. 7 | // 8 | 9 | import Quick 10 | import Nimble 11 | import RxSwift 12 | @testable import ExampleProjectSpm 13 | 14 | class SwiftSourceryTemplatesMocksSpec: QuickSpec { 15 | override static func spec() { 16 | describe("UploadProgressingMock") { 17 | var sut: UploadProgressingMock! 18 | beforeEach { 19 | sut = UploadProgressingMock() 20 | } 21 | it("progressGetCount == 0") { 22 | expect(sut.progressGetCount) == 0 23 | } 24 | } // describe("UploadProgressingMock") 25 | 26 | describe("InteractableMock") { 27 | var sut: InteractableMock! 28 | beforeEach { 29 | sut = InteractableMock() 30 | } 31 | it("activateCallCount == 0") { 32 | expect(sut.activateCallCount) == 0 33 | } 34 | } // describe("InteractableMock") 35 | 36 | describe("mock with AnyObserver") { 37 | var sut: SomeEntityBindableMock! 38 | beforeEach { 39 | sut = SomeEntityBindableMock() 40 | } 41 | describe("entityObserver() called on the mock") { 42 | beforeEach { 43 | _ = sut.entityObserver() 44 | } 45 | it("entityObserver call count increases") { 46 | expect(sut.entityObserverCallCount) == 1 47 | } 48 | context("entityObserver.on() called") { 49 | beforeEach { 50 | sut.entityObserver().onNext("next element") 51 | } 52 | it("entityObserverEvent call count increased") { 53 | expect(sut.entityObserverEventCallCount) == 1 54 | } 55 | } // context("entityObserver.on() called") 56 | } 57 | } // describe("mock with AnyObserver") 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /Examples/ExampleProjectSpm/Sources/ExampleProjectSpmTests/SwiftSourceryTemplatesTypeErasureSpec.swift: -------------------------------------------------------------------------------- 1 | // 2 | // File.swift 3 | // 4 | // 5 | // Created by Ivan Misuno on 18/11/2023. 6 | // 7 | 8 | import Quick 9 | import Nimble 10 | import UIKit 11 | import RxSwift 12 | @testable import ExampleProjectSpm 13 | 14 | enum ErrorEvent { 15 | case typeA 16 | case typeB 17 | } 18 | 19 | class MyErrorPresenter: ErrorPopoverPresentable { 20 | typealias EventType = ErrorEvent 21 | 22 | let subject = PublishSubject() 23 | 24 | func show(relativeTo positioningRect: CGRect, of positioningView: UIView, preferredEdge: CGRectEdge) -> Observable { 25 | return subject.asObservable() 26 | } 27 | 28 | func setActionSink(_ actionSink: AnyObject?) { 29 | } 30 | } 31 | 32 | class SwiftSourceryTemplatesTypeErasureSpec: QuickSpec { 33 | override static func spec() { 34 | var sut: AnyErrorPopoverPresentable! 35 | beforeEach { 36 | sut = AnyErrorPopoverPresentable(MyErrorPresenter()) 37 | } 38 | it("") { 39 | //_ = sut.show(relativeTo: .zero, of: UIView(), preferredEdge: .maxXEdge) 40 | } 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /Examples/ExampleProjectSpm/test-ios.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | xcodebuild test \ 4 | -scheme ExampleProjectSpm \ 5 | -destination 'platform=iOS Simulator,OS=17.4,name=iPhone 15' \ 6 | -configuration "Debug" \ 7 | -sdk "iphonesimulator" \ 8 | -skipPackagePluginValidation \ 9 | | xcbeautify 10 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | Copyright 2018 Ivan Misuno 2 | 3 | Licensed under the Apache License, Version 2.0 (the "License"); 4 | you may not use this file except in compliance with the License. 5 | You may obtain a copy of the License at 6 | 7 | http://www.apache.org/licenses/LICENSE-2.0 8 | 9 | Unless required by applicable law or agreed to in writing, software 10 | distributed under the License is distributed on an "AS IS" BASIS, 11 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | See the License for the specific language governing permissions and 13 | limitations under the License. 14 | -------------------------------------------------------------------------------- /Package.swift: -------------------------------------------------------------------------------- 1 | // swift-tools-version:5.9 2 | import PackageDescription 3 | 4 | let package = Package( 5 | name: "SourcerySwiftCodegen", 6 | platforms: [ 7 | .macOS(.v13) 8 | ], 9 | products: [ 10 | .plugin(name: "SourcerySwiftCodegenPlugin", targets: ["SourcerySwiftCodegenPlugin"]) 11 | ], 12 | targets: [ 13 | .binaryTarget( 14 | name: "sourcery", 15 | url: "https://github.com/krzysztofzablocki/Sourcery/releases/download/2.2.6/sourcery-2.2.6.artifactbundle.zip", 16 | checksum: "00ddb01d968cf5a1b9971a997f362553b2cf57ccdd437e7ecde9c7891ee9e4c1" 17 | ), 18 | .plugin( 19 | name: "SourcerySwiftCodegenPlugin", 20 | capability: .buildTool, 21 | dependencies: ["sourcery"] 22 | ), 23 | ] 24 | ) 25 | -------------------------------------------------------------------------------- /Plugins/SourcerySwiftCodegenPlugin/SourcerySwiftCodegenPlugin.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | import PackagePlugin 3 | 4 | protocol CodegenPluginContext { 5 | var rootDirectory: PackagePlugin.Path { get } 6 | var pluginWorkDirectory: PackagePlugin.Path { get } 7 | func tool(named name: String) throws -> PackagePlugin.PluginContext.Tool 8 | var environmentVars: [String: String] { get } 9 | } 10 | 11 | protocol CodegenPluginTarget { 12 | var name: String { get } 13 | func sourceryConfigFileLocations(rootDirectory: Path) -> Set 14 | } 15 | 16 | private func gitRootDirectory(_ path: Path) -> String { 17 | let task = Process() 18 | task.launchPath = "/usr/bin/env" 19 | task.currentDirectoryURL = URL(filePath: path.string) 20 | task.arguments = ["git", "rev-parse", "--show-toplevel"] 21 | 22 | let outputPipe = Pipe() 23 | let errorPipe = Pipe() 24 | task.standardOutput = outputPipe 25 | task.standardError = errorPipe 26 | let outHandle = outputPipe.fileHandleForReading 27 | let errorHandle = errorPipe.fileHandleForReading 28 | 29 | task.launch() 30 | 31 | let outputData = outHandle.readDataToEndOfFile() 32 | let errorData = errorHandle.readDataToEndOfFile() 33 | outHandle.closeFile() 34 | errorHandle.closeFile() 35 | 36 | task.waitUntilExit() 37 | 38 | let output = String(data: outputData, encoding: .utf8)?.trimmingCharacters(in: .whitespacesAndNewlines) ?? "" 39 | let error = String(data: errorData, encoding: .utf8)?.trimmingCharacters(in: .whitespacesAndNewlines) ?? "" 40 | 41 | guard task.terminationStatus == 0 else { 42 | Diagnostics.warning("Error running git command: \(task.terminationStatus): \(error)") 43 | return "" 44 | } 45 | 46 | return output 47 | } 48 | 49 | private func locateSourceryExecutable(_ context: CodegenPluginContext) throws -> Path { 50 | let sourcery = try context.tool(named: "sourcery").path 51 | if sourcery.fileExists && !sourcery.isDirectory && sourcery.isExecutable { 52 | return sourcery 53 | } 54 | 55 | // If artifactsbundle is zipped incorrectly, the execuitable ends up one nesting level deeper than needed. 56 | let sourceryNestedDirectory = sourcery.appending("bin", "sourcery") 57 | if sourceryNestedDirectory.fileExists && !sourceryNestedDirectory.isDirectory && sourceryNestedDirectory.isExecutable { 58 | return sourceryNestedDirectory 59 | } 60 | 61 | throw SourcerySwiftCodegenPluginError.sourceryNotFound(path: sourcery.string) 62 | } 63 | 64 | enum SourcerySwiftCodegenPluginError: Error, LocalizedError { 65 | case sourceryNotFound(path: String) 66 | 67 | var errorDescription: String? { 68 | switch self { 69 | case .sourceryNotFound(let path): 70 | return "Could not locate Sourcery executable in the tool path '\(path)'" 71 | } 72 | } 73 | } 74 | 75 | @main 76 | struct SourcerySwiftCodegenPlugin { 77 | func _createBuildCommands(context: CodegenPluginContext, target: CodegenPluginTarget) throws -> [Command] { 78 | 79 | let sourcery = try locateSourceryExecutable(context) 80 | Diagnostics.remark("Sourcery executable: '\(sourcery)'") 81 | 82 | let sourceryConfigFileLocations = target.sourceryConfigFileLocations(rootDirectory: context.rootDirectory) 83 | let sourceryConfigFilePaths = sourceryConfigFileLocations.flatMap { location in 84 | do { 85 | let files = try FileManager.default.contentsOfDirectory(atPath: location.string) 86 | return files.filter { $0.matches(rgSourcery) }.map { location.appending($0) } 87 | } catch let e { 88 | Diagnostics.error("\(e)") 89 | return [] 90 | } 91 | } 92 | Diagnostics.remark("Target \"\(target.name)\"\n - looking for Sourcery configs in: \(sourceryConfigFileLocations)\n - found configs: \(sourceryConfigFilePaths.map { $0.lastComponent })") 93 | 94 | // Write caches "SourceryCaches" subdirectory of the plugin work directory 95 | // (which is unique for each plugin and target). 96 | let perTargetCachesFilesDir = context.pluginWorkDirectory.appending(".sourceryCaches") 97 | try FileManager.default.createDirectory(atPath: perTargetCachesFilesDir.string, withIntermediateDirectories: true) 98 | 99 | // Per-target build directory 100 | // (which is unique for each plugin and target). 101 | let perTargetBuildDir = context.pluginWorkDirectory.appending(".sourceryBuild") 102 | try FileManager.default.createDirectory(atPath: perTargetBuildDir.string, withIntermediateDirectories: true) 103 | 104 | // Write generated files to the "GeneratedFiles" subdirectory of the plugin work directory 105 | // (which is unique for each plugin and target). 106 | let generatedFilesDir = context.pluginWorkDirectory.appending(".generatedFiles") 107 | try FileManager.default.createDirectory(atPath: generatedFilesDir.string, withIntermediateDirectories: true) 108 | 109 | let environmentVars = [ 110 | "SOURCERY_OUTPUT_DIR": generatedFilesDir.string 111 | ].merging(context.environmentVars, uniquingKeysWith: { k, _ in k }) 112 | 113 | return sourceryConfigFilePaths.map { configFilePath in 114 | let command = Self._createCommand( 115 | context: context, 116 | target: target, 117 | configFilePath: configFilePath, 118 | sourcery: sourcery, 119 | perTargetCachesFilesDir: perTargetCachesFilesDir, 120 | perTargetBuildDir: perTargetBuildDir, 121 | environmentVars: environmentVars, 122 | generatedFilesDir: generatedFilesDir) 123 | 124 | Diagnostics.remark("\(command)") 125 | 126 | return command 127 | } 128 | } 129 | 130 | #if compiler(>=6.0) 131 | private static func _createCommand( 132 | context: CodegenPluginContext, 133 | target: CodegenPluginTarget, 134 | configFilePath: Path, 135 | sourcery: Path, 136 | perTargetCachesFilesDir: Path, 137 | perTargetBuildDir: Path, 138 | environmentVars: [String: String], 139 | generatedFilesDir: Path 140 | ) -> Command { 141 | Command.prebuildCommand( 142 | displayName: "Target \(target.name): running Sourcery with config: \(configFilePath.lastComponent)", 143 | executable: sourcery, 144 | arguments: [ 145 | "--config", 146 | configFilePath.string, 147 | "--cacheBasePath", 148 | perTargetCachesFilesDir.string, 149 | "--buildPath", 150 | perTargetBuildDir.string, 151 | "--verbose", 152 | ], 153 | environment: environmentVars, 154 | outputFilesDirectory: generatedFilesDir 155 | ) 156 | } 157 | #else 158 | private static func _createCommand( 159 | context: CodegenPluginContext, 160 | target: CodegenPluginTarget, 161 | configFilePath: Path, 162 | sourcery: Path, 163 | perTargetCachesFilesDir: Path, 164 | perTargetBuildDir: Path, 165 | environmentVars: [String: String], 166 | generatedFilesDir: Path 167 | ) -> Command { 168 | Command._prebuildCommand( 169 | displayName: "Target \(target.name): running Sourcery with config: \(configFilePath.lastComponent)", 170 | executable: sourcery, 171 | arguments: [ 172 | "--config", 173 | configFilePath.string, 174 | "--cacheBasePath", 175 | perTargetCachesFilesDir.string, 176 | "--buildPath", 177 | perTargetBuildDir.string, 178 | "--verbose", 179 | ], 180 | environment: environmentVars, 181 | workingDirectory: context.pluginWorkDirectory, 182 | outputFilesDirectory: generatedFilesDir 183 | ) 184 | } 185 | #endif 186 | } 187 | 188 | // MARK: - BuildToolPlugin 189 | 190 | let rgSourcery = try! Regex("[^/:]*\\.sourcery([^/:]*)\\.yml$").ignoresCase() 191 | 192 | func wrap(_ target: PackagePlugin.Target) -> TargetWrapper { 193 | TargetWrapper(target) 194 | } 195 | 196 | extension PackagePlugin.PluginContext: CodegenPluginContext { 197 | var rootDirectory: PackagePlugin.Path { `package`.directory } 198 | 199 | var environmentVars: [String : String] { 200 | let environmentVars = 201 | [ 202 | "GIT_ROOT": gitRootDirectory(rootDirectory), 203 | "SOURCERY_PACKAGE": rootDirectory.string, // For SPM packages 204 | ].merging(`package`.targets.flatMap { t in 205 | [ 206 | ("SOURCERY_TARGET_\(t.name)", t.directory.string) 207 | ] + t.dependencies.flatMap { (d: TargetDependency) in 208 | d.dependencyInfoArray.map { 209 | ("SOURCERY_TARGET_\(t.name)_DEP_\($0.0)", $0.1.string) 210 | } 211 | } 212 | }, uniquingKeysWith: { (a, _) in a }) 213 | 214 | return environmentVars 215 | } 216 | } 217 | 218 | extension TargetDependency { 219 | var dependencyInfoArray: [(String, Path)] { 220 | switch self { 221 | case let .target(target): 222 | return [(target.name, target.directory)] 223 | 224 | case let .product(product): 225 | return product.sourceModules.map { 226 | ("\(product.name)_MODULE_\($0.moduleName)", $0.directory) 227 | } + product.targets.map { 228 | ("\(product.name)_TARGET_\($0.name)", $0.directory) 229 | } 230 | 231 | @unknown default: 232 | fatalError("Unknown TargetDependency value: \(self)") 233 | } 234 | } 235 | } 236 | 237 | struct TargetWrapper: CodegenPluginTarget { 238 | let wrapped: PackagePlugin.Target 239 | init(_ target: PackagePlugin.Target) { self.wrapped = target } 240 | 241 | var name: String { wrapped.name } 242 | func sourceryConfigFileLocations(rootDirectory: PackagePlugin.Path) -> Set { 243 | [wrapped.directory] 244 | } 245 | } 246 | 247 | extension SourcerySwiftCodegenPlugin: PackagePlugin.BuildToolPlugin { 248 | func createBuildCommands(context: PackagePlugin.PluginContext, target: PackagePlugin.Target) throws -> [Command] { 249 | return try _createBuildCommands(context: context, target: wrap(target)) 250 | } 251 | } 252 | 253 | // MARK: - XcodeProjectPlugin 254 | 255 | #if canImport(XcodeProjectPlugin) 256 | 257 | import XcodeProjectPlugin 258 | 259 | extension XcodeProjectPlugin.XcodePluginContext: CodegenPluginContext { 260 | var rootDirectory: PackagePlugin.Path { xcodeProject.directory } 261 | 262 | var environmentVars: [String : String] { 263 | let environmentVars = 264 | [ 265 | "GIT_ROOT": gitRootDirectory(rootDirectory), 266 | "SOURCERY_PROJECT": rootDirectory.string, 267 | ].merging(xcodeProject.targets.flatMap { t in 268 | // Diagnostics.warning("### Target '\(t.name)' dependencies: \(t.dependencies)") 269 | return t.dependencies.flatMap { (d: XcodeTargetDependency) in 270 | d.dependencyInfoArray.map { 271 | ("SOURCERY_TARGET_\(t.name)_DEP_\($0.0)", $0.1.string) 272 | } 273 | } 274 | }, uniquingKeysWith: { (a, _) in a }) 275 | 276 | return environmentVars 277 | } 278 | } 279 | 280 | extension XcodeTargetDependency { 281 | var dependencyInfoArray: [(String, Path)] { 282 | switch self { 283 | case .target(_): 284 | // Diagnostics.warning("### Dependency target: \(target.name)") 285 | return [] 286 | 287 | case let .product(product): 288 | // Diagnostics.warning("### Dependency product: \(product.name)") 289 | return product.sourceModules.map { 290 | ("\(product.name)_MODULE_\($0.moduleName)", $0.directory) 291 | } + product.targets.map { 292 | ("\(product.name)_TARGET_\($0.name)", $0.directory) 293 | } 294 | 295 | @unknown default: 296 | fatalError("Unknown TargetDependency value: \(self)") 297 | } 298 | } 299 | } 300 | 301 | extension XcodeProjectPlugin.XcodeTarget: CodegenPluginTarget { 302 | var name: String { displayName } 303 | func sourceryConfigFileLocations(rootDirectory: PackagePlugin.Path) -> Set { 304 | let rootDirectoryComponents = rootDirectory.components 305 | // return one level deep locations relative to the rootDirectory 306 | let inputLocations = Set(inputFiles.map { $0.path.removingLastComponent() }) 307 | return Set(inputLocations.flatMap { path -> [Path] in 308 | let components = path.components 309 | guard components.starts(with: rootDirectoryComponents) else { 310 | return [] 311 | } 312 | 313 | var res: [Path] = [] 314 | if components.count > rootDirectoryComponents.count { 315 | res.append(rootDirectory.appending(components[rootDirectoryComponents.count])) 316 | } 317 | return res 318 | }) 319 | } 320 | } 321 | 322 | extension PackagePlugin.Path { 323 | var components: [String] { 324 | let prev = removingLastComponent() 325 | guard !prev.string.isEmpty && prev.string != "/" else { return [lastComponent] } 326 | return prev.components + [lastComponent] 327 | } 328 | } 329 | 330 | extension SourcerySwiftCodegenPlugin: XcodeBuildToolPlugin { 331 | func createBuildCommands(context: XcodeProjectPlugin.XcodePluginContext, target: XcodeProjectPlugin.XcodeTarget) throws -> [Command] { 332 | return try _createBuildCommands(context: context, target: target) 333 | } 334 | } 335 | 336 | #endif 337 | 338 | // MARK: - Internal 339 | 340 | extension String { 341 | func matches(_ regex: Regex) -> Bool { 342 | guard let m = wholeMatch(of: regex) else { return false } 343 | return !m.isEmpty 344 | } 345 | } 346 | 347 | extension Path { 348 | var fileExists: Bool { 349 | FileManager.default.fileExists(atPath: string) 350 | } 351 | 352 | var isDirectory: Bool { 353 | var result: ObjCBool = false 354 | FileManager.default.fileExists(atPath: string, isDirectory: &result) 355 | return result.boolValue 356 | } 357 | 358 | var isExecutable: Bool { 359 | FileManager.default.isExecutableFile(atPath: string) 360 | } 361 | } 362 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # swift-sourcery-templates 2 | 3 | Advanced Protocol Mock and Type Erasure Code-generation templates for Swift language (using Sourcery). 4 | 5 | This repository contains two code-generation templates (to be used along with [Sourcery](https://github.com/krzysztofzablocki/Sourcery) engine): 6 | 7 | 1. `Mocks.swifttemplate` — to generate advanced protocol mock classes that can be used as [test doubles](https://martinfowler.com/bliki/TestDouble.html) in place of object dependencies for unit-testing; 8 | 2. `TypeErase.swifttemplate` — to generate advanced [type erasures](https://www.bignerdranch.com/blog/breaking-down-type-erasure-in-swift/). 9 | 10 | Both templates support protocols with associated types and generic functions (with constraints on generic types), 11 | provide reasonable default values for primitive types, and support advanced types and use cases typically found in projects 12 | that use [RxSwift](https://github.com/ReactiveX/RxSwift), and, in particular, User's [RIBs](https://github.com/uber/RIBs) frameworks. 13 | 14 | ## Rationale 15 | 16 | Code-generation is an extremely powerful technique to improve the developer team's productivity by eliminating time-consuming 17 | manual tasks, and ensuring consistency between different parts of the codebase. Typical examples include generating API wrappers 18 | for database and network services, data structure representations as native types in a specific programming language, generating 19 | test doubles and mocks for unit-testing, etc. 20 | 21 | Due to Swift language's static nature and a (very) limited runtime reflection capabilities, it's arguably [the only modern language 22 | with no mocking framework](https://blog.pragmaticengineer.com/swift-the-only-modern-language-with-no-mocking-framework/), 23 | which makes it hard for development teams to apply patterns typical for more dynamic languages when writing unit-tests. 24 | 25 | The common approach to improving developer's productivity has been to write tools that parse the source code of the program, 26 | and automatically generate mock class definitions that can be later used in place of actual object's dependencies in unit tests, 27 | to be able to instantiate the object in isolation from the rest of the system and test its behavior by analyzing side effects it 28 | performs on dependencies. There are tools like [Sourcery](https://github.com/krzysztofzablocki/Sourcery), 29 | [SwiftyMocky](https://github.com/MakeAWishFoundation/SwiftyMocky), [Cuckoo](https://github.com/Brightify/Cuckoo), 30 | there's even a [plug-in](https://plugins.jetbrains.com/plugin/9601-swift-mock-generator-for-appcode) for the JetBrain's AppCode. 31 | These tools and plug-ins are capable of auto-generating mock class definitions based on reading source type declarations, 32 | however, I was personally struggling to find a comprehensive solution so far, that would cover some advanced coding patterns, 33 | in particular, typical for programs that combine `RxSwift` and Uber's `RIBs` frameworks. 34 | 35 | ## Usage 36 | 37 | ### Swift Package Manager (SPM) prebuild plugin 38 | 39 | A [prebuild SPM plugin](https://github.com/apple/swift-package-manager/blob/main/Documentation/Plugins.md#build-tool-plugins) 40 | runs before the project is built, allowing to generate code based on the project source files. 41 | 42 | 1. To add the plugin to your project, add the plugin dependency to your `Package.swift`: 43 | 44 | ```swift 45 | // Package.swift 46 | // swift-tools-version: 5.9 // The minimum supported swift-tools version is 5.6 47 | import PackageDescription 48 | 49 | let package = Package( 50 | name: "YourPackageName", 51 | products: [ 52 | // ... 53 | ], 54 | dependencies: [ 55 | // ... 56 | .package(url: "https://github.com/ivanmisuno/swift-sourcery-templates.git", from: "0.2.2"), 57 | ], 58 | targets: [ 59 | .target( 60 | name: "YourTarget", 61 | dependencies: [ 62 | // ... 63 | ], 64 | plugins: [ 65 | .plugin(name: "SourcerySwiftCodegenPlugin", package: "swift-sourcery-templates") 66 | ] 67 | ), 68 | .testTarget( 69 | name: "YourTargetTests", 70 | dependencies: [ 71 | // ... 72 | .target(name: "ExampleProjectSpm"), 73 | ], 74 | plugins: [ 75 | .plugin(name: "SourcerySwiftCodegenPlugin", package: "swift-sourcery-templates") 76 | ] 77 | ), 78 | ] 79 | ) 80 | ``` 81 | 82 | The `SourcerySwiftCodegenPlugin` depends on the binary distribution of the Sourcery CLI. 83 | The first time it is invoked, Xcode/build system would ask for a permission to run the plugin. 84 | 85 | You might also get an unverified developer warning when the plugin tries to invoke Sourcery for the first time. 86 | To fix it, please use the [woraround](https://github.com/krzysztofzablocki/Sourcery/#issues) for removing Sourcery from quarantine: 87 | 88 | ``` 89 | xattr -dr com.apple.quarantine <...Derived Data Folder>/SourcePackages/checkouts/swift-sourcery-templates/Plugins/Sourcery/sourcery.artifactbundle/sourcery/bin/sourcery 90 | ``` 91 | 92 | 2. Configuring code generation 93 | 94 | The codegeneration is configured per target in an SPM project. Put a `*.sourcery.yml` config file in the target's source folder 95 | (refer to the [example project](Examples/ExampleProjectSpm/)): 96 | 97 | Sourcery config files per target 98 | 99 | Here, `ExampleProjectSpm` and `ExampleProjectSpmTests` are product targets, for which we want to enable code generation. 100 | We want to generate type erasures and interface mocks. Type erasure classes should be available for use in the main target, 101 | while mocks should be available in the test target. 102 | 103 | Here is caveat: Some mock classes should be generated for interfaces that are declared in external packages (e.g. for the `RIBs` package). 104 | We need to (a) include the external package sources in the config file: 105 | 106 | ``` 107 | # .Sourcery.Mocks.yml 108 | sources: 109 | - ${SOURCERY_TARGET_ExampleProjectSpm} 110 | - ${SOURCERY_TARGET_ExampleProjectSpmTests}/SourceryAnnotations 111 | - ${SOURCERY_TARGET_ExampleProjectSpm_DEP_RIBs_MODULE_RIBs} 112 | ``` 113 | 114 | We're doing a little trickery here to work around an issue with Sourcery — when invoked from a build tool SPM plugin, 115 | Sourcery cannot analyze the package structure, so the plugin exports the package structure via environment variables. 116 | In the excample above: 117 | 118 | - `SOURCERY_TARGET_ExampleProjectSpm` refers to the `ExampleProjectSpm` target source location; 119 | - `SOURCERY_TARGET_ExampleProjectSpmTests` refers to the `ExampleProjectSpmTests` target source location; 120 | - `SOURCERY_TARGET_ExampleProjectSpm_DEP_RIBs_MODULE_RIBs` refers to the `RIBs` module source location (`RIBs` is the dependency of the `ExampleProjectSpm` target). 121 | 122 | The plugin exports all package dependencies' source locations via environment variable similarly: 123 | 124 | - `SOURCERY_TARGET__DEP__MODULE_RIBs` 125 | - `SOURCERY_TARGET__DEP__TARGET_RIBs` 126 | 127 | > [!NOTE] 128 | > For the complete Sourcery config file reference, please refer to the [official documentation](https://krzysztofzablocki.github.io/Sourcery/). 129 | 130 | 3. Finding the generated files 131 | 132 | The above might seem tricky at first. The plugin helps debug setup issues by emitting invocation and debug logs to the build log: 133 | 134 | Command invocation log 135 | 136 | The invocation log also contains all exported environment variables for the dependencies. 137 | 138 | ### Podfile 139 | 140 | _Following examples refer to the [tutorial project](https://github.com/ivanmisuno/Tutorial_RIBs_CodeGeneration) (WIP), 141 | please feel free to clone and see its workings. Accompanying blog post is coming._ 142 | 143 | 1. Add `SwiftMockTemplates` Pod to your projects's Test target: 144 | 145 | ```Podfile 146 | pod 'SwiftMockTemplates', :git => 'https://github.com/ivanmisuno/swift-sourcery-templates.git', :tag => '0.1.0' 147 | ``` 148 | 149 | (I'll release it as a public Podspec once I complete unit-tests). 150 | 151 | 2. Add `.sourcery-mocks.yml` config file to the project's root: 152 | 153 | ```yml 154 | sources: 155 | - Tutorial_RIBs_CodeGeneration 156 | - Tutorial_RIBs_CodeGenerationTests/Mocks/AnnotatedRIBsProtocols.swift 157 | - Pods/RIBs 158 | templates: 159 | - Pods/SwiftMockTemplates/templates/Mocks.swifttemplate 160 | output: Tutorial_RIBs_CodeGenerationTests/Mocks 161 | args: 162 | testable: 163 | - Tutorial_RIBs_CodeGeneration 164 | import: 165 | - RIBs 166 | - RxSwift 167 | - RxTest 168 | excludedSwiftLintRules: 169 | - force_cast 170 | - function_body_length 171 | - line_length 172 | - vertical_whitespace 173 | ``` 174 | 175 | 3. Add `codegen.sh` script that will run `Sourcery` with the above config file: 176 | 177 | ```sh 178 | #!/bin/bash 179 | 180 | # the directory of the script. all locations are relative to the $DIR 181 | DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" 182 | PARENT_DIR="$DIR/.." 183 | 184 | SOURCERY_DIR="$PARENT_DIR/Pods/Sourcery" 185 | SOURCERY="$SOURCERY_DIR/bin/sourcery" 186 | 187 | "$SOURCERY" --config "$PARENT_DIR"/.sourcery-mocks.yml $1 $2 188 | ``` 189 | 190 | Note that `sourcery` executable is installed in `Pods` folder. 191 | 192 | 4. Annotate protocols in your code for which you'd like to generate mock classes with the following annotation: 193 | 194 | ```Swift 195 | /// sourcery: CreateMock 196 | ``` 197 | 198 | There are more advanced use cases, the documentation is coming. 199 | 200 | 5. Run following command to generate mock classes: 201 | 202 | ```sh 203 | scripts $ ./codegen.sh [--disableCache] 204 | ``` 205 | 206 | This command will generate `Mocks.generated.swift` file in the `output` folder as specified in the config file. 207 | 208 | 6. Add the generated file to the test target in the Xcode project. 209 | 210 | # License 211 | 212 | Copyright 2018 Ivan Misuno 213 | 214 | Licensed under the Apache License, Version 2.0 (the "License"); 215 | you may not use this file except in compliance with the License. 216 | You may obtain a copy of the License at 217 | 218 | http://www.apache.org/licenses/LICENSE-2.0 219 | 220 | Unless required by applicable law or agreed to in writing, software 221 | distributed under the License is distributed on an "AS IS" BASIS, 222 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 223 | See the License for the specific language governing permissions and 224 | limitations under the License. 225 | -------------------------------------------------------------------------------- /SwiftMockTemplates.podspec: -------------------------------------------------------------------------------- 1 | Pod::Spec.new do |s| 2 | s.name = 'SwiftMockTemplates' 3 | s.version = '0.2.7' 4 | s.summary = 'Code-generation templates for Swift language.' 5 | s.description = <<-DESC 6 | Code-generation templates (to be used along with Sourcery engine) for generating advanced protocol mock classes 7 | that can be used as test doubles in place of object dependencies for unit-testing. 8 | DESC 9 | s.homepage = 'https://github.com/ivanmisuno/swift-sourcery-templates' 10 | s.license = { :type => 'Apache License, Version 2.0' } 11 | s.author = { 'Ivan Misuno' => 'i.misuno@gmail.com' } 12 | 13 | s.dependency 'Sourcery', '2.1.2' 14 | 15 | s.source = { :http => "https://github.com/ivanmisuno/swift-sourcery-templates/releases/download/#{s.version}/swift-sourcery-templates-#{s.version}.zip" } 16 | s.preserve_paths = '*' 17 | s.exclude_files = '**/*.zip' 18 | 19 | end 20 | -------------------------------------------------------------------------------- /docs/img/command_invocation_log.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ivanmisuno/swift-sourcery-templates/2ad0cb2d2cbbad61d11c91f8091d1d422fd5c10d/docs/img/command_invocation_log.png -------------------------------------------------------------------------------- /docs/img/sourcery_target_config.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ivanmisuno/swift-sourcery-templates/2ad0cb2d2cbbad61d11c91f8091d1d422fd5c10d/docs/img/sourcery_target_config.png -------------------------------------------------------------------------------- /templates/Mocks.swifttemplate: -------------------------------------------------------------------------------- 1 | <%- include("_header") -%> 2 | <%- includeFile("Utility/Annotations") -%> 3 | <%- includeFile("Utility/Generics") -%> 4 | <%- includeFile("Utility/String") -%> 5 | <%- includeFile("Utility/StringLettercase") -%> 6 | <%- includeFile("Mocks/MockMethod") -%> 7 | <%- includeFile("Mocks/MockVar") -%> 8 | <%- includeFile("Mocks/MockGenerator") -%> 9 | <%- includeFile("Mocks/SourceCode") -%> 10 | <%- includeFile("Mocks/SourceryRuntimeExtensions") -%> 11 | <%= try MockGenerator.generate(for: types.protocols.map { $0 }.filter { $0.annotations["CreateMock"] != nil }) %> 12 | -------------------------------------------------------------------------------- /templates/Mocks/MockGenerator.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | import SourceryRuntime 3 | 4 | enum MockError: Error { 5 | case noDefaultValue(typeName: TypeName) 6 | case duplicateGenericTypeName(context: String) 7 | case internalError(message: String) 8 | } 9 | 10 | private struct Constants { 11 | static let NEWL = "" 12 | static let genericTypePrefix = "_" 13 | } 14 | 15 | class MockGenerator { 16 | static func generate(for types: [Type]) throws -> String { 17 | var topScope = TopScope() 18 | 19 | for type in types { 20 | let mockVars = MockVar.from(type) 21 | let variablesToInit = mockVars.filter { $0.provideValueInInitializer }.map { (mockedVariableName: $0.mockedVariableName, variable: $0.variable, defaultValue: try? $0.variable.typeName.defaultValue()) } 22 | let mockMethods = try MockMethod.from(type, genericTypePrefix: Constants.genericTypePrefix) 23 | 24 | topScope += Constants.NEWL 25 | topScope += "// MARK: - \(type.name)" 26 | 27 | let genericTypes: [GenericTypeInfo] = (type.genericTypes + mockMethods.flatMap { $0.genericTypes }).merged() 28 | 29 | var mock = SourceCode("class \(type.name)Mock\(genericTypes.genericTypesModifier): \(type.isObjcProtocol ? "NSObject, " : "")\(type.name)\(genericTypes.genericTypesConstraints)") 30 | mock.isBlockMandatory = true 31 | 32 | // generic typealiases 33 | mock += genericTypes.typealiasesDeclarations 34 | 35 | // variables 36 | let mockVarsFlattened = try mockVars.flatMap { try $0.mockImpl() } 37 | if !mockVarsFlattened.isEmpty { 38 | mock += Constants.NEWL 39 | mock += "// MARK: - Variables" 40 | mock += mockVarsFlattened 41 | } 42 | 43 | // initializer 44 | if !variablesToInit.isEmpty { 45 | let argumentList = variablesToInit.map { 46 | let defaultValue = $0.defaultValue != nil ? " = \($0.defaultValue!)" : "" 47 | return "\($0.mockedVariableName): \($0.variable.typeName)\(defaultValue)" 48 | }.joined(separator: ", ") 49 | let initImpl = SourceCode("init(\(argumentList))") 50 | initImpl += variablesToInit.map { "self.\($0.mockedVariableName) = \($0.mockedVariableName)" } 51 | mock += Constants.NEWL 52 | mock += "// MARK: - Initializer" 53 | mock += initImpl 54 | } 55 | 56 | // methods 57 | let mockMethodsFlattened = try mockMethods.flatMap { try $0.mockImpl() } 58 | if !mockMethodsFlattened.isEmpty { 59 | mock += Constants.NEWL 60 | mock += "// MARK: - Methods" 61 | mock += mockMethodsFlattened 62 | } 63 | 64 | topScope += mock 65 | } 66 | 67 | return topScope.indentedSourcecode() 68 | } 69 | } 70 | 71 | private extension SourceryRuntime.`Type` { 72 | var isObjcProtocol: Bool { 73 | return annotations["ObjcProtocol"] != nil 74 | || inheritedTypes.contains("NSObjectProtocol") 75 | } 76 | } 77 | 78 | private extension SourceryRuntime.`Type` { 79 | var genericTypes: [GenericTypeInfo] { 80 | let associatedTypes = annotatedAssociatedTypes() 81 | return associatedTypes 82 | } 83 | } 84 | 85 | private extension Collection where Element == GenericTypeInfo { 86 | 87 | /// For a given list of generic types, creates a corresponding type declaration modifier string. 88 | /// E.g., for a list ["T1", "T2"] this will create "<_T1, _T2>" modifier string (where "_" is the value of genericTypePrefix). 89 | var genericTypesModifier: String { 90 | let types = sorted { $0.genericType < $1.genericType }.map { "\(Constants.genericTypePrefix)\($0.genericType)" } 91 | return !types.isEmpty ? "<\(types.joined(separator: ", "))>" : "" 92 | } 93 | 94 | /// For a given list of generic types, some of which have type constraints, creates a "where" clause for the type declaration. 95 | /// E.g., for a list ["T1: RawRepresentable", "T1: Hashable", "T2"] this will create "where _T1: RawRepresentable, _T1: Hashable" modifier string (where "_" is the value of genericTypePrefix). 96 | var genericTypesConstraints: String { 97 | let constraints = sorted { $0.genericType < $1.genericType }.flatMap { genericType in return genericType.constraints.map { "\(Constants.genericTypePrefix)\($0)" }.sorted() } 98 | return !constraints.isEmpty ? " where \(constraints.joined(separator: ", "))" : "" 99 | } 100 | 101 | var typealiasesDeclarations: [String] { 102 | guard !isEmpty else { return [] } 103 | var result: [String] = [] 104 | result.append(Constants.NEWL) 105 | result.append("// MARK: - Generic typealiases") 106 | result.append(contentsOf: sorted { $0.genericType < $1.genericType }.map { "typealias \($0.genericType) = \(Constants.genericTypePrefix)\($0.genericType)" }) 107 | return result 108 | } 109 | } 110 | 111 | extension MockError: LocalizedError { 112 | var errorDescription: String? { 113 | switch self { 114 | case .noDefaultValue(let typeName): return "Unable to generate default value for \(typeName)" 115 | case .duplicateGenericTypeName(let context): return "Duplicate generic type name found while generating mock implementation: \(context)" 116 | case .internalError(let message): return "Internal error: \(message)" 117 | } 118 | } 119 | } 120 | 121 | extension MockError: CustomStringConvertible { 122 | var description: String { 123 | return errorDescription ?? String(describing: self) 124 | } 125 | } 126 | 127 | extension MockError: CustomDebugStringConvertible { 128 | var debugDescription: String { 129 | return errorDescription ?? String(describing: self) 130 | } 131 | } 132 | -------------------------------------------------------------------------------- /templates/Mocks/MockMethod.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | import SourceryRuntime 3 | 4 | class MockMethod { 5 | fileprivate let type: SourceryRuntime.`Type` 6 | let method: SourceryRuntime.Method 7 | fileprivate let genericTypePrefix: String 8 | fileprivate let useShortName: Bool 9 | 10 | init(type: SourceryRuntime.`Type`, method: SourceryRuntime.Method, genericTypePrefix: String, useShortName: Bool) { 11 | self.type = type 12 | self.method = method 13 | self.genericTypePrefix = genericTypePrefix 14 | self.useShortName = useShortName 15 | } 16 | 17 | static func from(_ type: Type, genericTypePrefix: String) throws -> [MockMethod] { 18 | let allMethods = type.allMethods.filter { !$0.isStatic && $0.definedInType != nil && $0.definedInType?.isExtension == false }.uniquesWithoutGenericConstraints() 19 | let mockedMethods = allMethods 20 | .map { MockMethod(type: type, method: $0, genericTypePrefix: genericTypePrefix, useShortName: true) } 21 | .minimumNonConflictingPermutation 22 | guard !mockedMethods.hasDuplicateMockedMethodNames else { 23 | throw MockError.internalError(message: "Mock generator: not all duplicates resolved: \(mockedMethods.map { $0.mockedMethodName })!") 24 | } 25 | return mockedMethods.sorted { $0.mockedMethodName < $1.mockedMethodName } 26 | } 27 | } 28 | 29 | extension MockMethod { 30 | var isVoid: Bool { 31 | return method.returnTypeName.isVoid 32 | } 33 | 34 | var isGeneric: Bool { 35 | return method.isGeneric 36 | } 37 | 38 | var genericTypes: [GenericTypeInfo] { 39 | return method.annotatedGenericTypes() 40 | } 41 | } 42 | 43 | extension MockMethod { 44 | fileprivate var annotatedMethodName: String? { 45 | return method.annotations(for: ["methodName"]).first 46 | } 47 | 48 | fileprivate var mockedMethodName: String { 49 | if let annotatedMethodName = annotatedMethodName { 50 | return annotatedMethodName 51 | } 52 | var result: [String] = [method.callName] 53 | if !useShortName { 54 | result += method.parameters.map { 55 | let argumentLabel = $0.argumentLabel == nil ? "" : $0.argumentLabel != $0.name ? $0.argumentLabel!.uppercasedFirstLetter() : "" 56 | return "\(argumentLabel)\($0.name.uppercasedFirstLetter())" 57 | } 58 | } 59 | return result.joined().swiftifiedMethodName 60 | } 61 | 62 | func mockImpl() throws -> [SourceCode] { 63 | var mockMethodHandlers = TopScope() 64 | 65 | let mockCallCount = mockCallCountImpl 66 | mockMethodHandlers += mockCallCount.1 67 | 68 | var mockHandler = mockHandlerImpl 69 | mockMethodHandlers += mockHandler.1 70 | 71 | // func declaration 72 | var methodImpl = SourceCode("func \(method.shortName)(\(method.methodParametersDecl))\(method.throwingDecl)\(method.returnTypeDecl)") 73 | 74 | // Increment usage call count. 75 | methodImpl += "\(mockCallCount.0) += 1" 76 | 77 | // Call the handler. 78 | methodImpl += mockHandlerCallImpl 79 | 80 | // Fallback return value. 81 | if isVoid { 82 | // No return value 83 | } else if method.isOptionalReturnType { 84 | // Return nil 85 | methodImpl += "return nil" 86 | } else { 87 | // Do something smart 88 | if method.returnTypeName.hasComplexTypeWithSmartDefaultValue(isProperty: false) { 89 | let smartDefaultValueImplementation = try method.returnTypeName.smartDefaultValueImplementation( 90 | isProperty: false, 91 | mockVariablePrefix: mockedMethodName, 92 | forceCastingToReturnTypeName: isGeneric) 93 | 94 | methodImpl += smartDefaultValueImplementation.0 95 | mockMethodHandlers += smartDefaultValueImplementation.1 96 | } else if method.returnTypeName.hasDefaultValue, let defaultValue = try? method.returnTypeName.defaultValue() { 97 | methodImpl += SourceCode("return \(defaultValue)") 98 | } else { 99 | // fatal 100 | methodImpl += "fatalError(\"\(mockHandler.0) expected to be set.\")" 101 | } 102 | } 103 | 104 | var result = TopScope() 105 | result += methodImpl 106 | result += mockMethodHandlers.nested 107 | return result.nested 108 | } 109 | 110 | private var mockedVarCallCountName: String { 111 | return "\(mockedMethodName)CallCount" 112 | } 113 | 114 | private var mockCallCountImpl: (String, SourceCode) { 115 | return (mockedVarCallCountName, SourceCode("var \(mockedVarCallCountName): Int = 0")) 116 | } 117 | 118 | private var mockMethodHandlerName: String { 119 | return "\(mockedMethodName)Handler" 120 | } 121 | 122 | private var mockMethodHandlerReturnType: String { 123 | return !isVoid ? method.returnTypeName.name.trimmingWhereClause() : "" 124 | } 125 | 126 | private var mockHandlerImpl: (String, SourceCode) { 127 | let handlerParameters = method.parameters.map { "_ \($0.name): \($0.typeName.name)" }.joined(separator: ", ") 128 | return (mockMethodHandlerName, SourceCode("var \(mockMethodHandlerName): ((\(handlerParameters))\(method.throwingHandlerDecl) -> (\(mockMethodHandlerReturnType)))? = nil")) 129 | } 130 | 131 | private var mockHandlerCallImpl: SourceCode { 132 | let returning = isVoid ? "" : "return " 133 | let parameters = method.parameters 134 | .map { 135 | let referenceTaking = $0.`inout` ? "&" : "" 136 | let parameterName = "\(referenceTaking)\($0.name)" 137 | if isGeneric, let extractedAnnotatedGenericTypesPlaceholder = $0.annotations(for: ["annotatedGenericType", "annotatedGenericTypes", "genericTypePlaceholder", "genericTypesPlaceholder"]).first { 138 | let forceCastingToGenericParameterType = " as! \(extractedAnnotatedGenericTypesPlaceholder.resolvingGenericPlaceholders(prefix: genericTypePrefix))" 139 | return "\(parameterName)\(forceCastingToGenericParameterType)" 140 | } else { 141 | return parameterName 142 | } 143 | } 144 | .joined(separator: ", ") 145 | let forceCastingToGenericReturnValue = isGeneric && !isVoid ? " as! \(mockMethodHandlerReturnType)" : "" 146 | let invocationThrowing = method.`throws` ? "try " : method.`rethrows` ? "try! " : "" 147 | let handlerName = mockHandlerImpl.0 148 | return SourceCode("if let __\(handlerName) = self.\(handlerName)") {[ 149 | SourceCode("\(returning)\(invocationThrowing)__\(handlerName)(\(parameters))\(forceCastingToGenericReturnValue)") 150 | ]} 151 | } 152 | } 153 | 154 | private struct Regex { 155 | static let placeholderPattern = "\\{([^\\}]+)\\}" 156 | } 157 | 158 | private extension String { 159 | var swiftifiedMethodName: String { 160 | return self 161 | .replacingOccurrences(of: "(", with: "_") 162 | .replacingOccurrences(of: ")", with: "") 163 | .replacingOccurrences(of: ":", with: "_") 164 | .replacingOccurrences(of: "`", with: "") 165 | .camelCased() 166 | .lowercasedFirstWord() 167 | } 168 | 169 | func resolvingGenericPlaceholders(prefix genericTypePrefix: String) -> String { 170 | return replacingOccurrences(of: Regex.placeholderPattern, with: "\(genericTypePrefix)$1", options: .regularExpression) 171 | } 172 | } 173 | 174 | private extension MockMethod { 175 | var shortNameKey: String { 176 | return method.shortName.swiftifiedMethodName 177 | } 178 | } 179 | 180 | private extension SourceryRuntime.Method { 181 | var methodParametersDecl: String { 182 | return parameters 183 | .map { $0.parametersDecl } 184 | .joined(separator: ", ") 185 | } 186 | 187 | var throwingDecl: String { 188 | return self.`throws` ? " throws" : self.`rethrows` ? " rethrows" : "" 189 | } 190 | 191 | var throwingHandlerDecl: String { 192 | return self.`throws` || self.`rethrows` ? " throws" : "" 193 | } 194 | 195 | var returnTypeDecl: String { 196 | return !returnTypeName.isVoid ? " -> \(returnTypeName.name)" : "" 197 | } 198 | } 199 | 200 | private extension SourceryRuntime.MethodParameter { 201 | var parametersDecl: String { 202 | let argumentLabel = argumentLabel == nil ? "_ " : argumentLabel != name ? "\(argumentLabel!) " : "" 203 | return "\(argumentLabel)\(name): \(typeName.name)" 204 | } 205 | } 206 | 207 | private extension Collection where Element == MockMethod { 208 | /// If all method mocks were created with `useShortName: true`, then the resulting mock might have duplicate backing variable names for methods, e.g.: 209 | /// ``` 210 | /// func updateTips(_ tips: [Tip]) 211 | /// func updateTips(with: AnySequence) throws 212 | /// ``` 213 | /// would both produce `var updateTipsCallCount: Int = 0`, which will break the build. 214 | /// This method finds such occurrences and tries to use fully-qualified names of the method to produce mock variables for groups of duplicate methods. 215 | /// While doing so, it will try to use `useShortName: true` for the shortest method name, to produce a little bit more readable/deterministic mock class, 216 | /// where the existing implementation would not change if new method overrides are added later with more parameters. 217 | var minimumNonConflictingPermutation: [MockMethod] { 218 | return reduce(into: [:]) { (partialResult: inout [String: [MockMethod]], nextItem: MockMethod) in 219 | let key = nextItem.shortNameKey 220 | var group = partialResult[key] ?? [] 221 | group.append(nextItem) 222 | partialResult[key] = group 223 | } 224 | .map { $0.1.makeUniqueByUsingLongNamesExceptForFewestArgumentMethod() } 225 | .joined() 226 | .map { $0 } 227 | } 228 | 229 | private func makeUniqueByUsingLongNamesExceptForFewestArgumentMethod() -> [MockMethod] { 230 | guard count != 1 else { return Array(self) } 231 | func areInAscendingOrder(lhs: MockMethod, rhs: MockMethod) -> Bool { 232 | if lhs.method.parameters.count < rhs.method.parameters.count { 233 | // Use the method with the fewest number of parameters 234 | return true 235 | } 236 | if lhs.method.parameters.count == rhs.method.parameters.count, 237 | let firstParameterLhs = lhs.method.parameters.first, 238 | let firstParameterRhs = rhs.method.parameters.first { 239 | // If two methods have the same number of parameters, but one has shorter syntax by not requiring parameter label, use it instead. 240 | return firstParameterLhs.argumentLabel == nil && firstParameterRhs.argumentLabel != nil 241 | } 242 | // Otherwise, use the other one. 243 | return false 244 | } 245 | guard let fewestArgumentsMethod = min(by: areInAscendingOrder), let largestArgumentsMethod = max(by: areInAscendingOrder) else { fatalError("Should not happen.") } 246 | if fewestArgumentsMethod.method.parameters.parametersCountAppendingOneForArgumentLabel == largestArgumentsMethod.method.parameters.parametersCountAppendingOneForArgumentLabel { 247 | // min and max methods in the collection have exactly the same number of parameters, we can't choose one over another, 248 | // so use long form for all. 249 | return makeUniqueByUsingLongNames() 250 | } 251 | let copy = map { MockMethod(type: $0.type, method: $0.method, genericTypePrefix: $0.genericTypePrefix, useShortName: $0 === fewestArgumentsMethod) } 252 | guard !copy.hasDuplicateMockedMethodNames else { 253 | return makeUniqueByUsingLongNames() 254 | } 255 | return copy 256 | } 257 | 258 | private func makeUniqueByUsingLongNames() -> [MockMethod] { 259 | return map { MockMethod(type: $0.type, method: $0.method, genericTypePrefix: $0.genericTypePrefix, useShortName: false) } 260 | } 261 | 262 | var hasDuplicateMockedMethodNames: Bool { 263 | var mockedMethodNames = Set() 264 | for nextItem in self { 265 | let key = nextItem.mockedMethodName 266 | if mockedMethodNames.contains(key) { 267 | return true 268 | } 269 | mockedMethodNames.insert(key) 270 | } 271 | return false 272 | } 273 | } 274 | 275 | private extension Collection where Element == SourceryRuntime.MethodParameter { 276 | var parametersCountAppendingOneForArgumentLabel: Int { 277 | return (first?.argumentLabel != nil ? 1 : 0) + count 278 | } 279 | } 280 | 281 | private extension Collection where Element == SourceryRuntime.Method { 282 | /// Courtesy of https://github.com/MakeAWishFoundation/SwiftyMocky/blob/develop/Sources/Templates/Mock.swifttemplate 283 | func uniques() -> [SourceryRuntime.Method] { 284 | func returnTypeStripped(_ method: SourceryRuntime.Method) -> String { 285 | let returnTypeRaw = "\(method.returnTypeName)" 286 | var stripped: String = { 287 | guard let range = returnTypeRaw.range(of: "where") else { return returnTypeRaw } 288 | var stripped = returnTypeRaw 289 | stripped.removeSubrange((range.lowerBound)...) 290 | return stripped 291 | }() 292 | stripped = stripped.trimmingCharacters(in: CharacterSet(charactersIn: " ")) 293 | return stripped 294 | } 295 | 296 | func areSameParams(_ p1: SourceryRuntime.MethodParameter, _ p2: SourceryRuntime.MethodParameter) -> Bool { 297 | guard p1.argumentLabel == p2.argumentLabel else { return false } 298 | guard p1.name == p2.name else { return false } 299 | guard p1.argumentLabel == p2.argumentLabel else { return false } 300 | guard p1.typeName.name == p2.typeName.name else { return false } 301 | guard p1.actualTypeName?.name == p2.actualTypeName?.name else { return false } 302 | return true 303 | } 304 | 305 | func areSameMethods(_ m1: SourceryRuntime.Method, _ m2: SourceryRuntime.Method) -> Bool { 306 | guard m1.name != m2.name else { return m1.returnTypeName == m2.returnTypeName } 307 | guard m1.selectorName == m2.selectorName else { return false } 308 | guard m1.parameters.count == m2.parameters.count else { return false } 309 | 310 | let p1 = m1.parameters 311 | let p2 = m2.parameters 312 | 313 | for i in 0.. [SourceryRuntime.Method] in 321 | guard !result.contains(where: { areSameMethods($0,element) }) else { return result } 322 | return result + [element] 323 | }) 324 | } 325 | 326 | /// Courtesy of https://github.com/MakeAWishFoundation/SwiftyMocky/blob/develop/Sources/Templates/Mock.swifttemplate 327 | func uniquesWithoutGenericConstraints() -> [SourceryRuntime.Method] { 328 | func returnTypeStripped(_ method: SourceryRuntime.Method) -> String { 329 | let returnTypeRaw = "\(method.returnTypeName)" 330 | var stripped: String = { 331 | guard let range = returnTypeRaw.range(of: "where") else { return returnTypeRaw } 332 | var stripped = returnTypeRaw 333 | stripped.removeSubrange((range.lowerBound)...) 334 | return stripped 335 | }() 336 | stripped = stripped.trimmingCharacters(in: CharacterSet(charactersIn: " ")) 337 | return stripped 338 | } 339 | 340 | func areSameParams(_ p1: SourceryRuntime.MethodParameter, _ p2: SourceryRuntime.MethodParameter) -> Bool { 341 | guard p1.argumentLabel == p2.argumentLabel else { return false } 342 | guard p1.name == p2.name else { return false } 343 | guard p1.argumentLabel == p2.argumentLabel else { return false } 344 | guard p1.typeName.name == p2.typeName.name else { return false } 345 | guard p1.actualTypeName?.name == p2.actualTypeName?.name else { return false } 346 | return true 347 | } 348 | 349 | func areSameMethods(_ m1: SourceryRuntime.Method, _ m2: SourceryRuntime.Method) -> Bool { 350 | guard m1.name != m2.name else { return returnTypeStripped(m1) == returnTypeStripped(m2) } 351 | guard m1.selectorName == m2.selectorName else { return false } 352 | guard m1.parameters.count == m2.parameters.count else { return false } 353 | 354 | let p1 = m1.parameters 355 | let p2 = m2.parameters 356 | 357 | for i in 0.. [SourceryRuntime.Method] in 365 | guard !result.contains(where: { areSameMethods($0,element) }) else { return result } 366 | return result + [element] 367 | }) 368 | } 369 | } 370 | -------------------------------------------------------------------------------- /templates/Mocks/MockVar.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | import SourceryRuntime 3 | 4 | class MockVar { 5 | let variable: SourceryRuntime.Variable 6 | 7 | var mockedVariableName: String { 8 | return "\(variable.name)" 9 | } 10 | 11 | init(variable: SourceryRuntime.Variable) { 12 | self.variable = variable 13 | } 14 | 15 | static func from(_ type: Type) -> [MockVar] { 16 | let allVariables = type.allVariables.filter { !$0.isStatic && $0.definedInType != nil && $0.definedInType?.isExtension == false }.uniqueVariables 17 | return allVariables.map { MockVar(variable: $0) }.sorted { $0.mockedVariableName < $1.mockedVariableName } 18 | } 19 | } 20 | 21 | extension MockVar { 22 | 23 | // Does this variable require init()? 24 | var provideValueInInitializer: Bool { 25 | return !variable.typeName.hasComplexTypeWithSmartDefaultValue(isProperty: true) 26 | && (variable.isAnnotatedInit || !variable.typeName.hasDefaultValue) 27 | && !variable.isAnnotatedHandler 28 | } 29 | 30 | func mockImpl() throws -> [SourceCode] { 31 | let mockedVariableImplementation: SourceCode 32 | let mockedVariableHandlers = TopScope() 33 | 34 | if !variable.isMutable, 35 | variable.typeName.hasComplexTypeWithSmartDefaultValue(isProperty: true), 36 | let smartDefaultValueImplementation = try? variable.typeName.smartDefaultValueImplementation(isProperty: true, mockVariablePrefix: mockedVariableName) { 37 | 38 | mockedVariableImplementation = SourceCode("var \(variable.name): \(variable.typeName)") {[ 39 | SourceCode("\(mockedVariableName)GetCount += 1"), 40 | SourceCode("if let handler = \(mockedVariableName)GetHandler") {[ 41 | SourceCode("return handler()") 42 | ]}, 43 | smartDefaultValueImplementation.getterImplementation 44 | ]} 45 | mockedVariableHandlers += "var \(mockedVariableName)GetCount: Int = 0" 46 | mockedVariableHandlers += "var \(mockedVariableName)GetHandler: (() -> \(variable.typeName))? = nil" 47 | mockedVariableHandlers += smartDefaultValueImplementation.mockedVariableHandlers 48 | } else { 49 | let variableDecl = !variable.isMutable && variable.isAnnotatedConst ? "let" : "var" 50 | if variable.isAnnotatedHandler { 51 | // Value should be implemented with the `get` handler 52 | var getterImplementation: [SourceCode] = [ 53 | SourceCode("\(mockedVariableName)GetCount += 1"), 54 | SourceCode("if let handler = \(mockedVariableName)GetHandler") {[ 55 | SourceCode("return handler()") 56 | ]}, 57 | SourceCode("fatalError(\"`\(mockedVariableName)GetHandler` must be set!\")") 58 | ] 59 | if variable.isMutable { 60 | mockedVariableImplementation = SourceCode("var \(variable.name): \(variable.typeName)") {[ 61 | SourceCode("get", nested: getterImplementation) 62 | ]} 63 | } else { 64 | mockedVariableImplementation = SourceCode("var \(variable.name): \(variable.typeName)", nested: getterImplementation) 65 | } 66 | mockedVariableHandlers += "var \(mockedVariableName)GetCount: Int = 0" 67 | mockedVariableHandlers += "var \(mockedVariableName)GetHandler: (() -> \(variable.typeName))? = nil" 68 | } else if !variable.isAnnotatedInit, variable.typeName.hasDefaultValue, let defaultValue = try? variable.typeName.defaultValue() { 69 | // Default value can be guessed. 70 | mockedVariableImplementation = SourceCode("\(variableDecl) \(variable.name): \(variable.typeName) = \(defaultValue)") 71 | } else { 72 | // No default value, the value must be provided to the mock class's initializer. 73 | mockedVariableImplementation = SourceCode("\(variableDecl) \(variable.name): \(variable.typeName)") 74 | } 75 | if variable.isMutable { 76 | mockedVariableImplementation += SourceCode(variable.isAnnotatedHandler ? "set" : "didSet") {[ 77 | SourceCode("\(mockedVariableName)SetCount += 1") 78 | ]} 79 | mockedVariableHandlers += "var \(mockedVariableName)SetCount: Int = 0" 80 | } 81 | } 82 | var topScope = TopScope() 83 | topScope += mockedVariableImplementation 84 | topScope += mockedVariableHandlers.nested 85 | return topScope.nested 86 | } 87 | } 88 | 89 | private extension Collection where Element: SourceryRuntime.Variable { 90 | var uniqueVariables: [SourceryRuntime.Variable] { 91 | return reduce(into: [], { (result, element) in 92 | guard !result.contains(where: { $0.name == element.name }) else { return } 93 | result.append(element) 94 | }) 95 | } 96 | } 97 | -------------------------------------------------------------------------------- /templates/Mocks/SourceCode.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | import SourceryRuntime 3 | 4 | /// Represents source code construct: 5 | /// line 6 | /// or 7 | /// line { 8 | /// $(nested) 9 | /// } 10 | class SourceCode { 11 | let line: String 12 | var nested: [SourceCode] 13 | var isBlockMandatory: Bool = false // if `true`, always output curly braces after the line, even if nested is empty, e.g., empty class/function declaration. 14 | 15 | init(_ line: String, nested: [SourceCode] = []) { 16 | self.line = line 17 | self.nested = nested 18 | } 19 | convenience init(_ line: String, nested: () -> [SourceCode]) { 20 | self.init(line, nested: nested()) 21 | } 22 | 23 | func nest(_ source: SourceCode) { 24 | nested.append(source) 25 | } 26 | 27 | func indentedSourcecode(_ level: Int = 0) -> String { 28 | guard !line.isEmpty else { 29 | assert(nested.isEmpty) 30 | return "" 31 | } 32 | let indent = String(repeating: " ", count: level * 4) 33 | var result = "\(indent)\(line)" 34 | let nestedCode = nested.map { $0.indentedSourcecode(level + 1) }.joined(separator: "\n") 35 | if !nestedCode.isEmpty { 36 | if !line.contains("{") { // as in, e.g., "return AnyObserver { [weak self] event in" 37 | result += " {" 38 | } 39 | result += "\n\(nestedCode)\n\(indent)}" 40 | } else if isBlockMandatory { 41 | result += " {\n\(indent)}" 42 | } 43 | return result 44 | } 45 | } 46 | 47 | class TopScope { 48 | var nested: [SourceCode] = [] 49 | 50 | func nest(_ source: SourceCode) { 51 | nested.append(source) 52 | } 53 | 54 | func indentedSourcecode() -> String { 55 | return nested.map { $0.indentedSourcecode(0) }.joined(separator: "\n") 56 | } 57 | } 58 | 59 | protocol SourceAppendable { 60 | func nest(_ source: SourceCode) 61 | } 62 | extension SourceCode: SourceAppendable {} 63 | extension TopScope: SourceAppendable {} 64 | extension SourceAppendable { 65 | @discardableResult 66 | func nest(_ line: String) -> SourceCode { 67 | let sourceLine = SourceCode(line) 68 | nest(sourceLine) 69 | return sourceLine 70 | } 71 | 72 | func nest(contentsOf array: [SourceCode]) { 73 | array.forEach { nest($0) } 74 | } 75 | 76 | func nest(contentsOf array: [String]) { 77 | array.forEach { nest($0) } 78 | } 79 | 80 | static func +=(_ lhs: Self, _ rhs: SourceCode) { 81 | lhs.nest(rhs) 82 | } 83 | static func +=(_ lhs: Self, _ rhs: [SourceCode]) { 84 | lhs.nest(contentsOf: rhs) 85 | } 86 | 87 | @discardableResult 88 | static func +=(_ lhs: Self, _ line: String) -> SourceCode { 89 | return lhs.nest(line) 90 | } 91 | 92 | static func +=(_ lhs: Self, _ lines: [String]) { 93 | lhs.nest(contentsOf: lines) 94 | } 95 | } 96 | -------------------------------------------------------------------------------- /templates/Mocks/SourceryRuntimeExtensions.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | import SourceryRuntime 3 | 4 | extension SourceryRuntime.TypeName { 5 | 6 | var hasDefaultValue: Bool { 7 | return (try? defaultValue()) != nil 8 | } 9 | 10 | func defaultValue() throws -> String { 11 | if isOptional { return "nil" } 12 | if isVoid { return "()" } 13 | if isArray { return "[]" } 14 | if isDictionary { return "[:]" } 15 | if isTuple, let tuple = tuple { 16 | let joined = try tuple.elements.map { try $0.typeName.defaultValue() }.joined(separator: ", ") 17 | return "(\(joined))" 18 | } 19 | 20 | switch unwrappedTypeName { 21 | case "String": return "\"\"" 22 | case "Bool": return "false" 23 | case "Int", "Int32", "Int64", "UInt", "UInt32", "UInt64": return "0" 24 | case "Float", "Double": return "0.0" 25 | case "NSTimeInterval", "TimeInterval": return "0.0" 26 | case "CGFloat": return "CGFloat(0)" 27 | case "CGPoint", "NSPoint": return "CGPoint.zero" 28 | case "CGSize", "NSSize": return "CGSize.zero" 29 | case "CGRect", "NSRect": return "CGRect.zero" 30 | default: 31 | break 32 | } 33 | 34 | if let generic = generic { 35 | switch generic.name { 36 | case "Set": return "\(generic.name)()" 37 | default: 38 | break 39 | } 40 | } 41 | 42 | throw MockError.noDefaultValue(typeName: self) 43 | } 44 | 45 | var mockTypeName: String { 46 | if isVoid { 47 | return "()" 48 | } 49 | 50 | if isTuple, let tuple { 51 | return tuple.name + (isOptional ? "?" : "") 52 | } 53 | 54 | if isArray, let array { 55 | return "[\(array.elementTypeName.mockTypeName)]" + (isOptional ? "?" : "") 56 | } 57 | 58 | if isDictionary, let dictionary { 59 | return "[\(dictionary.keyTypeName.mockTypeName) : \(dictionary.valueTypeName.mockTypeName)]" + (isOptional ? "?" : "") 60 | } 61 | 62 | if isOptional, unwrappedTypeName.hasPrefix("any ") { 63 | // Fix `any ProtocolName?` --> `(any ProtocolName)?` 64 | return "(\(unwrappedTypeName))?" 65 | } 66 | 67 | return actualTypeName?.mockTypeName ?? name 68 | } 69 | 70 | var needsSubjectMapToReturnType: Bool { 71 | if isTuple, let tuple { 72 | for element in tuple.elements { 73 | if element.typeName.isGeneric { 74 | return false 75 | } 76 | } 77 | return false 78 | } 79 | 80 | if isArray, let array { 81 | return array.elementTypeName.needsSubjectMapToReturnType 82 | } 83 | 84 | if isDictionary, let dictionary { 85 | return dictionary.valueTypeName.needsSubjectMapToReturnType 86 | } 87 | 88 | return false 89 | } 90 | 91 | func hasComplexTypeWithSmartDefaultValue(isProperty: Bool) -> Bool { 92 | return (try? smartDefaultValueImplementation(isProperty: isProperty, mockVariablePrefix: "")) != nil 93 | } 94 | 95 | func smartDefaultValueImplementation(isProperty: Bool, mockVariablePrefix: String, forceCastingToReturnTypeName: Bool = false) throws -> (getterImplementation: SourceCode, mockedVariableHandlers: [SourceCode]) { 96 | if isGeneric, 97 | let generic = generic, 98 | generic.name == "Single" || generic.name == "Observable" || generic.name == "AnyObserver", 99 | generic.typeParameters.count == 1 { 100 | 101 | let returnTypeName = generic.typeParameters[0].typeName.mockTypeName 102 | let forceCasting = forceCastingToReturnTypeName && !isVoid ? " as! \(name.trimmingWhereClause())" : "" 103 | switch generic.name { 104 | case "Single": 105 | let getterImplementation = SourceCode("return Single.create { (observer: @escaping (SingleEvent<\(returnTypeName)>) -> ()) -> Disposable in") { [ 106 | SourceCode(""" 107 | return self.\(mockVariablePrefix)Subject.subscribe { (event: Event<\(returnTypeName)>) in 108 | switch event { 109 | case .next(let element): 110 | observer(.success(element)) 111 | case .error(let error): 112 | observer(.failure(error)) 113 | default: 114 | break 115 | } 116 | } 117 | """) 118 | ]} 119 | let mockedVariableHandlers = [SourceCode("lazy var \(mockVariablePrefix)Subject = PublishSubject<\(returnTypeName)>()")] 120 | return (getterImplementation, mockedVariableHandlers) 121 | case "Observable": 122 | let optionalMappingClauseForTupleTypes = generic.typeParameters[0].typeName.needsSubjectMapToReturnType ? ".map { $0 }" : "" 123 | let getterImplementation = SourceCode("return \(mockVariablePrefix)Subject\(optionalMappingClauseForTupleTypes).as\(generic.name)()\(forceCasting)") 124 | let mockedVariableHandlers = [SourceCode("lazy var \(mockVariablePrefix)Subject = PublishSubject<\(returnTypeName)>()")] 125 | return (getterImplementation, mockedVariableHandlers) 126 | case "AnyObserver": 127 | let getterImplementation = SourceCode("return AnyObserver { [weak self] event in") { [ 128 | SourceCode("self?.\(mockVariablePrefix)EventCallCount += 1"), 129 | SourceCode("self?.\(mockVariablePrefix)EventHandler?(event)"), 130 | ]} 131 | let mockedVariableHandlers: [SourceCode] = [ 132 | SourceCode("var \(mockVariablePrefix)EventCallCount: Int = 0"), 133 | SourceCode("var \(mockVariablePrefix)EventHandler: ((Event<\(returnTypeName)>) -> ())? = nil"), 134 | ] 135 | return (getterImplementation, mockedVariableHandlers) 136 | default: 137 | fatalError("Should not happen") 138 | } 139 | } 140 | 141 | if unwrappedTypeName == "Disposable" { 142 | guard !isProperty else { throw MockError.noDefaultValue(typeName: self) } // Only functions with `Disposable` return type are supported. 143 | let getterImplementation = SourceCode("return Disposables.create { [weak self] in") { [ 144 | SourceCode("self?.\(mockVariablePrefix)DisposeCallCount += 1"), 145 | SourceCode("self?.\(mockVariablePrefix)DisposeHandler?()"), 146 | ]} 147 | let mockedVariableHandlers: [SourceCode] = [ 148 | SourceCode("var \(mockVariablePrefix)DisposeCallCount: Int = 0"), 149 | SourceCode("var \(mockVariablePrefix)DisposeHandler: (() -> ())? = nil"), 150 | ] 151 | return (getterImplementation, mockedVariableHandlers) 152 | } 153 | 154 | throw MockError.noDefaultValue(typeName: self) 155 | } 156 | } 157 | -------------------------------------------------------------------------------- /templates/TypeErase.swifttemplate: -------------------------------------------------------------------------------- 1 | <%# 2 | A type erasure is a Swift-language pattern to combine both generic type parameters and associatedtypes, e.g.: 3 | ```swift 4 | protocol PresenterType { 5 | associatedtype Model 6 | func present(_ model: Model) 7 | } 8 | class TablePresenter: PresenterType { 9 | // ... 10 | } 11 | class CollectionPresenter: PresenterType { 12 | // ... 13 | } 14 | struct CarModel {} 15 | struct ShipModel {} 16 | let carPresenters = [AnyPresenter(TablePresenter()), AnyPresenter(CollectionPresenter())] 17 | let shipPresenters = [AnyPresenter(TablePresenter()), AnyPresenter(CollectionPresenter())] 18 | ``` 19 | The pattern implemented here follows this article: https://www.bignerdranch.com/blog/breaking-down-type-erasures-in-swift/ 20 | -%> 21 | <%- include("_header") -%> 22 | <%- includeFile("Utility/Annotations") -%> 23 | <%- includeFile("Utility/Generics") -%> 24 | <%- includeFile("Utility/String") -%> 25 | <%- includeFile("Utility/StringLettercase") -%> 26 | <%_ 27 | let typesToProcess = types.protocols.filter { $0.annotations["TypeErase"] != nil } 28 | generateAdditionalImports(typesToProcess) 29 | 30 | for type in typesToProcess { 31 | let associatedTypes: [GenericTypeInfo] = type.annotatedAssociatedTypes() 32 | let genericTypesModifier = !associatedTypes.isEmpty ? "<\(associatedTypes.map { $0.genericType }.joined(separator: ", "))>" : "" 33 | let genericTypesConstraints: String = { 34 | let constraints = associatedTypes.flatMap { $0.constraints }.sorted() 35 | return !constraints.isEmpty ? " where \(constraints.joined(separator: ", "))" : "" 36 | }() 37 | 38 | let allVariables = type.allVariables.filter { !$0.isStatic } 39 | let allMethods = type.allMethods.filter { !$0.isStatic } 40 | -%> 41 | 42 | // MARK: - Type erasure for `<%=type.name%>` 43 | 44 | private class _Any<%=type.name%>Base<%=genericTypesModifier%>: <%=type.name%><%=genericTypesConstraints%> { 45 | init() { 46 | guard type(of: self) != _Any<%=type.name%>Base.self else { 47 | fatalError("_Any<%=type.name%>Base<%=genericTypesModifier%> instances can not be created; create a subclass instance instead") 48 | } 49 | } 50 | <%_ if !allVariables.isEmpty { -%> 51 | 52 | <%_ for p in allVariables { -%> 53 | var <%=p.name%>: <%=p.typeName%> { 54 | get { fatalError("Must override") } 55 | <% if p.isMutable { %>set { fatalError("Must override") }<% } %> 56 | } 57 | <%_ } -%> 58 | <%_ } -%> 59 | <%_ if !allMethods.isEmpty { -%> 60 | 61 | <%_ for m in allMethods { -%> 62 | func <%=m.name%><%=m.throws ? " throws" : ""%> -> <%=m.returnTypeName%> { 63 | fatalError("Must override") 64 | } 65 | <%_ } -%> 66 | <%_ } -%> 67 | } 68 | 69 | private final class _Any<%=type.name%>Box>: _Any<%=type.name%>Base<%=!associatedTypes.isEmpty ? "<\(associatedTypes.map{"Concrete.\($0.genericType)"}.joined(separator: ", "))>" : ""%> { 70 | private let concrete: Concrete 71 | <%_ for a in associatedTypes { -%> 72 | typealias <%=a.genericType%> = Concrete.<%=a.genericType%> 73 | <%_ } -%> 74 | 75 | init(_ concrete: Concrete) { 76 | self.concrete = concrete 77 | } 78 | <%_ if !allVariables.isEmpty { -%> 79 | 80 | <%_ for p in allVariables { -%> 81 | override var <%=p.name%>: <%=p.typeName%> { 82 | get { return concrete.<%=p.name%> } 83 | <% if p.isMutable { %>set { concrete.<%=p.name%> = newValue }<% } %> 84 | } 85 | <%_ } -%> 86 | <%_ } -%> 87 | <%_ if !allMethods.isEmpty { -%> 88 | 89 | <%_ for m in allMethods { -%> 90 | override func <%=m.name%><%=m.throws ? " throws" : ""%> -> <%=m.returnTypeName%> { 91 | return <%=m.throws ? "try " : ""%>concrete.<%=m.callName%>(<%= m.parameters.map { "\($0.argumentLabel != nil ? "\($0.argumentLabel!): " : "")\($0.`inout` ? "inout ": "")\($0.name)" }.joined(separator: ", ") %>) 92 | } 93 | <%_ } -%> 94 | <%_ } -%> 95 | } 96 | 97 | <%=type.accessLevel == "public" ? "public " : ""%>final class Any<%=type.name%><%=genericTypesModifier%>: <%=type.name%><%=genericTypesConstraints%> { 98 | private let box: _Any<%=type.name%>Base<%=genericTypesModifier%> 99 | 100 | <%=type.accessLevel == "public" ? "public " : ""%>init>(_ concrete: Concrete)<%=!associatedTypes.isEmpty ? " where \(associatedTypes.map {"Concrete.\($0.genericType) == \($0.genericType)"}.joined(separator: ", "))" : ""%> { 101 | self.box = _Any<%=type.name%>Box(concrete) 102 | } 103 | <%_ if !allVariables.isEmpty { -%> 104 | 105 | <%_ for p in allVariables { -%> 106 | <%=type.accessLevel == "public" ? "public " : ""%>var <%=p.name%>: <%=p.typeName%> { 107 | get { return box.<%=p.name%> } 108 | <% if p.isMutable { %>set { box.<%=p.name%> = newValue }<% } %> 109 | } 110 | <%_ } -%> 111 | <%_ } -%> 112 | <%_ if !allMethods.isEmpty { -%> 113 | 114 | <%_ for m in allMethods { -%> 115 | <%=type.accessLevel == "public" ? "public " : ""%>func <%=m.name%><%=m.throws ? " throws" : ""%> -> <%=m.returnTypeName%> { 116 | return <%=m.throws ? "try " : ""%>box.<%=m.callName%>(<%= m.parameters.map { "\($0.argumentLabel != nil ? "\($0.argumentLabel!): " : "")\($0.`inout` ? "inout ": "")\($0.name)" }.joined(separator: ", ") %>) 117 | } 118 | <%_ } -%> 119 | <%_ } -%> 120 | } 121 | <%_ -%> 122 | <% } -%> 123 | -------------------------------------------------------------------------------- /templates/Utility/Annotations.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | import SourceryRuntime 3 | 4 | extension SourceryRuntime.Annotated { 5 | func annotations(for names: [String]) -> [String] { 6 | return names 7 | .flatMap { extractAnnotations(for: $0) } 8 | .removeDuplicates() 9 | .sorted() 10 | } 11 | 12 | private func extractAnnotations(for name: String) -> [String] { 13 | if let annotations = annotations[caseInsensitive: name] as? [String] { 14 | return annotations.map { $0.trimmingWhitespace() } 15 | } else if let annotation = annotations[caseInsensitive: name] as? String { 16 | return [annotation.trimmingWhitespace()] 17 | } else { 18 | return [] 19 | } 20 | } 21 | 22 | // `get`-only variable requirements in protocols are considered mutable and are mocked using `var` declarations by default. 23 | // To generate a `let` declaration, annotate with `sourcery: const`. 24 | var isAnnotatedConst: Bool { 25 | return annotations[caseInsensitive: "const"] != nil 26 | } 27 | 28 | // For a 'get'-only variable requirement in the protocol, determine if it should be included in the mock class' initializer list. 29 | var isAnnotatedInit: Bool { 30 | precondition(!isAnnotatedInitInternal || !isAnnotatedHandlerInternal, "`isAnnotatedInit` is mutually exclusive with `isAnnotatedHandler`") 31 | return isAnnotatedInitInternal 32 | } 33 | private var isAnnotatedInitInternal: Bool { 34 | return annotations[caseInsensitive: "init"] != nil 35 | } 36 | 37 | // For a `get`-only variable requirement in the protocol, 38 | var isAnnotatedHandler: Bool { 39 | precondition(!isAnnotatedHandlerInternal || !isAnnotatedInitInternal, "`isAnnotatedHandler` is mutually exclusive with `isAnnotatedInit`") 40 | return isAnnotatedHandlerInternal 41 | } 42 | private var isAnnotatedHandlerInternal: Bool { 43 | return annotations[caseInsensitive: "handler"] != nil 44 | } 45 | } 46 | 47 | private extension Dictionary where Key == String { 48 | subscript(caseInsensitive key: Key) -> Value? { 49 | return first { $0.0.lowercased() == key.lowercased() }?.value 50 | } 51 | } 52 | 53 | private extension Array where Element: Hashable, Element: Comparable { 54 | func removeDuplicates() -> [Element] { 55 | return Array(Set(self)).sorted() 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /templates/Utility/Generics.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | import SourceryRuntime 3 | 4 | struct GenericTypeInfo { 5 | let genericType: String // The generic type name, e.g. "S" 6 | let constraints: Set // Potentially, a constraint on the genericType's subtype, e.g., "S.Iterator.Element: Object" 7 | } 8 | 9 | extension SourceryRuntime.Annotated { 10 | func annotatedAssociatedTypes() -> [GenericTypeInfo] { 11 | return extractAnnotatedGenericTypes(for: ["associatedtype", "associatedtypes"]) 12 | } 13 | func annotatedGenericTypes() -> [GenericTypeInfo] { 14 | return extractAnnotatedGenericTypes(for: ["generictype", "generictypes"]) 15 | } 16 | 17 | // Each element in `names` might contain several declarations, e.g., "S: Sequence, S: Annotable, S.Iterator.Element: Object" 18 | private func extractAnnotatedGenericTypes(for names: [String]) -> [GenericTypeInfo] { 19 | return annotations(for: names) 20 | .flatMap { 21 | return $0.commaSeparated().map { constraint in 22 | let trimmed = constraint.trimmingWhitespace() 23 | let split = trimmed.colonSeparated() 24 | let associatedType = String(split[0].split(separator: ".", maxSplits: 1)[0]) // In case it's from the generic constraint of the form "S.Iterator.Element: Object" 25 | let constraints: [String] = split.count == 2 ? [trimmed] : [] // This will hold the original value. 26 | return GenericTypeInfo(genericType: associatedType, constraints: Set(constraints)) 27 | } 28 | } 29 | .merged() 30 | } 31 | } 32 | 33 | extension Collection where Element == GenericTypeInfo { 34 | // Merge elements with the same `genericType` value, combining `constraints` of merged elements. 35 | func merged() -> [GenericTypeInfo] { 36 | return reduce(into: []) { (partialResult: inout [GenericTypeInfo], element: GenericTypeInfo) in 37 | if let index = partialResult.firstIndex(where: { $0.genericType == element.genericType }) { 38 | partialResult[index] = GenericTypeInfo(genericType: element.genericType, constraints: partialResult[index].constraints.union(element.constraints)) 39 | } else { 40 | partialResult.append(element) 41 | } 42 | } 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /templates/Utility/String.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | 3 | extension String { 4 | func trimmingWhitespace() -> String { 5 | return trimmingCharacters(in: .whitespacesAndNewlines) 6 | } 7 | 8 | func trimmingWhereClause() -> String { 9 | if let rangeOfWhereClause = range(of: " where ") { 10 | return String(self[.. [String] { 20 | return components(separatedBy: ",", excludingDelimiterBetween: ("<[({", "})]>")) 21 | } 22 | 23 | /// :nodoc: 24 | /// Returns components separated with colon respecting nested types 25 | func colonSeparated() -> [String] { 26 | return components(separatedBy: ":", excludingDelimiterBetween: ("<[({", "})]>")) 27 | } 28 | 29 | /// :nodoc: 30 | /// Returns components separated with semicolon respecting nested contexts 31 | func semicolonSeparated() -> [String] { 32 | return components(separatedBy: ";", excludingDelimiterBetween: ("{", "}")) 33 | } 34 | 35 | /// :nodoc: 36 | func components(separatedBy delimiter: String, excludingDelimiterBetween between: (open: String, close: String)) -> [String] { 37 | var boundingCharactersCount: Int = 0 38 | var quotesCount: Int = 0 39 | var item = "" 40 | var items = [String]() 41 | var matchedDelimiter = (alreadyMatched: "", leftToMatch: delimiter) 42 | 43 | for char in self { 44 | if between.open.contains(char) { 45 | if !(boundingCharactersCount == 0 && String(char) == delimiter) { 46 | boundingCharactersCount += 1 47 | } 48 | } else if between.close.contains(char) { 49 | // do not count `->` 50 | if !(char == ">" && item.last == "-") { 51 | boundingCharactersCount = max(0, boundingCharactersCount - 1) 52 | } 53 | } 54 | if char == "\"" { 55 | quotesCount += 1 56 | } 57 | 58 | guard boundingCharactersCount == 0 && quotesCount % 2 == 0 else { 59 | item.append(char) 60 | continue 61 | } 62 | 63 | if char == matchedDelimiter.leftToMatch.first { 64 | matchedDelimiter.alreadyMatched.append(char) 65 | matchedDelimiter.leftToMatch = String(matchedDelimiter.leftToMatch.dropFirst()) 66 | if matchedDelimiter.leftToMatch.isEmpty { 67 | items.append(item) 68 | item = "" 69 | matchedDelimiter = (alreadyMatched: "", leftToMatch: delimiter) 70 | } 71 | } else { 72 | if matchedDelimiter.alreadyMatched.isEmpty { 73 | item.append(char) 74 | } else { 75 | item.append(matchedDelimiter.alreadyMatched) 76 | matchedDelimiter = (alreadyMatched: "", leftToMatch: delimiter) 77 | } 78 | } 79 | } 80 | items.append(item) 81 | return items 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /templates/Utility/StringLettercase.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | 3 | // Courtesy of https://github.com/SwiftGen/StencilSwiftKit 4 | 5 | extension String { 6 | func lowercasedFirstLetter() -> String { 7 | return FiltersStrings.lowerFirstLetter(self) 8 | } 9 | func lowercasedFirstWord() -> String { 10 | return FiltersStrings.lowerFirstWord(self) 11 | } 12 | func uppercasedFirstLetter() -> String { 13 | return FiltersStrings.upperFirstLetter(self) 14 | } 15 | func snakeCased(toLower: Bool = true) -> String { 16 | return FiltersStrings.camelToSnakeCase(self, toLower: toLower) 17 | } 18 | func camelCased(stripLeading: Bool = false) -> String { 19 | return FiltersStrings.snakeToCamelCase(self, stripLeading: stripLeading) 20 | } 21 | } 22 | 23 | private final class FiltersStrings { 24 | /// Lowers the first letter of the string 25 | /// e.g. "People picker" gives "people picker", "Sports Stats" gives "sports Stats" 26 | static func lowerFirstLetter(_ string: String) -> String { 27 | let first = String(string.prefix(1)).lowercased() 28 | let other = String(string.dropFirst(1)) 29 | return first + other 30 | } 31 | 32 | /// If the string starts with only one uppercase letter, lowercase that first letter 33 | /// If the string starts with multiple uppercase letters, lowercase those first letters 34 | /// up to the one before the last uppercase one, but only if the last one is followed by 35 | /// a lowercase character. 36 | /// e.g. "PeoplePicker" gives "peoplePicker" but "URLChooser" gives "urlChooser" 37 | static func lowerFirstWord(_ string: String) -> String { 38 | let cs = CharacterSet.uppercaseLetters 39 | let scalars = string.unicodeScalars 40 | let start = scalars.startIndex 41 | var idx = start 42 | while let scalar = UnicodeScalar(scalars[idx].value), cs.contains(scalar) && idx <= scalars.endIndex { 43 | idx = scalars.index(after: idx) 44 | } 45 | if idx > scalars.index(after: start) && idx < scalars.endIndex, 46 | let scalar = UnicodeScalar(scalars[idx].value), 47 | CharacterSet.lowercaseLetters.contains(scalar) { 48 | idx = scalars.index(before: idx) 49 | } 50 | let transformed = String(scalars[start.. String { 62 | return _upperFirstLetter(string) 63 | } 64 | 65 | /// Converts camelCase to snake_case. Takes an optional Bool argument for making the string lower case, 66 | /// which defaults to true 67 | /// 68 | /// - Parameters: 69 | /// - value: the value to be processed 70 | /// - arguments: the arguments to the function; expecting zero or one boolean argument 71 | /// - Returns: the snake case string 72 | static func camelToSnakeCase(_ string: String, toLower: Bool = true) -> String { 73 | let snakeCase = snakecase(string) 74 | if toLower { 75 | return snakeCase.lowercased() 76 | } 77 | return snakeCase 78 | } 79 | 80 | /// Converts snake_case to camelCase, stripping prefix underscores if needed 81 | /// 82 | /// - Parameters: 83 | /// - string: the value to be processed 84 | /// - stripLeading: if false, will preserve leading underscores 85 | /// - Returns: the camel case string 86 | static func snakeToCamelCase(_ string: String, stripLeading: Bool) -> String { 87 | let unprefixed: String 88 | if containsAnyLowercasedChar(string) { 89 | let comps = string.components(separatedBy: "_") 90 | unprefixed = comps.map { upperFirstLetter($0) }.joined(separator: "") 91 | } else { 92 | let comps = snakecase(string).components(separatedBy: "_") 93 | unprefixed = comps.map { $0.capitalized }.joined(separator: "") 94 | } 95 | 96 | // only if passed true, strip the prefix underscores 97 | var prefixUnderscores = "" 98 | var result: String { return prefixUnderscores + unprefixed } 99 | if stripLeading { 100 | return result 101 | } 102 | for scalar in string.unicodeScalars { 103 | guard scalar == "_" else { break } 104 | prefixUnderscores += "_" 105 | } 106 | return result 107 | } 108 | 109 | // MARK: - Private 110 | private static func containsAnyLowercasedChar(_ string: String) -> Bool { 111 | let lowercaseCharRegex = try! NSRegularExpression(pattern: "[a-z]", options: .dotMatchesLineSeparators) 112 | let fullRange = NSRange(location: 0, length: string.unicodeScalars.count) 113 | return lowercaseCharRegex.firstMatch(in: string, options: .reportCompletion, range: fullRange) != nil 114 | } 115 | 116 | /// Uppers the first letter of the string 117 | /// e.g. "people picker" gives "People picker", "sports Stats" gives "Sports Stats" 118 | /// 119 | /// - Parameters: 120 | /// - value: the value to uppercase first letter of 121 | /// - arguments: the arguments to the function; expecting zero 122 | /// - Returns: the string with first letter being uppercased 123 | private static func _upperFirstLetter(_ string: String) -> String { 124 | guard let first = string.unicodeScalars.first else { return string } 125 | return String(first).uppercased() + String(string.unicodeScalars.dropFirst()) 126 | } 127 | 128 | /// This returns the snake cased variant of the string. 129 | /// 130 | /// - Parameter string: The string to snake_case 131 | /// - Returns: The string snake cased from either snake_cased or camelCased string. 132 | private static func snakecase(_ string: String) -> String { 133 | let longUpper = try! NSRegularExpression(pattern: "([A-Z\\d]+)([A-Z][a-z])", options: .dotMatchesLineSeparators) 134 | let camelCased = try! NSRegularExpression(pattern: "([a-z\\d])([A-Z])", options: .dotMatchesLineSeparators) 135 | 136 | let fullRange = NSRange(location: 0, length: string.unicodeScalars.count) 137 | var result = longUpper.stringByReplacingMatches(in: string, 138 | options: .reportCompletion, 139 | range: fullRange, 140 | withTemplate: "$1_$2") 141 | result = camelCased.stringByReplacingMatches(in: result, 142 | options: .reportCompletion, 143 | range: fullRange, 144 | withTemplate: "$1_$2") 145 | return result.replacingOccurrences(of: "-", with: "_") 146 | } 147 | } 148 | -------------------------------------------------------------------------------- /templates/_header.swifttemplate: -------------------------------------------------------------------------------- 1 | <%# ================================================== SwiftLint -%><%_ -%> 2 | <%_ if let rules = argument["excludedSwiftLintRules"] as? [String] { -%> 3 | <%_ for rule in rules { -%> 4 | <%_ %>//swiftlint:disable <%= rule %> 5 | <%_ } -%> 6 | <%_ } -%> 7 | 8 | <%# ================================================== IMPORTS -%><%_ -%> 9 | <%_ 10 | func generateAdditionalImports(_ types: [Type]) { 11 | let additionalImports = Set(types.flatMap { $0.annotations(for: ["import"]) }) 12 | let existingImports = Set(extractImports()) 13 | let missingImports = additionalImports.subtracting(existingImports).sorted() 14 | guard !missingImports.isEmpty else { return } 15 | print(missingImports.map { "import \($0)" }.joined(separator: "\n")) 16 | } 17 | 18 | func extractImports(attributeName: String = "import") -> [String] { 19 | if let imported = argument[attributeName] as? String { 20 | return [imported] 21 | } else if let allimported = argument[attributeName] as? [String] { 22 | return allimported 23 | } else { 24 | return [] 25 | } 26 | } 27 | -%> 28 | <%_ for imported in extractImports().sorted() { -%> 29 | <%_ %>import <%= imported %> 30 | <%_ } -%> 31 | <%_ for imported in extractImports(attributeName: "testable").sorted() { -%> 32 | <%_ %>@testable import <%= imported %> 33 | <%_ } -%> 34 | --------------------------------------------------------------------------------