├── .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 |
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 |
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 |
--------------------------------------------------------------------------------