├── .gitignore
├── .swift-version
├── GenericCellControllers.podspec
├── GenericCellControllers.xcworkspace
├── contents.xcworkspacedata
└── xcshareddata
│ └── IDEWorkspaceChecks.plist
├── GenericCellControllers
├── GenericCellControllers.xcodeproj
│ ├── project.pbxproj
│ └── xcshareddata
│ │ └── xcschemes
│ │ └── GenericCellControllers.xcscheme
├── Source
│ ├── CellControllers
│ │ ├── CellController.swift
│ │ └── GenericCellController.swift
│ ├── GenericCellControllers.h
│ ├── Info.plist
│ └── ReusableCell
│ │ └── ReusableCellHolder.swift
└── Tests
│ └── Info.plist
├── GenericCellControllersExample
├── GenericCellControllersExample.xcodeproj
│ └── project.pbxproj
└── GenericCellControllersExample
│ ├── AppDelegate.swift
│ ├── Assets.xcassets
│ └── AppIcon.appiconset
│ │ └── Contents.json
│ ├── Base.lproj
│ └── LaunchScreen.storyboard
│ ├── Feed
│ ├── Cells
│ │ ├── FeedCellControllerFactory.swift
│ │ ├── Image
│ │ │ ├── FeedImageCellController.swift
│ │ │ ├── FeedImageTableViewCell.swift
│ │ │ └── FeedImageTableViewCell.xib
│ │ └── Text
│ │ │ ├── FeedTextCellController.swift
│ │ │ ├── FeedTextTableViewCell.swift
│ │ │ └── FeedTextTableViewCell.xib
│ ├── FeedCoordinator.swift
│ ├── FeedTableViewController.swift
│ ├── FeedTableViewController.xib
│ └── Models
│ │ ├── FeedDataProvider.swift
│ │ └── FeedModels.swift
│ ├── ImageDetail
│ ├── ImageDetailViewController.swift
│ └── ImageDetailViewController.xib
│ ├── Info.plist
│ ├── Resources
│ ├── cat01.jpg
│ ├── cat02.jpg
│ ├── cat03.jpg
│ ├── cat04.jpg
│ ├── cat05.jpg
│ ├── cat06.jpg
│ └── cat07.jpg
│ └── TextDetail
│ ├── TextDetailViewController.swift
│ └── TextDetailViewController.xib
├── LICENSE
└── README.md
/.gitignore:
--------------------------------------------------------------------------------
1 | ## OS X Finder
2 | .DS_Store
3 |
4 | ## Build generated
5 | build/
6 | DerivedData
7 |
8 | ## Various settings
9 | *.pbxuser
10 | !default.pbxuser
11 | *.mode1v3
12 | !default.mode1v3
13 | *.mode2v3
14 | !default.mode2v3
15 | *.perspectivev3
16 | !default.perspectivev3
17 | xcuserdata
18 |
19 | ## Other
20 | *.xccheckout
21 | *.moved-aside
22 | *.xcuserstate
23 | *.xcscmblueprint
24 |
25 | ## Obj-C/Swift specific
26 | *.hmap
27 | *.ipa
28 |
29 | # Swift Package Manager
30 | .build/
31 |
32 |
--------------------------------------------------------------------------------
/.swift-version:
--------------------------------------------------------------------------------
1 | 5.0
2 |
--------------------------------------------------------------------------------
/GenericCellControllers.podspec:
--------------------------------------------------------------------------------
1 |
2 | Pod::Spec.new do |spec|
3 | spec.name = 'GenericCellControllers'
4 | spec.version = '1.2.0'
5 | spec.license = { :type => 'MIT' }
6 | spec.homepage = 'https://github.com/Busuu/generic-cell-controllers'
7 | spec.authors = { 'Javier Valdera' => 'javier.valdera@gmail.com' }
8 | spec.summary = 'Framework that makes it easier to work with heterogeneous lists of data in iOS.'
9 | spec.description = <<-DESC
10 | Generic Cell Controllers provide protocols, implementations and extensions that will make work
11 | with heterogeneous lists of data in iOS really easy. It provides a way to abstract the details
12 | of each particular cell configuration and management away from the View Controllers, Data
13 | Sources and Delegates.
14 | DESC
15 |
16 | spec.source = { :git => 'https://github.com/Busuu/generic-cell-controllers.git', :tag => '1.2.0' }
17 | spec.swift_version = '5.0'
18 |
19 | spec.platform = :ios, "12.0"
20 | spec.ios.deployment_target = '12.0'
21 |
22 | spec.source_files = 'GenericCellControllers/Source/**/*.{h,m,swift}'
23 | spec.ios.source_files = 'GenericCellControllers/Source/**/*.{h,m,swift}'
24 |
25 | spec.ios.framework = 'UIKit'
26 | end
27 |
--------------------------------------------------------------------------------
/GenericCellControllers.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/GenericCellControllers.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | IDEDidComputeMac32BitWarning
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/GenericCellControllers/GenericCellControllers.xcodeproj/project.pbxproj:
--------------------------------------------------------------------------------
1 | // !$*UTF8*$!
2 | {
3 | archiveVersion = 1;
4 | classes = {
5 | };
6 | objectVersion = 46;
7 | objects = {
8 |
9 | /* Begin PBXBuildFile section */
10 | CB5261171F0AA79000C7BF65 /* GenericCellControllers.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = CB52610D1F0AA79000C7BF65 /* GenericCellControllers.framework */; };
11 | CB52612A1F0AA7B600C7BF65 /* GenericCellControllers.h in Headers */ = {isa = PBXBuildFile; fileRef = CB5261281F0AA7B600C7BF65 /* GenericCellControllers.h */; settings = {ATTRIBUTES = (Public, ); }; };
12 | CBA31A271F7BB48F00E6BFFE /* GenericCellController.swift in Sources */ = {isa = PBXBuildFile; fileRef = CBA31A261F7BB48F00E6BFFE /* GenericCellController.swift */; };
13 | CBB1F0DE1F0AA96500F8D4CD /* CellController.swift in Sources */ = {isa = PBXBuildFile; fileRef = CBB1F0DB1F0AA96500F8D4CD /* CellController.swift */; };
14 | CBB1F0DF1F0AA96500F8D4CD /* ReusableCellHolder.swift in Sources */ = {isa = PBXBuildFile; fileRef = CBB1F0DD1F0AA96500F8D4CD /* ReusableCellHolder.swift */; };
15 | /* End PBXBuildFile section */
16 |
17 | /* Begin PBXContainerItemProxy section */
18 | CB5261181F0AA79000C7BF65 /* PBXContainerItemProxy */ = {
19 | isa = PBXContainerItemProxy;
20 | containerPortal = CB5261041F0AA78F00C7BF65 /* Project object */;
21 | proxyType = 1;
22 | remoteGlobalIDString = CB52610C1F0AA78F00C7BF65;
23 | remoteInfo = GenericCellControllers;
24 | };
25 | /* End PBXContainerItemProxy section */
26 |
27 | /* Begin PBXFileReference section */
28 | CB3D16CA1F167D0B00FE6CE3 /* GenericCellControllers.podspec */ = {isa = PBXFileReference; lastKnownFileType = text; name = GenericCellControllers.podspec; path = ../GenericCellControllers.podspec; sourceTree = ""; };
29 | CB52610D1F0AA79000C7BF65 /* GenericCellControllers.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = GenericCellControllers.framework; sourceTree = BUILT_PRODUCTS_DIR; };
30 | CB5261161F0AA79000C7BF65 /* GenericCellControllersTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = GenericCellControllersTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
31 | CB5261281F0AA7B600C7BF65 /* GenericCellControllers.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GenericCellControllers.h; sourceTree = ""; };
32 | CB5261291F0AA7B600C7BF65 /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; };
33 | CB52612E1F0AA7C300C7BF65 /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; };
34 | CBA31A261F7BB48F00E6BFFE /* GenericCellController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GenericCellController.swift; sourceTree = ""; };
35 | CBB1F0DB1F0AA96500F8D4CD /* CellController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CellController.swift; sourceTree = ""; };
36 | CBB1F0DD1F0AA96500F8D4CD /* ReusableCellHolder.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ReusableCellHolder.swift; sourceTree = ""; };
37 | /* End PBXFileReference section */
38 |
39 | /* Begin PBXFrameworksBuildPhase section */
40 | CB5261091F0AA78F00C7BF65 /* Frameworks */ = {
41 | isa = PBXFrameworksBuildPhase;
42 | buildActionMask = 2147483647;
43 | files = (
44 | );
45 | runOnlyForDeploymentPostprocessing = 0;
46 | };
47 | CB5261131F0AA79000C7BF65 /* Frameworks */ = {
48 | isa = PBXFrameworksBuildPhase;
49 | buildActionMask = 2147483647;
50 | files = (
51 | CB5261171F0AA79000C7BF65 /* GenericCellControllers.framework in Frameworks */,
52 | );
53 | runOnlyForDeploymentPostprocessing = 0;
54 | };
55 | /* End PBXFrameworksBuildPhase section */
56 |
57 | /* Begin PBXGroup section */
58 | CB5261031F0AA78F00C7BF65 = {
59 | isa = PBXGroup;
60 | children = (
61 | CB3D16CA1F167D0B00FE6CE3 /* GenericCellControllers.podspec */,
62 | CB5261271F0AA7B600C7BF65 /* Source */,
63 | CB52612C1F0AA7C300C7BF65 /* Tests */,
64 | CB52610E1F0AA79000C7BF65 /* Products */,
65 | );
66 | sourceTree = "";
67 | };
68 | CB52610E1F0AA79000C7BF65 /* Products */ = {
69 | isa = PBXGroup;
70 | children = (
71 | CB52610D1F0AA79000C7BF65 /* GenericCellControllers.framework */,
72 | CB5261161F0AA79000C7BF65 /* GenericCellControllersTests.xctest */,
73 | );
74 | name = Products;
75 | sourceTree = "";
76 | };
77 | CB5261271F0AA7B600C7BF65 /* Source */ = {
78 | isa = PBXGroup;
79 | children = (
80 | CBB1F0DA1F0AA96500F8D4CD /* CellControllers */,
81 | CBB1F0DC1F0AA96500F8D4CD /* ReusableCell */,
82 | CB5261281F0AA7B600C7BF65 /* GenericCellControllers.h */,
83 | CB5261291F0AA7B600C7BF65 /* Info.plist */,
84 | );
85 | path = Source;
86 | sourceTree = "";
87 | };
88 | CB52612C1F0AA7C300C7BF65 /* Tests */ = {
89 | isa = PBXGroup;
90 | children = (
91 | CB52612E1F0AA7C300C7BF65 /* Info.plist */,
92 | );
93 | path = Tests;
94 | sourceTree = "";
95 | };
96 | CBB1F0DA1F0AA96500F8D4CD /* CellControllers */ = {
97 | isa = PBXGroup;
98 | children = (
99 | CBB1F0DB1F0AA96500F8D4CD /* CellController.swift */,
100 | CBA31A261F7BB48F00E6BFFE /* GenericCellController.swift */,
101 | );
102 | path = CellControllers;
103 | sourceTree = "";
104 | };
105 | CBB1F0DC1F0AA96500F8D4CD /* ReusableCell */ = {
106 | isa = PBXGroup;
107 | children = (
108 | CBB1F0DD1F0AA96500F8D4CD /* ReusableCellHolder.swift */,
109 | );
110 | path = ReusableCell;
111 | sourceTree = "";
112 | };
113 | /* End PBXGroup section */
114 |
115 | /* Begin PBXHeadersBuildPhase section */
116 | CB52610A1F0AA78F00C7BF65 /* Headers */ = {
117 | isa = PBXHeadersBuildPhase;
118 | buildActionMask = 2147483647;
119 | files = (
120 | CB52612A1F0AA7B600C7BF65 /* GenericCellControllers.h in Headers */,
121 | );
122 | runOnlyForDeploymentPostprocessing = 0;
123 | };
124 | /* End PBXHeadersBuildPhase section */
125 |
126 | /* Begin PBXNativeTarget section */
127 | CB52610C1F0AA78F00C7BF65 /* GenericCellControllers */ = {
128 | isa = PBXNativeTarget;
129 | buildConfigurationList = CB5261211F0AA79000C7BF65 /* Build configuration list for PBXNativeTarget "GenericCellControllers" */;
130 | buildPhases = (
131 | CB5261081F0AA78F00C7BF65 /* Sources */,
132 | CB5261091F0AA78F00C7BF65 /* Frameworks */,
133 | CB52610A1F0AA78F00C7BF65 /* Headers */,
134 | CB52610B1F0AA78F00C7BF65 /* Resources */,
135 | );
136 | buildRules = (
137 | );
138 | dependencies = (
139 | );
140 | name = GenericCellControllers;
141 | productName = GenericCellControllers;
142 | productReference = CB52610D1F0AA79000C7BF65 /* GenericCellControllers.framework */;
143 | productType = "com.apple.product-type.framework";
144 | };
145 | CB5261151F0AA79000C7BF65 /* GenericCellControllersTests */ = {
146 | isa = PBXNativeTarget;
147 | buildConfigurationList = CB5261241F0AA79000C7BF65 /* Build configuration list for PBXNativeTarget "GenericCellControllersTests" */;
148 | buildPhases = (
149 | CB5261121F0AA79000C7BF65 /* Sources */,
150 | CB5261131F0AA79000C7BF65 /* Frameworks */,
151 | CB5261141F0AA79000C7BF65 /* Resources */,
152 | );
153 | buildRules = (
154 | );
155 | dependencies = (
156 | CB5261191F0AA79000C7BF65 /* PBXTargetDependency */,
157 | );
158 | name = GenericCellControllersTests;
159 | productName = GenericCellControllersTests;
160 | productReference = CB5261161F0AA79000C7BF65 /* GenericCellControllersTests.xctest */;
161 | productType = "com.apple.product-type.bundle.unit-test";
162 | };
163 | /* End PBXNativeTarget section */
164 |
165 | /* Begin PBXProject section */
166 | CB5261041F0AA78F00C7BF65 /* Project object */ = {
167 | isa = PBXProject;
168 | attributes = {
169 | LastSwiftUpdateCheck = 0830;
170 | LastUpgradeCheck = 1200;
171 | ORGANIZATIONNAME = "Busuu Ltd.";
172 | TargetAttributes = {
173 | CB52610C1F0AA78F00C7BF65 = {
174 | CreatedOnToolsVersion = 8.3.3;
175 | DevelopmentTeam = 9X4849482L;
176 | LastSwiftMigration = 1200;
177 | ProvisioningStyle = Automatic;
178 | };
179 | CB5261151F0AA79000C7BF65 = {
180 | CreatedOnToolsVersion = 8.3.3;
181 | DevelopmentTeam = 9X4849482L;
182 | ProvisioningStyle = Automatic;
183 | };
184 | };
185 | };
186 | buildConfigurationList = CB5261071F0AA78F00C7BF65 /* Build configuration list for PBXProject "GenericCellControllers" */;
187 | compatibilityVersion = "Xcode 3.2";
188 | developmentRegion = en;
189 | hasScannedForEncodings = 0;
190 | knownRegions = (
191 | en,
192 | Base,
193 | );
194 | mainGroup = CB5261031F0AA78F00C7BF65;
195 | productRefGroup = CB52610E1F0AA79000C7BF65 /* Products */;
196 | projectDirPath = "";
197 | projectRoot = "";
198 | targets = (
199 | CB52610C1F0AA78F00C7BF65 /* GenericCellControllers */,
200 | CB5261151F0AA79000C7BF65 /* GenericCellControllersTests */,
201 | );
202 | };
203 | /* End PBXProject section */
204 |
205 | /* Begin PBXResourcesBuildPhase section */
206 | CB52610B1F0AA78F00C7BF65 /* Resources */ = {
207 | isa = PBXResourcesBuildPhase;
208 | buildActionMask = 2147483647;
209 | files = (
210 | );
211 | runOnlyForDeploymentPostprocessing = 0;
212 | };
213 | CB5261141F0AA79000C7BF65 /* Resources */ = {
214 | isa = PBXResourcesBuildPhase;
215 | buildActionMask = 2147483647;
216 | files = (
217 | );
218 | runOnlyForDeploymentPostprocessing = 0;
219 | };
220 | /* End PBXResourcesBuildPhase section */
221 |
222 | /* Begin PBXSourcesBuildPhase section */
223 | CB5261081F0AA78F00C7BF65 /* Sources */ = {
224 | isa = PBXSourcesBuildPhase;
225 | buildActionMask = 2147483647;
226 | files = (
227 | CBA31A271F7BB48F00E6BFFE /* GenericCellController.swift in Sources */,
228 | CBB1F0DF1F0AA96500F8D4CD /* ReusableCellHolder.swift in Sources */,
229 | CBB1F0DE1F0AA96500F8D4CD /* CellController.swift in Sources */,
230 | );
231 | runOnlyForDeploymentPostprocessing = 0;
232 | };
233 | CB5261121F0AA79000C7BF65 /* Sources */ = {
234 | isa = PBXSourcesBuildPhase;
235 | buildActionMask = 2147483647;
236 | files = (
237 | );
238 | runOnlyForDeploymentPostprocessing = 0;
239 | };
240 | /* End PBXSourcesBuildPhase section */
241 |
242 | /* Begin PBXTargetDependency section */
243 | CB5261191F0AA79000C7BF65 /* PBXTargetDependency */ = {
244 | isa = PBXTargetDependency;
245 | target = CB52610C1F0AA78F00C7BF65 /* GenericCellControllers */;
246 | targetProxy = CB5261181F0AA79000C7BF65 /* PBXContainerItemProxy */;
247 | };
248 | /* End PBXTargetDependency section */
249 |
250 | /* Begin XCBuildConfiguration section */
251 | CB52611F1F0AA79000C7BF65 /* Debug */ = {
252 | isa = XCBuildConfiguration;
253 | buildSettings = {
254 | ALWAYS_SEARCH_USER_PATHS = NO;
255 | CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES;
256 | CLANG_ANALYZER_NONNULL = YES;
257 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
258 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
259 | CLANG_CXX_LIBRARY = "libc++";
260 | CLANG_ENABLE_MODULES = YES;
261 | CLANG_ENABLE_OBJC_ARC = YES;
262 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
263 | CLANG_WARN_BOOL_CONVERSION = YES;
264 | CLANG_WARN_COMMA = YES;
265 | CLANG_WARN_CONSTANT_CONVERSION = YES;
266 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
267 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
268 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
269 | CLANG_WARN_EMPTY_BODY = YES;
270 | CLANG_WARN_ENUM_CONVERSION = YES;
271 | CLANG_WARN_INFINITE_RECURSION = YES;
272 | CLANG_WARN_INT_CONVERSION = YES;
273 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
274 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
275 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
276 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
277 | CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
278 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
279 | CLANG_WARN_STRICT_PROTOTYPES = YES;
280 | CLANG_WARN_SUSPICIOUS_MOVE = YES;
281 | CLANG_WARN_UNREACHABLE_CODE = YES;
282 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
283 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
284 | COPY_PHASE_STRIP = NO;
285 | CURRENT_PROJECT_VERSION = 1;
286 | DEBUG_INFORMATION_FORMAT = dwarf;
287 | ENABLE_STRICT_OBJC_MSGSEND = YES;
288 | ENABLE_TESTABILITY = YES;
289 | GCC_C_LANGUAGE_STANDARD = gnu99;
290 | GCC_DYNAMIC_NO_PIC = NO;
291 | GCC_NO_COMMON_BLOCKS = YES;
292 | GCC_OPTIMIZATION_LEVEL = 0;
293 | GCC_PREPROCESSOR_DEFINITIONS = (
294 | "DEBUG=1",
295 | "$(inherited)",
296 | );
297 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
298 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
299 | GCC_WARN_UNDECLARED_SELECTOR = YES;
300 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
301 | GCC_WARN_UNUSED_FUNCTION = YES;
302 | GCC_WARN_UNUSED_VARIABLE = YES;
303 | IPHONEOS_DEPLOYMENT_TARGET = 12.0;
304 | MTL_ENABLE_DEBUG_INFO = YES;
305 | ONLY_ACTIVE_ARCH = YES;
306 | SDKROOT = iphoneos;
307 | SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG;
308 | SWIFT_OPTIMIZATION_LEVEL = "-Onone";
309 | TARGETED_DEVICE_FAMILY = "1,2";
310 | VERSIONING_SYSTEM = "apple-generic";
311 | VERSION_INFO_PREFIX = "";
312 | };
313 | name = Debug;
314 | };
315 | CB5261201F0AA79000C7BF65 /* Release */ = {
316 | isa = XCBuildConfiguration;
317 | buildSettings = {
318 | ALWAYS_SEARCH_USER_PATHS = NO;
319 | CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES;
320 | CLANG_ANALYZER_NONNULL = YES;
321 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
322 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
323 | CLANG_CXX_LIBRARY = "libc++";
324 | CLANG_ENABLE_MODULES = YES;
325 | CLANG_ENABLE_OBJC_ARC = YES;
326 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
327 | CLANG_WARN_BOOL_CONVERSION = YES;
328 | CLANG_WARN_COMMA = YES;
329 | CLANG_WARN_CONSTANT_CONVERSION = YES;
330 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
331 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
332 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
333 | CLANG_WARN_EMPTY_BODY = YES;
334 | CLANG_WARN_ENUM_CONVERSION = YES;
335 | CLANG_WARN_INFINITE_RECURSION = YES;
336 | CLANG_WARN_INT_CONVERSION = YES;
337 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
338 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
339 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
340 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
341 | CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
342 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
343 | CLANG_WARN_STRICT_PROTOTYPES = YES;
344 | CLANG_WARN_SUSPICIOUS_MOVE = YES;
345 | CLANG_WARN_UNREACHABLE_CODE = YES;
346 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
347 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
348 | COPY_PHASE_STRIP = NO;
349 | CURRENT_PROJECT_VERSION = 1;
350 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
351 | ENABLE_NS_ASSERTIONS = NO;
352 | ENABLE_STRICT_OBJC_MSGSEND = YES;
353 | GCC_C_LANGUAGE_STANDARD = gnu99;
354 | GCC_NO_COMMON_BLOCKS = YES;
355 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
356 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
357 | GCC_WARN_UNDECLARED_SELECTOR = YES;
358 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
359 | GCC_WARN_UNUSED_FUNCTION = YES;
360 | GCC_WARN_UNUSED_VARIABLE = YES;
361 | IPHONEOS_DEPLOYMENT_TARGET = 12.0;
362 | MTL_ENABLE_DEBUG_INFO = NO;
363 | SDKROOT = iphoneos;
364 | SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule";
365 | TARGETED_DEVICE_FAMILY = "1,2";
366 | VALIDATE_PRODUCT = YES;
367 | VERSIONING_SYSTEM = "apple-generic";
368 | VERSION_INFO_PREFIX = "";
369 | };
370 | name = Release;
371 | };
372 | CB5261221F0AA79000C7BF65 /* Debug */ = {
373 | isa = XCBuildConfiguration;
374 | buildSettings = {
375 | CODE_SIGN_IDENTITY = "";
376 | DEFINES_MODULE = YES;
377 | DEVELOPMENT_TEAM = 9X4849482L;
378 | DYLIB_COMPATIBILITY_VERSION = 1;
379 | DYLIB_CURRENT_VERSION = 1;
380 | DYLIB_INSTALL_NAME_BASE = "@rpath";
381 | INFOPLIST_FILE = Source/Info.plist;
382 | INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
383 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
384 | PRODUCT_BUNDLE_IDENTIFIER = com.busuu.GenericCellControllers;
385 | PRODUCT_NAME = "$(TARGET_NAME)";
386 | SKIP_INSTALL = YES;
387 | SWIFT_VERSION = 5.0;
388 | };
389 | name = Debug;
390 | };
391 | CB5261231F0AA79000C7BF65 /* Release */ = {
392 | isa = XCBuildConfiguration;
393 | buildSettings = {
394 | CODE_SIGN_IDENTITY = "";
395 | DEFINES_MODULE = YES;
396 | DEVELOPMENT_TEAM = 9X4849482L;
397 | DYLIB_COMPATIBILITY_VERSION = 1;
398 | DYLIB_CURRENT_VERSION = 1;
399 | DYLIB_INSTALL_NAME_BASE = "@rpath";
400 | INFOPLIST_FILE = Source/Info.plist;
401 | INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
402 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
403 | PRODUCT_BUNDLE_IDENTIFIER = com.busuu.GenericCellControllers;
404 | PRODUCT_NAME = "$(TARGET_NAME)";
405 | SKIP_INSTALL = YES;
406 | SWIFT_VERSION = 5.0;
407 | };
408 | name = Release;
409 | };
410 | CB5261251F0AA79000C7BF65 /* Debug */ = {
411 | isa = XCBuildConfiguration;
412 | buildSettings = {
413 | ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
414 | DEVELOPMENT_TEAM = 9X4849482L;
415 | INFOPLIST_FILE = Tests/Info.plist;
416 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
417 | PRODUCT_BUNDLE_IDENTIFIER = com.busuu.GenericCellControllersTests;
418 | PRODUCT_NAME = "$(TARGET_NAME)";
419 | SWIFT_VERSION = 5.0;
420 | };
421 | name = Debug;
422 | };
423 | CB5261261F0AA79000C7BF65 /* Release */ = {
424 | isa = XCBuildConfiguration;
425 | buildSettings = {
426 | ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
427 | DEVELOPMENT_TEAM = 9X4849482L;
428 | INFOPLIST_FILE = Tests/Info.plist;
429 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
430 | PRODUCT_BUNDLE_IDENTIFIER = com.busuu.GenericCellControllersTests;
431 | PRODUCT_NAME = "$(TARGET_NAME)";
432 | SWIFT_VERSION = 5.0;
433 | };
434 | name = Release;
435 | };
436 | /* End XCBuildConfiguration section */
437 |
438 | /* Begin XCConfigurationList section */
439 | CB5261071F0AA78F00C7BF65 /* Build configuration list for PBXProject "GenericCellControllers" */ = {
440 | isa = XCConfigurationList;
441 | buildConfigurations = (
442 | CB52611F1F0AA79000C7BF65 /* Debug */,
443 | CB5261201F0AA79000C7BF65 /* Release */,
444 | );
445 | defaultConfigurationIsVisible = 0;
446 | defaultConfigurationName = Release;
447 | };
448 | CB5261211F0AA79000C7BF65 /* Build configuration list for PBXNativeTarget "GenericCellControllers" */ = {
449 | isa = XCConfigurationList;
450 | buildConfigurations = (
451 | CB5261221F0AA79000C7BF65 /* Debug */,
452 | CB5261231F0AA79000C7BF65 /* Release */,
453 | );
454 | defaultConfigurationIsVisible = 0;
455 | defaultConfigurationName = Release;
456 | };
457 | CB5261241F0AA79000C7BF65 /* Build configuration list for PBXNativeTarget "GenericCellControllersTests" */ = {
458 | isa = XCConfigurationList;
459 | buildConfigurations = (
460 | CB5261251F0AA79000C7BF65 /* Debug */,
461 | CB5261261F0AA79000C7BF65 /* Release */,
462 | );
463 | defaultConfigurationIsVisible = 0;
464 | defaultConfigurationName = Release;
465 | };
466 | /* End XCConfigurationList section */
467 | };
468 | rootObject = CB5261041F0AA78F00C7BF65 /* Project object */;
469 | }
470 |
--------------------------------------------------------------------------------
/GenericCellControllers/GenericCellControllers.xcodeproj/xcshareddata/xcschemes/GenericCellControllers.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 |
63 |
69 |
70 |
71 |
72 |
78 |
79 |
85 |
86 |
87 |
88 |
90 |
91 |
94 |
95 |
96 |
--------------------------------------------------------------------------------
/GenericCellControllers/Source/CellControllers/CellController.swift:
--------------------------------------------------------------------------------
1 | //
2 | // CellControllerType.swift
3 | // Busuu
4 | //
5 | // Created by Javier Valdera on 29/11/2016.
6 | // Copyright © 2016 busuu. All rights reserved.
7 | //
8 |
9 | import Foundation
10 |
11 | open class CellController {
12 |
13 | private weak var reusableCellHolder: T?
14 | public var indexPath: IndexPath?
15 |
16 | public init() {}
17 |
18 | open class var cellClass: AnyClass {
19 | fatalError("Must be overriden by children.")
20 | }
21 |
22 | public static var cellIdentifier: String {
23 | return String(describing: cellClass)
24 | }
25 |
26 | public static func registerCell(on reusableCellHolder: T) {
27 | let bundle = Bundle(for: cellClass)
28 | if bundle.path(forResource: cellIdentifier, ofType: "nib") != nil {
29 | let nib = UINib(nibName: cellIdentifier, bundle: bundle)
30 | reusableCellHolder.register(nib, forCellWithReuseIdentifier: cellIdentifier)
31 | } else {
32 | reusableCellHolder.register(cellClass, forCellWithReuseIdentifier: cellIdentifier)
33 | }
34 | }
35 |
36 | public final func cellFromReusableCellHolder(_ reusableCellHolder: T, forIndexPath indexPath: IndexPath) -> T.CellType {
37 | self.reusableCellHolder = reusableCellHolder
38 | self.indexPath = indexPath
39 |
40 | let cell = reusableCellHolder.dequeueReusableCell(withReuseIdentifier: type(of: self).cellIdentifier, for: indexPath)
41 | configureCell(cell)
42 |
43 | return cell
44 | }
45 |
46 | public final func innerCurrentCell() -> T.CellType? {
47 | guard let indexPath = indexPath else { return nil }
48 | return reusableCellHolder?.cellForItem(at: indexPath)
49 | }
50 |
51 | open func configureCell(_ cell: T.CellType) {
52 | // By default do nothing.
53 | }
54 |
55 | open func willDisplayCell(_ cell: T.CellType) {
56 | // By default do nothing.
57 | }
58 |
59 | open func didEndDisplayingCell(_ cell: T.CellType) {
60 | // By default do nothing.
61 | }
62 |
63 | open func didSelectCell() {
64 | // By default do nothing.
65 | }
66 |
67 | open func didDeselectCell() {
68 | // By default do nothing.
69 | }
70 |
71 | open func shouldHighlightCell() -> Bool {
72 | return true
73 | }
74 |
75 | open func didHightlightCell() {
76 | // By default do nothing.
77 | }
78 |
79 | open func didUnhightlightCell() {
80 | // By default do nothing.
81 | }
82 |
83 | open func cellSize(reusableCellHolder: T) -> CGSize {
84 | fatalError("Must be overriden by children.")
85 | }
86 |
87 | open func estimatedCellSize(reusableCellHolder: T) -> CGSize {
88 | fatalError("Must be overriden by children.")
89 | }
90 |
91 |
92 | }
93 |
--------------------------------------------------------------------------------
/GenericCellControllers/Source/CellControllers/GenericCellController.swift:
--------------------------------------------------------------------------------
1 | //
2 | // GenericCellController.swift
3 | // GenericCellControllers
4 | //
5 | // Created by Javier Valdera on 27/09/2017.
6 | // Copyright © 2017 Busuu Ltd. All rights reserved.
7 | //
8 |
9 | import Foundation
10 |
11 | public typealias TableCellController = CellController
12 | public typealias CollectionCellController = CellController
13 |
14 | open class GenericCellController : CellController {
15 |
16 | public final override class var cellClass: AnyClass {
17 | return T.self
18 | }
19 |
20 | public final override func configureCell(_ cell: T.CellHolder.CellType) {
21 | let cell = cell as! T
22 | configureCell(cell)
23 | }
24 |
25 | public final func currentCell() -> T? {
26 | return innerCurrentCell() as? T
27 | }
28 |
29 | public final override func willDisplayCell(_ cell: T.CellHolder.CellType) {
30 | let cell = cell as! T
31 | willDisplayCell(cell)
32 | }
33 |
34 | public final override func didEndDisplayingCell(_ cell: T.CellHolder.CellType) {
35 | let cell = cell as! T
36 | didEndDisplayingCell(cell)
37 | }
38 |
39 | open func configureCell(_ cell: T) {
40 | // By default do nothing.
41 | }
42 |
43 | open func willDisplayCell(_ cell: T) {
44 | // By default do nothing.
45 | }
46 |
47 | open func didEndDisplayingCell(_ cell: T) {
48 | // By default do nothing.
49 | }
50 |
51 | }
52 |
--------------------------------------------------------------------------------
/GenericCellControllers/Source/GenericCellControllers.h:
--------------------------------------------------------------------------------
1 | //
2 | // GenericCellControllers.h
3 | // GenericCellControllers
4 | //
5 | // Created by Javier Valdera on 03/07/2017.
6 | // Copyright © 2017 Busuu Ltd. All rights reserved.
7 | //
8 |
9 | #import
10 |
11 | //! Project version number for GenericCellControllers.
12 | FOUNDATION_EXPORT double GenericCellControllersVersionNumber;
13 |
14 | //! Project version string for GenericCellControllers.
15 | FOUNDATION_EXPORT const unsigned char GenericCellControllersVersionString[];
16 |
17 | // In this header, you should import all the public headers of your framework using statements like #import
18 |
19 |
20 |
--------------------------------------------------------------------------------
/GenericCellControllers/Source/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | en
7 | CFBundleExecutable
8 | $(EXECUTABLE_NAME)
9 | CFBundleIdentifier
10 | $(PRODUCT_BUNDLE_IDENTIFIER)
11 | CFBundleInfoDictionaryVersion
12 | 6.0
13 | CFBundleName
14 | $(PRODUCT_NAME)
15 | CFBundlePackageType
16 | FMWK
17 | CFBundleShortVersionString
18 | 1.0
19 | CFBundleVersion
20 | $(CURRENT_PROJECT_VERSION)
21 | NSPrincipalClass
22 |
23 |
24 |
25 |
--------------------------------------------------------------------------------
/GenericCellControllers/Source/ReusableCell/ReusableCellHolder.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ReusableCellHolder.swift
3 | // Busuu
4 | //
5 | // Created by Javier Valdera on 30/11/2016.
6 | // Copyright © 2016 busuu. All rights reserved.
7 | //
8 |
9 | import Foundation
10 |
11 | // MARK: - ReusableCell
12 | public protocol ReusableCell: class {
13 | associatedtype CellHolder: ReusableCellHolder
14 | }
15 |
16 | extension UITableViewCell: ReusableCell {
17 | public typealias CellHolder = UITableView
18 | }
19 |
20 | extension UICollectionViewCell: ReusableCell {
21 | public typealias CellHolder = UICollectionView
22 | }
23 |
24 | // MARK: - ReusableCellHolder
25 | public protocol ReusableCellHolder: class {
26 | associatedtype CellType: ReusableCell where CellType.CellHolder == Self
27 |
28 | func register(_ nib: UINib?, forCellWithReuseIdentifier identifier: String)
29 | func register(_ cellClass: AnyClass?, forCellWithReuseIdentifier identifier: String)
30 | func dequeueReusableCell(withReuseIdentifier identifier: String, for indexPath: IndexPath) -> CellType
31 | func cellForItem(at indexPath: IndexPath) -> CellType?
32 | }
33 |
34 | extension UITableView: ReusableCellHolder {
35 |
36 | @objc(registerNib:forCellWithReuseIdentifier:)
37 | public func register(_ nib: UINib?, forCellWithReuseIdentifier identifier: String) {
38 | register(nib, forCellReuseIdentifier: identifier)
39 | }
40 |
41 | @objc(registerClass:forCellWithReuseIdentifier:)
42 | public func register(_ cellClass: AnyClass?, forCellWithReuseIdentifier identifier: String) {
43 | register(cellClass, forCellReuseIdentifier: identifier)
44 | }
45 |
46 | public func dequeueReusableCell(withReuseIdentifier identifier: String, for indexPath: IndexPath) -> UITableViewCell {
47 | return dequeueReusableCell(withIdentifier: identifier, for: indexPath)
48 | }
49 |
50 | public func cellForItem(at indexPath: IndexPath) -> UITableViewCell? {
51 | return cellForRow(at: indexPath)
52 | }
53 |
54 | }
55 |
56 | extension UICollectionView: ReusableCellHolder {}
57 |
--------------------------------------------------------------------------------
/GenericCellControllers/Tests/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | en
7 | CFBundleExecutable
8 | $(EXECUTABLE_NAME)
9 | CFBundleIdentifier
10 | $(PRODUCT_BUNDLE_IDENTIFIER)
11 | CFBundleInfoDictionaryVersion
12 | 6.0
13 | CFBundleName
14 | $(PRODUCT_NAME)
15 | CFBundlePackageType
16 | BNDL
17 | CFBundleShortVersionString
18 | 1.0
19 | CFBundleVersion
20 | 1
21 |
22 |
23 |
--------------------------------------------------------------------------------
/GenericCellControllersExample/GenericCellControllersExample.xcodeproj/project.pbxproj:
--------------------------------------------------------------------------------
1 | // !$*UTF8*$!
2 | {
3 | archiveVersion = 1;
4 | classes = {
5 | };
6 | objectVersion = 48;
7 | objects = {
8 |
9 | /* Begin PBXBuildFile section */
10 | CB0A90BF1F7D1753004C652A /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = CB0A90BE1F7D1753004C652A /* AppDelegate.swift */; };
11 | CB0A90C61F7D1753004C652A /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = CB0A90C51F7D1753004C652A /* Assets.xcassets */; };
12 | CB0A90C91F7D1753004C652A /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = CB0A90C71F7D1753004C652A /* LaunchScreen.storyboard */; };
13 | CB0A90E11F7D17A9004C652A /* FeedTableViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = CB0A90DF1F7D17A9004C652A /* FeedTableViewController.swift */; };
14 | CB0A90E21F7D17A9004C652A /* FeedTableViewController.xib in Resources */ = {isa = PBXBuildFile; fileRef = CB0A90E01F7D17A9004C652A /* FeedTableViewController.xib */; };
15 | CB0A90EC1F7D1A14004C652A /* GenericCellControllers.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = CB0A90EB1F7D1A0A004C652A /* GenericCellControllers.framework */; };
16 | CB0A90ED1F7D1A14004C652A /* GenericCellControllers.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = CB0A90EB1F7D1A0A004C652A /* GenericCellControllers.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
17 | CB0A90F31F7D1A61004C652A /* FeedImageTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = CB0A90F11F7D1A61004C652A /* FeedImageTableViewCell.swift */; };
18 | CB0A90F41F7D1A61004C652A /* FeedImageTableViewCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = CB0A90F21F7D1A61004C652A /* FeedImageTableViewCell.xib */; };
19 | CB0A90F71F7D1A6D004C652A /* FeedTextTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = CB0A90F51F7D1A6D004C652A /* FeedTextTableViewCell.swift */; };
20 | CB0A90F81F7D1A6D004C652A /* FeedTextTableViewCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = CB0A90F61F7D1A6D004C652A /* FeedTextTableViewCell.xib */; };
21 | CB0A90FA1F7D1DF1004C652A /* FeedTextCellController.swift in Sources */ = {isa = PBXBuildFile; fileRef = CB0A90F91F7D1DF1004C652A /* FeedTextCellController.swift */; };
22 | CB0A90FC1F7D1E21004C652A /* FeedImageCellController.swift in Sources */ = {isa = PBXBuildFile; fileRef = CB0A90FB1F7D1E21004C652A /* FeedImageCellController.swift */; };
23 | CB0A90FE1F7D1E5A004C652A /* FeedCellControllerFactory.swift in Sources */ = {isa = PBXBuildFile; fileRef = CB0A90FD1F7D1E5A004C652A /* FeedCellControllerFactory.swift */; };
24 | CB0A91011F7D2049004C652A /* FeedModels.swift in Sources */ = {isa = PBXBuildFile; fileRef = CB0A91001F7D2049004C652A /* FeedModels.swift */; };
25 | CB0A91031F7D2CD2004C652A /* FeedDataProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = CB0A91021F7D2CD2004C652A /* FeedDataProvider.swift */; };
26 | CB0A91101F7D3176004C652A /* cat01.jpg in Resources */ = {isa = PBXBuildFile; fileRef = CB0A91081F7D3123004C652A /* cat01.jpg */; };
27 | CB0A91111F7D3176004C652A /* cat02.jpg in Resources */ = {isa = PBXBuildFile; fileRef = CB0A910F1F7D3125004C652A /* cat02.jpg */; };
28 | CB0A91121F7D3176004C652A /* cat03.jpg in Resources */ = {isa = PBXBuildFile; fileRef = CB0A91061F7D3122004C652A /* cat03.jpg */; };
29 | CB0A91131F7D3176004C652A /* cat04.jpg in Resources */ = {isa = PBXBuildFile; fileRef = CB0A91071F7D3123004C652A /* cat04.jpg */; };
30 | CB0A91141F7D3176004C652A /* cat05.jpg in Resources */ = {isa = PBXBuildFile; fileRef = CB0A910C1F7D3124004C652A /* cat05.jpg */; };
31 | CB0A91151F7D3176004C652A /* cat06.jpg in Resources */ = {isa = PBXBuildFile; fileRef = CB0A910E1F7D3125004C652A /* cat06.jpg */; };
32 | CB0A91161F7D3176004C652A /* cat07.jpg in Resources */ = {isa = PBXBuildFile; fileRef = CB0A910D1F7D3125004C652A /* cat07.jpg */; };
33 | CB0A911E1F7D3B2A004C652A /* ImageDetailViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = CB0A911C1F7D3B2A004C652A /* ImageDetailViewController.swift */; };
34 | CB0A911F1F7D3B2A004C652A /* ImageDetailViewController.xib in Resources */ = {isa = PBXBuildFile; fileRef = CB0A911D1F7D3B2A004C652A /* ImageDetailViewController.xib */; };
35 | CB0A91211F7D3D9C004C652A /* FeedCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = CB0A91201F7D3D9C004C652A /* FeedCoordinator.swift */; };
36 | CB0A91241F7D41A7004C652A /* TextDetailViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = CB0A91221F7D41A6004C652A /* TextDetailViewController.swift */; };
37 | CB0A91251F7D41A7004C652A /* TextDetailViewController.xib in Resources */ = {isa = PBXBuildFile; fileRef = CB0A91231F7D41A6004C652A /* TextDetailViewController.xib */; };
38 | /* End PBXBuildFile section */
39 |
40 | /* Begin PBXCopyFilesBuildPhase section */
41 | CB0A90E61F7D19E9004C652A /* Embed Frameworks */ = {
42 | isa = PBXCopyFilesBuildPhase;
43 | buildActionMask = 2147483647;
44 | dstPath = "";
45 | dstSubfolderSpec = 10;
46 | files = (
47 | CB0A90ED1F7D1A14004C652A /* GenericCellControllers.framework in Embed Frameworks */,
48 | );
49 | name = "Embed Frameworks";
50 | runOnlyForDeploymentPostprocessing = 0;
51 | };
52 | /* End PBXCopyFilesBuildPhase section */
53 |
54 | /* Begin PBXFileReference section */
55 | CB0A90BB1F7D1753004C652A /* GenericCellControllersExample.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = GenericCellControllersExample.app; sourceTree = BUILT_PRODUCTS_DIR; };
56 | CB0A90BE1F7D1753004C652A /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; };
57 | CB0A90C51F7D1753004C652A /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; };
58 | CB0A90C81F7D1753004C652A /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; };
59 | CB0A90CA1F7D1753004C652A /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; };
60 | CB0A90DF1F7D17A9004C652A /* FeedTableViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FeedTableViewController.swift; sourceTree = ""; };
61 | CB0A90E01F7D17A9004C652A /* FeedTableViewController.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = FeedTableViewController.xib; sourceTree = ""; };
62 | CB0A90EB1F7D1A0A004C652A /* GenericCellControllers.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = GenericCellControllers.framework; sourceTree = BUILT_PRODUCTS_DIR; };
63 | CB0A90F11F7D1A61004C652A /* FeedImageTableViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FeedImageTableViewCell.swift; sourceTree = ""; };
64 | CB0A90F21F7D1A61004C652A /* FeedImageTableViewCell.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = FeedImageTableViewCell.xib; sourceTree = ""; };
65 | CB0A90F51F7D1A6D004C652A /* FeedTextTableViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FeedTextTableViewCell.swift; sourceTree = ""; };
66 | CB0A90F61F7D1A6D004C652A /* FeedTextTableViewCell.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = FeedTextTableViewCell.xib; sourceTree = ""; };
67 | CB0A90F91F7D1DF1004C652A /* FeedTextCellController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FeedTextCellController.swift; sourceTree = ""; };
68 | CB0A90FB1F7D1E21004C652A /* FeedImageCellController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FeedImageCellController.swift; sourceTree = ""; };
69 | CB0A90FD1F7D1E5A004C652A /* FeedCellControllerFactory.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FeedCellControllerFactory.swift; sourceTree = ""; };
70 | CB0A91001F7D2049004C652A /* FeedModels.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FeedModels.swift; sourceTree = ""; };
71 | CB0A91021F7D2CD2004C652A /* FeedDataProvider.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FeedDataProvider.swift; sourceTree = ""; };
72 | CB0A91061F7D3122004C652A /* cat03.jpg */ = {isa = PBXFileReference; lastKnownFileType = image.jpeg; path = cat03.jpg; sourceTree = ""; };
73 | CB0A91071F7D3123004C652A /* cat04.jpg */ = {isa = PBXFileReference; lastKnownFileType = image.jpeg; path = cat04.jpg; sourceTree = ""; };
74 | CB0A91081F7D3123004C652A /* cat01.jpg */ = {isa = PBXFileReference; lastKnownFileType = image.jpeg; path = cat01.jpg; sourceTree = ""; };
75 | CB0A910C1F7D3124004C652A /* cat05.jpg */ = {isa = PBXFileReference; lastKnownFileType = image.jpeg; path = cat05.jpg; sourceTree = ""; };
76 | CB0A910D1F7D3125004C652A /* cat07.jpg */ = {isa = PBXFileReference; lastKnownFileType = image.jpeg; path = cat07.jpg; sourceTree = ""; };
77 | CB0A910E1F7D3125004C652A /* cat06.jpg */ = {isa = PBXFileReference; lastKnownFileType = image.jpeg; path = cat06.jpg; sourceTree = ""; };
78 | CB0A910F1F7D3125004C652A /* cat02.jpg */ = {isa = PBXFileReference; lastKnownFileType = image.jpeg; path = cat02.jpg; sourceTree = ""; };
79 | CB0A911C1F7D3B2A004C652A /* ImageDetailViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ImageDetailViewController.swift; sourceTree = ""; };
80 | CB0A911D1F7D3B2A004C652A /* ImageDetailViewController.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = ImageDetailViewController.xib; sourceTree = ""; };
81 | CB0A91201F7D3D9C004C652A /* FeedCoordinator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FeedCoordinator.swift; sourceTree = ""; };
82 | CB0A91221F7D41A6004C652A /* TextDetailViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TextDetailViewController.swift; sourceTree = ""; };
83 | CB0A91231F7D41A6004C652A /* TextDetailViewController.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = TextDetailViewController.xib; sourceTree = ""; };
84 | /* End PBXFileReference section */
85 |
86 | /* Begin PBXFrameworksBuildPhase section */
87 | CB0A90B81F7D1753004C652A /* Frameworks */ = {
88 | isa = PBXFrameworksBuildPhase;
89 | buildActionMask = 2147483647;
90 | files = (
91 | CB0A90EC1F7D1A14004C652A /* GenericCellControllers.framework in Frameworks */,
92 | );
93 | runOnlyForDeploymentPostprocessing = 0;
94 | };
95 | /* End PBXFrameworksBuildPhase section */
96 |
97 | /* Begin PBXGroup section */
98 | CB0A90B21F7D1753004C652A = {
99 | isa = PBXGroup;
100 | children = (
101 | CB0A90BD1F7D1753004C652A /* GenericCellControllersExample */,
102 | CB0A90BC1F7D1753004C652A /* Products */,
103 | CB0A90E91F7D1A0A004C652A /* Frameworks */,
104 | );
105 | sourceTree = "";
106 | };
107 | CB0A90BC1F7D1753004C652A /* Products */ = {
108 | isa = PBXGroup;
109 | children = (
110 | CB0A90BB1F7D1753004C652A /* GenericCellControllersExample.app */,
111 | );
112 | name = Products;
113 | sourceTree = "";
114 | };
115 | CB0A90BD1F7D1753004C652A /* GenericCellControllersExample */ = {
116 | isa = PBXGroup;
117 | children = (
118 | CB0A90DE1F7D177C004C652A /* Feed */,
119 | CB0A911B1F7D3B03004C652A /* TextDetail */,
120 | CB0A911A1F7D3AF7004C652A /* ImageDetail */,
121 | CB0A90BE1F7D1753004C652A /* AppDelegate.swift */,
122 | CB0A90C51F7D1753004C652A /* Assets.xcassets */,
123 | CB0A90C71F7D1753004C652A /* LaunchScreen.storyboard */,
124 | CB0A90CA1F7D1753004C652A /* Info.plist */,
125 | CB0A91051F7D310E004C652A /* Resources */,
126 | );
127 | path = GenericCellControllersExample;
128 | sourceTree = "";
129 | };
130 | CB0A90DE1F7D177C004C652A /* Feed */ = {
131 | isa = PBXGroup;
132 | children = (
133 | CB0A90FF1F7D203A004C652A /* Models */,
134 | CB0A90EE1F7D1A28004C652A /* Cells */,
135 | CB0A90DF1F7D17A9004C652A /* FeedTableViewController.swift */,
136 | CB0A90E01F7D17A9004C652A /* FeedTableViewController.xib */,
137 | CB0A91201F7D3D9C004C652A /* FeedCoordinator.swift */,
138 | );
139 | path = Feed;
140 | sourceTree = "";
141 | };
142 | CB0A90E91F7D1A0A004C652A /* Frameworks */ = {
143 | isa = PBXGroup;
144 | children = (
145 | CB0A90EB1F7D1A0A004C652A /* GenericCellControllers.framework */,
146 | );
147 | name = Frameworks;
148 | sourceTree = "";
149 | };
150 | CB0A90EE1F7D1A28004C652A /* Cells */ = {
151 | isa = PBXGroup;
152 | children = (
153 | CB0A90F01F7D1A3A004C652A /* Text */,
154 | CB0A90EF1F7D1A32004C652A /* Image */,
155 | CB0A90FD1F7D1E5A004C652A /* FeedCellControllerFactory.swift */,
156 | );
157 | path = Cells;
158 | sourceTree = "";
159 | };
160 | CB0A90EF1F7D1A32004C652A /* Image */ = {
161 | isa = PBXGroup;
162 | children = (
163 | CB0A90F11F7D1A61004C652A /* FeedImageTableViewCell.swift */,
164 | CB0A90F21F7D1A61004C652A /* FeedImageTableViewCell.xib */,
165 | CB0A90FB1F7D1E21004C652A /* FeedImageCellController.swift */,
166 | );
167 | path = Image;
168 | sourceTree = "";
169 | };
170 | CB0A90F01F7D1A3A004C652A /* Text */ = {
171 | isa = PBXGroup;
172 | children = (
173 | CB0A90F51F7D1A6D004C652A /* FeedTextTableViewCell.swift */,
174 | CB0A90F61F7D1A6D004C652A /* FeedTextTableViewCell.xib */,
175 | CB0A90F91F7D1DF1004C652A /* FeedTextCellController.swift */,
176 | );
177 | path = Text;
178 | sourceTree = "";
179 | };
180 | CB0A90FF1F7D203A004C652A /* Models */ = {
181 | isa = PBXGroup;
182 | children = (
183 | CB0A91001F7D2049004C652A /* FeedModels.swift */,
184 | CB0A91021F7D2CD2004C652A /* FeedDataProvider.swift */,
185 | );
186 | path = Models;
187 | sourceTree = "";
188 | };
189 | CB0A91051F7D310E004C652A /* Resources */ = {
190 | isa = PBXGroup;
191 | children = (
192 | CB0A91081F7D3123004C652A /* cat01.jpg */,
193 | CB0A910F1F7D3125004C652A /* cat02.jpg */,
194 | CB0A91061F7D3122004C652A /* cat03.jpg */,
195 | CB0A91071F7D3123004C652A /* cat04.jpg */,
196 | CB0A910C1F7D3124004C652A /* cat05.jpg */,
197 | CB0A910E1F7D3125004C652A /* cat06.jpg */,
198 | CB0A910D1F7D3125004C652A /* cat07.jpg */,
199 | );
200 | path = Resources;
201 | sourceTree = "";
202 | };
203 | CB0A911A1F7D3AF7004C652A /* ImageDetail */ = {
204 | isa = PBXGroup;
205 | children = (
206 | CB0A911C1F7D3B2A004C652A /* ImageDetailViewController.swift */,
207 | CB0A911D1F7D3B2A004C652A /* ImageDetailViewController.xib */,
208 | );
209 | path = ImageDetail;
210 | sourceTree = "";
211 | };
212 | CB0A911B1F7D3B03004C652A /* TextDetail */ = {
213 | isa = PBXGroup;
214 | children = (
215 | CB0A91221F7D41A6004C652A /* TextDetailViewController.swift */,
216 | CB0A91231F7D41A6004C652A /* TextDetailViewController.xib */,
217 | );
218 | path = TextDetail;
219 | sourceTree = "";
220 | };
221 | /* End PBXGroup section */
222 |
223 | /* Begin PBXNativeTarget section */
224 | CB0A90BA1F7D1753004C652A /* GenericCellControllersExample */ = {
225 | isa = PBXNativeTarget;
226 | buildConfigurationList = CB0A90D81F7D1753004C652A /* Build configuration list for PBXNativeTarget "GenericCellControllersExample" */;
227 | buildPhases = (
228 | CB0A90B71F7D1753004C652A /* Sources */,
229 | CB0A90B81F7D1753004C652A /* Frameworks */,
230 | CB0A90B91F7D1753004C652A /* Resources */,
231 | CB0A90E61F7D19E9004C652A /* Embed Frameworks */,
232 | );
233 | buildRules = (
234 | );
235 | dependencies = (
236 | );
237 | name = GenericCellControllersExample;
238 | productName = GenericCellControllersExample;
239 | productReference = CB0A90BB1F7D1753004C652A /* GenericCellControllersExample.app */;
240 | productType = "com.apple.product-type.application";
241 | };
242 | /* End PBXNativeTarget section */
243 |
244 | /* Begin PBXProject section */
245 | CB0A90B31F7D1753004C652A /* Project object */ = {
246 | isa = PBXProject;
247 | attributes = {
248 | LastSwiftUpdateCheck = 0900;
249 | LastUpgradeCheck = 1200;
250 | ORGANIZATIONNAME = "Busuu Ltd.";
251 | TargetAttributes = {
252 | CB0A90BA1F7D1753004C652A = {
253 | CreatedOnToolsVersion = 9.0;
254 | LastSwiftMigration = 1200;
255 | ProvisioningStyle = Automatic;
256 | };
257 | };
258 | };
259 | buildConfigurationList = CB0A90B61F7D1753004C652A /* Build configuration list for PBXProject "GenericCellControllersExample" */;
260 | compatibilityVersion = "Xcode 8.0";
261 | developmentRegion = en;
262 | hasScannedForEncodings = 0;
263 | knownRegions = (
264 | en,
265 | Base,
266 | );
267 | mainGroup = CB0A90B21F7D1753004C652A;
268 | productRefGroup = CB0A90BC1F7D1753004C652A /* Products */;
269 | projectDirPath = "";
270 | projectRoot = "";
271 | targets = (
272 | CB0A90BA1F7D1753004C652A /* GenericCellControllersExample */,
273 | );
274 | };
275 | /* End PBXProject section */
276 |
277 | /* Begin PBXResourcesBuildPhase section */
278 | CB0A90B91F7D1753004C652A /* Resources */ = {
279 | isa = PBXResourcesBuildPhase;
280 | buildActionMask = 2147483647;
281 | files = (
282 | CB0A90C91F7D1753004C652A /* LaunchScreen.storyboard in Resources */,
283 | CB0A911F1F7D3B2A004C652A /* ImageDetailViewController.xib in Resources */,
284 | CB0A91101F7D3176004C652A /* cat01.jpg in Resources */,
285 | CB0A90F41F7D1A61004C652A /* FeedImageTableViewCell.xib in Resources */,
286 | CB0A91121F7D3176004C652A /* cat03.jpg in Resources */,
287 | CB0A91111F7D3176004C652A /* cat02.jpg in Resources */,
288 | CB0A91141F7D3176004C652A /* cat05.jpg in Resources */,
289 | CB0A90C61F7D1753004C652A /* Assets.xcassets in Resources */,
290 | CB0A91161F7D3176004C652A /* cat07.jpg in Resources */,
291 | CB0A90E21F7D17A9004C652A /* FeedTableViewController.xib in Resources */,
292 | CB0A91251F7D41A7004C652A /* TextDetailViewController.xib in Resources */,
293 | CB0A91131F7D3176004C652A /* cat04.jpg in Resources */,
294 | CB0A91151F7D3176004C652A /* cat06.jpg in Resources */,
295 | CB0A90F81F7D1A6D004C652A /* FeedTextTableViewCell.xib in Resources */,
296 | );
297 | runOnlyForDeploymentPostprocessing = 0;
298 | };
299 | /* End PBXResourcesBuildPhase section */
300 |
301 | /* Begin PBXSourcesBuildPhase section */
302 | CB0A90B71F7D1753004C652A /* Sources */ = {
303 | isa = PBXSourcesBuildPhase;
304 | buildActionMask = 2147483647;
305 | files = (
306 | CB0A90F31F7D1A61004C652A /* FeedImageTableViewCell.swift in Sources */,
307 | CB0A91031F7D2CD2004C652A /* FeedDataProvider.swift in Sources */,
308 | CB0A90BF1F7D1753004C652A /* AppDelegate.swift in Sources */,
309 | CB0A90F71F7D1A6D004C652A /* FeedTextTableViewCell.swift in Sources */,
310 | CB0A91241F7D41A7004C652A /* TextDetailViewController.swift in Sources */,
311 | CB0A91011F7D2049004C652A /* FeedModels.swift in Sources */,
312 | CB0A90E11F7D17A9004C652A /* FeedTableViewController.swift in Sources */,
313 | CB0A90FA1F7D1DF1004C652A /* FeedTextCellController.swift in Sources */,
314 | CB0A91211F7D3D9C004C652A /* FeedCoordinator.swift in Sources */,
315 | CB0A90FE1F7D1E5A004C652A /* FeedCellControllerFactory.swift in Sources */,
316 | CB0A90FC1F7D1E21004C652A /* FeedImageCellController.swift in Sources */,
317 | CB0A911E1F7D3B2A004C652A /* ImageDetailViewController.swift in Sources */,
318 | );
319 | runOnlyForDeploymentPostprocessing = 0;
320 | };
321 | /* End PBXSourcesBuildPhase section */
322 |
323 | /* Begin PBXVariantGroup section */
324 | CB0A90C71F7D1753004C652A /* LaunchScreen.storyboard */ = {
325 | isa = PBXVariantGroup;
326 | children = (
327 | CB0A90C81F7D1753004C652A /* Base */,
328 | );
329 | name = LaunchScreen.storyboard;
330 | sourceTree = "";
331 | };
332 | /* End PBXVariantGroup section */
333 |
334 | /* Begin XCBuildConfiguration section */
335 | CB0A90D61F7D1753004C652A /* Debug */ = {
336 | isa = XCBuildConfiguration;
337 | buildSettings = {
338 | ALWAYS_SEARCH_USER_PATHS = NO;
339 | CLANG_ANALYZER_NONNULL = YES;
340 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
341 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
342 | CLANG_CXX_LIBRARY = "libc++";
343 | CLANG_ENABLE_MODULES = YES;
344 | CLANG_ENABLE_OBJC_ARC = YES;
345 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
346 | CLANG_WARN_BOOL_CONVERSION = YES;
347 | CLANG_WARN_COMMA = YES;
348 | CLANG_WARN_CONSTANT_CONVERSION = YES;
349 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
350 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
351 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
352 | CLANG_WARN_EMPTY_BODY = YES;
353 | CLANG_WARN_ENUM_CONVERSION = YES;
354 | CLANG_WARN_INFINITE_RECURSION = YES;
355 | CLANG_WARN_INT_CONVERSION = YES;
356 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
357 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
358 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
359 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
360 | CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
361 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
362 | CLANG_WARN_STRICT_PROTOTYPES = YES;
363 | CLANG_WARN_SUSPICIOUS_MOVE = YES;
364 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
365 | CLANG_WARN_UNREACHABLE_CODE = YES;
366 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
367 | CODE_SIGN_IDENTITY = "iPhone Developer";
368 | COPY_PHASE_STRIP = NO;
369 | DEBUG_INFORMATION_FORMAT = dwarf;
370 | ENABLE_STRICT_OBJC_MSGSEND = YES;
371 | ENABLE_TESTABILITY = YES;
372 | GCC_C_LANGUAGE_STANDARD = gnu11;
373 | GCC_DYNAMIC_NO_PIC = NO;
374 | GCC_NO_COMMON_BLOCKS = YES;
375 | GCC_OPTIMIZATION_LEVEL = 0;
376 | GCC_PREPROCESSOR_DEFINITIONS = (
377 | "DEBUG=1",
378 | "$(inherited)",
379 | );
380 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
381 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
382 | GCC_WARN_UNDECLARED_SELECTOR = YES;
383 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
384 | GCC_WARN_UNUSED_FUNCTION = YES;
385 | GCC_WARN_UNUSED_VARIABLE = YES;
386 | IPHONEOS_DEPLOYMENT_TARGET = 12.0;
387 | MTL_ENABLE_DEBUG_INFO = YES;
388 | ONLY_ACTIVE_ARCH = YES;
389 | SDKROOT = iphoneos;
390 | SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG;
391 | SWIFT_OPTIMIZATION_LEVEL = "-Onone";
392 | };
393 | name = Debug;
394 | };
395 | CB0A90D71F7D1753004C652A /* Release */ = {
396 | isa = XCBuildConfiguration;
397 | buildSettings = {
398 | ALWAYS_SEARCH_USER_PATHS = NO;
399 | CLANG_ANALYZER_NONNULL = YES;
400 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
401 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
402 | CLANG_CXX_LIBRARY = "libc++";
403 | CLANG_ENABLE_MODULES = YES;
404 | CLANG_ENABLE_OBJC_ARC = YES;
405 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
406 | CLANG_WARN_BOOL_CONVERSION = YES;
407 | CLANG_WARN_COMMA = YES;
408 | CLANG_WARN_CONSTANT_CONVERSION = YES;
409 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
410 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
411 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
412 | CLANG_WARN_EMPTY_BODY = YES;
413 | CLANG_WARN_ENUM_CONVERSION = YES;
414 | CLANG_WARN_INFINITE_RECURSION = YES;
415 | CLANG_WARN_INT_CONVERSION = YES;
416 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
417 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
418 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
419 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
420 | CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
421 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
422 | CLANG_WARN_STRICT_PROTOTYPES = YES;
423 | CLANG_WARN_SUSPICIOUS_MOVE = YES;
424 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
425 | CLANG_WARN_UNREACHABLE_CODE = YES;
426 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
427 | CODE_SIGN_IDENTITY = "iPhone Developer";
428 | COPY_PHASE_STRIP = NO;
429 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
430 | ENABLE_NS_ASSERTIONS = NO;
431 | ENABLE_STRICT_OBJC_MSGSEND = YES;
432 | GCC_C_LANGUAGE_STANDARD = gnu11;
433 | GCC_NO_COMMON_BLOCKS = YES;
434 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
435 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
436 | GCC_WARN_UNDECLARED_SELECTOR = YES;
437 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
438 | GCC_WARN_UNUSED_FUNCTION = YES;
439 | GCC_WARN_UNUSED_VARIABLE = YES;
440 | IPHONEOS_DEPLOYMENT_TARGET = 12.0;
441 | MTL_ENABLE_DEBUG_INFO = NO;
442 | SDKROOT = iphoneos;
443 | SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule";
444 | VALIDATE_PRODUCT = YES;
445 | };
446 | name = Release;
447 | };
448 | CB0A90D91F7D1753004C652A /* Debug */ = {
449 | isa = XCBuildConfiguration;
450 | buildSettings = {
451 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
452 | CODE_SIGN_STYLE = Automatic;
453 | DEVELOPMENT_TEAM = 9X4849482L;
454 | INFOPLIST_FILE = GenericCellControllersExample/Info.plist;
455 | IPHONEOS_DEPLOYMENT_TARGET = 12.0;
456 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
457 | PRODUCT_BUNDLE_IDENTIFIER = com.busuu.GenericCellControllersExample;
458 | PRODUCT_NAME = "$(TARGET_NAME)";
459 | SWIFT_VERSION = 5.0;
460 | TARGETED_DEVICE_FAMILY = 1;
461 | };
462 | name = Debug;
463 | };
464 | CB0A90DA1F7D1753004C652A /* Release */ = {
465 | isa = XCBuildConfiguration;
466 | buildSettings = {
467 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
468 | CODE_SIGN_STYLE = Automatic;
469 | DEVELOPMENT_TEAM = 9X4849482L;
470 | INFOPLIST_FILE = GenericCellControllersExample/Info.plist;
471 | IPHONEOS_DEPLOYMENT_TARGET = 12.0;
472 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
473 | PRODUCT_BUNDLE_IDENTIFIER = com.busuu.GenericCellControllersExample;
474 | PRODUCT_NAME = "$(TARGET_NAME)";
475 | SWIFT_VERSION = 5.0;
476 | TARGETED_DEVICE_FAMILY = 1;
477 | };
478 | name = Release;
479 | };
480 | /* End XCBuildConfiguration section */
481 |
482 | /* Begin XCConfigurationList section */
483 | CB0A90B61F7D1753004C652A /* Build configuration list for PBXProject "GenericCellControllersExample" */ = {
484 | isa = XCConfigurationList;
485 | buildConfigurations = (
486 | CB0A90D61F7D1753004C652A /* Debug */,
487 | CB0A90D71F7D1753004C652A /* Release */,
488 | );
489 | defaultConfigurationIsVisible = 0;
490 | defaultConfigurationName = Release;
491 | };
492 | CB0A90D81F7D1753004C652A /* Build configuration list for PBXNativeTarget "GenericCellControllersExample" */ = {
493 | isa = XCConfigurationList;
494 | buildConfigurations = (
495 | CB0A90D91F7D1753004C652A /* Debug */,
496 | CB0A90DA1F7D1753004C652A /* Release */,
497 | );
498 | defaultConfigurationIsVisible = 0;
499 | defaultConfigurationName = Release;
500 | };
501 | /* End XCConfigurationList section */
502 | };
503 | rootObject = CB0A90B31F7D1753004C652A /* Project object */;
504 | }
505 |
--------------------------------------------------------------------------------
/GenericCellControllersExample/GenericCellControllersExample/AppDelegate.swift:
--------------------------------------------------------------------------------
1 | //
2 | // AppDelegate.swift
3 | // GenericCellControllersExample
4 | //
5 | // Created by Javier Valdera on 28/09/2017.
6 | // Copyright © 2017 Busuu Ltd. All rights reserved.
7 | //
8 |
9 | import UIKit
10 |
11 | @UIApplicationMain
12 | class AppDelegate: UIResponder, UIApplicationDelegate {
13 |
14 | var window: UIWindow?
15 |
16 | func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
17 | // Override point for customization after application launch.
18 | self.window = UIWindow(frame: UIScreen.main.bounds)
19 | let viewController = FeedTableViewController()
20 | let navigationController = UINavigationController(rootViewController: viewController)
21 | self.window?.rootViewController = navigationController
22 | self.window?.makeKeyAndVisible()
23 | return true
24 | }
25 |
26 | }
27 |
28 |
--------------------------------------------------------------------------------
/GenericCellControllersExample/GenericCellControllersExample/Assets.xcassets/AppIcon.appiconset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "iphone",
5 | "size" : "20x20",
6 | "scale" : "2x"
7 | },
8 | {
9 | "idiom" : "iphone",
10 | "size" : "20x20",
11 | "scale" : "3x"
12 | },
13 | {
14 | "idiom" : "iphone",
15 | "size" : "29x29",
16 | "scale" : "2x"
17 | },
18 | {
19 | "idiom" : "iphone",
20 | "size" : "29x29",
21 | "scale" : "3x"
22 | },
23 | {
24 | "idiom" : "iphone",
25 | "size" : "40x40",
26 | "scale" : "2x"
27 | },
28 | {
29 | "idiom" : "iphone",
30 | "size" : "40x40",
31 | "scale" : "3x"
32 | },
33 | {
34 | "idiom" : "iphone",
35 | "size" : "60x60",
36 | "scale" : "2x"
37 | },
38 | {
39 | "idiom" : "iphone",
40 | "size" : "60x60",
41 | "scale" : "3x"
42 | },
43 | {
44 | "idiom" : "ipad",
45 | "size" : "20x20",
46 | "scale" : "1x"
47 | },
48 | {
49 | "idiom" : "ipad",
50 | "size" : "20x20",
51 | "scale" : "2x"
52 | },
53 | {
54 | "idiom" : "ipad",
55 | "size" : "29x29",
56 | "scale" : "1x"
57 | },
58 | {
59 | "idiom" : "ipad",
60 | "size" : "29x29",
61 | "scale" : "2x"
62 | },
63 | {
64 | "idiom" : "ipad",
65 | "size" : "40x40",
66 | "scale" : "1x"
67 | },
68 | {
69 | "idiom" : "ipad",
70 | "size" : "40x40",
71 | "scale" : "2x"
72 | },
73 | {
74 | "idiom" : "ipad",
75 | "size" : "76x76",
76 | "scale" : "1x"
77 | },
78 | {
79 | "idiom" : "ipad",
80 | "size" : "76x76",
81 | "scale" : "2x"
82 | },
83 | {
84 | "idiom" : "ipad",
85 | "size" : "83.5x83.5",
86 | "scale" : "2x"
87 | }
88 | ],
89 | "info" : {
90 | "version" : 1,
91 | "author" : "xcode"
92 | }
93 | }
--------------------------------------------------------------------------------
/GenericCellControllersExample/GenericCellControllersExample/Base.lproj/LaunchScreen.storyboard:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
--------------------------------------------------------------------------------
/GenericCellControllersExample/GenericCellControllersExample/Feed/Cells/FeedCellControllerFactory.swift:
--------------------------------------------------------------------------------
1 | //
2 | // FeedCellControllerFactory.swift
3 | // GenericCellControllersExample
4 | //
5 | // Created by Javier Valdera on 28/09/2017.
6 | // Copyright © 2017 Busuu Ltd. All rights reserved.
7 | //
8 |
9 | import Foundation
10 | import GenericCellControllers
11 |
12 | class FeedCellControllerFactory {
13 |
14 | func registerCells(on tableView: UITableView) {
15 | // The factory is the only one who knows the exact Cell Controllers we are using, hence,
16 | // it is responsible for registering them in the UITableView/UICollectionView
17 |
18 | FeedImageCellController.registerCell(on: tableView)
19 | FeedTextCellController.registerCell(on: tableView)
20 | }
21 |
22 | func cellControllers(from elements: [FeedElement], coordinator: FeedCoordinator) -> [TableCellController] {
23 | // Matching each type of data to the right Cell Controller is now something we only do once. Here.
24 |
25 | return elements.map { (element) in
26 | switch element {
27 | case .picture(let picture):
28 | return FeedImageCellController(picture: picture, coordinator: coordinator)
29 | case .post(let post):
30 | return FeedTextCellController(post: post, coordinator: coordinator)
31 | }
32 | }
33 | }
34 |
35 | }
36 |
--------------------------------------------------------------------------------
/GenericCellControllersExample/GenericCellControllersExample/Feed/Cells/Image/FeedImageCellController.swift:
--------------------------------------------------------------------------------
1 | //
2 | // FeedImageCellController.swift
3 | // GenericCellControllersExample
4 | //
5 | // Created by Javier Valdera on 28/09/2017.
6 | // Copyright © 2017 Busuu Ltd. All rights reserved.
7 | //
8 |
9 | import Foundation
10 | import GenericCellControllers
11 |
12 | class FeedImageCellController: GenericCellController {
13 |
14 | private let picture: Picture
15 | private let coordinator: FeedCoordinator
16 |
17 | init(picture: Picture,
18 | coordinator: FeedCoordinator) {
19 | self.picture = picture
20 | self.coordinator = coordinator
21 | }
22 |
23 | override func configureCell(_ cell: FeedImageTableViewCell) {
24 | // Easy configuration since the Cell Controller receive the right type of cell and
25 | // it is already holding the data this cell needs to display.
26 |
27 | cell.authorLabel.text = "By: \(picture.author)"
28 | cell.pictureImageView.image = UIImage(named: picture.imageName)
29 | }
30 |
31 | override func didSelectCell() {
32 | // We can call the right method in the coordinator since the Cell Controller only deals
33 | // with one type of data.
34 |
35 | coordinator.pictureSelected(picture)
36 | }
37 |
38 | }
39 |
--------------------------------------------------------------------------------
/GenericCellControllersExample/GenericCellControllersExample/Feed/Cells/Image/FeedImageTableViewCell.swift:
--------------------------------------------------------------------------------
1 | //
2 | // FeedImageTableViewCell.swift
3 | // GenericCellControllersExample
4 | //
5 | // Created by Javier Valdera on 28/09/2017.
6 | // Copyright © 2017 Busuu Ltd. All rights reserved.
7 | //
8 |
9 | import UIKit
10 |
11 | class FeedImageTableViewCell: UITableViewCell {
12 |
13 | @IBOutlet weak var authorLabel: UILabel!
14 | @IBOutlet weak var pictureImageView: UIImageView!
15 |
16 | }
17 |
--------------------------------------------------------------------------------
/GenericCellControllersExample/GenericCellControllersExample/Feed/Cells/Image/FeedImageTableViewCell.xib:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
--------------------------------------------------------------------------------
/GenericCellControllersExample/GenericCellControllersExample/Feed/Cells/Text/FeedTextCellController.swift:
--------------------------------------------------------------------------------
1 | //
2 | // FeedTextCellController.swift
3 | // GenericCellControllersExample
4 | //
5 | // Created by Javier Valdera on 28/09/2017.
6 | // Copyright © 2017 Busuu Ltd. All rights reserved.
7 | //
8 |
9 | import Foundation
10 | import GenericCellControllers
11 |
12 | class FeedTextCellController: GenericCellController {
13 |
14 | private let post: Post
15 | private let coordinator: FeedCoordinator
16 |
17 | init(post: Post,
18 | coordinator: FeedCoordinator) {
19 | self.post = post
20 | self.coordinator = coordinator
21 | }
22 |
23 | override func configureCell(_ cell: FeedTextTableViewCell) {
24 | // Easy configuration since the Cell Controller receive the right type of cell and
25 | // it is already holding the data this cell needs to display.
26 |
27 | cell.authorLabel.text = "By: \(post.author)"
28 | cell.contentLabel?.text = post.text
29 | }
30 |
31 | override func didSelectCell() {
32 | // We can call the right method in the coordinator since the Cell Controller only deals
33 | // with one type of data.
34 |
35 | coordinator.postSelected(post)
36 | }
37 |
38 | }
39 |
--------------------------------------------------------------------------------
/GenericCellControllersExample/GenericCellControllersExample/Feed/Cells/Text/FeedTextTableViewCell.swift:
--------------------------------------------------------------------------------
1 | //
2 | // FeedTextTableViewCell.swift
3 | // GenericCellControllersExample
4 | //
5 | // Created by Javier Valdera on 28/09/2017.
6 | // Copyright © 2017 Busuu Ltd. All rights reserved.
7 | //
8 |
9 | import UIKit
10 |
11 | class FeedTextTableViewCell: UITableViewCell {
12 |
13 | @IBOutlet weak var authorLabel: UILabel!
14 | @IBOutlet weak var contentLabel: UILabel!
15 |
16 | }
17 |
--------------------------------------------------------------------------------
/GenericCellControllersExample/GenericCellControllersExample/Feed/Cells/Text/FeedTextTableViewCell.xib:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
29 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
--------------------------------------------------------------------------------
/GenericCellControllersExample/GenericCellControllersExample/Feed/FeedCoordinator.swift:
--------------------------------------------------------------------------------
1 | //
2 | // FeedCoordinator.swift
3 | // GenericCellControllersExample
4 | //
5 | // Created by Javier Valdera on 28/09/2017.
6 | // Copyright © 2017 Busuu Ltd. All rights reserved.
7 | //
8 |
9 | import Foundation
10 |
11 | class FeedCoordinator {
12 |
13 | private weak var viewController: FeedTableViewController?
14 |
15 | init(viewController: FeedTableViewController?) {
16 | self.viewController = viewController
17 | }
18 |
19 | func pictureSelected(_ picture: Picture) {
20 |
21 | let imageViewController = ImageDetailViewController(picture: picture)
22 | viewController?.navigationController?.pushViewController(imageViewController, animated: true)
23 | }
24 |
25 | func postSelected(_ post: Post) {
26 |
27 | let textViewController = TextDetailViewController(post: post)
28 | viewController?.navigationController?.pushViewController(textViewController, animated: true)
29 | }
30 |
31 | }
32 |
--------------------------------------------------------------------------------
/GenericCellControllersExample/GenericCellControllersExample/Feed/FeedTableViewController.swift:
--------------------------------------------------------------------------------
1 | //
2 | // FeedTableViewController.swift
3 | // GenericCellControllersExample
4 | //
5 | // Created by Javier Valdera on 28/09/2017.
6 | // Copyright © 2017 Busuu Ltd. All rights reserved.
7 | //
8 |
9 | import UIKit
10 | import GenericCellControllers
11 |
12 | class FeedTableViewController: UITableViewController {
13 |
14 | private let cellControllerFactory = FeedCellControllerFactory()
15 | private var cellControllers: [TableCellController] = []
16 |
17 | override func viewDidLoad() {
18 | super.viewDidLoad()
19 |
20 | title = "Cute Cats Feed"
21 |
22 | configureTableView()
23 | displayData()
24 | }
25 |
26 | // MARK: - Table view data source
27 | override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
28 | return cellControllers.count
29 | }
30 |
31 | override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
32 | // Delegate on your Cell Controllers the dequeuing of the cells.
33 | return cellControllers[indexPath.row].cellFromReusableCellHolder(tableView, forIndexPath: indexPath)
34 | }
35 |
36 | // MARK: - Table view delegate
37 | override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
38 | // Pass the event to the Cell Controller. It is responsible for knowing what to do.
39 | cellControllers[indexPath.row].didSelectCell()
40 | }
41 |
42 | }
43 |
44 | private extension FeedTableViewController {
45 |
46 | func configureTableView() {
47 |
48 | tableView.rowHeight = UITableView.automaticDimension
49 | tableView.estimatedRowHeight = 200
50 |
51 | // Since the Cell Controller Factory is the only one that knows the actual cell controllers
52 | // we are using, we delegate on it for registering the cells in the UITableView/UICollectionView
53 | cellControllerFactory.registerCells(on: tableView)
54 | }
55 |
56 | func displayData() {
57 |
58 | // Get your data from your source (network, database...)
59 | let data = FeedDataProvider.harcodedData
60 |
61 | // Use the Cell Controller Factory to create your cell controllers from your data,
62 | // passing any external colaborators the cell controllers might need.
63 | cellControllers = cellControllerFactory.cellControllers(from: data, coordinator: FeedCoordinator(viewController: self))
64 |
65 | // Reload your UITableView/UICollectionView
66 | tableView.reloadData()
67 | }
68 |
69 | }
70 |
--------------------------------------------------------------------------------
/GenericCellControllersExample/GenericCellControllersExample/Feed/FeedTableViewController.xib:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
--------------------------------------------------------------------------------
/GenericCellControllersExample/GenericCellControllersExample/Feed/Models/FeedDataProvider.swift:
--------------------------------------------------------------------------------
1 | //
2 | // FeedDataProvider.swift
3 | // GenericCellControllersExample
4 | //
5 | // Created by Javier Valdera on 28/09/2017.
6 | // Copyright © 2017 Busuu Ltd. All rights reserved.
7 | //
8 |
9 | import Foundation
10 |
11 | struct FeedDataProvider {
12 |
13 | static let harcodedData: [FeedElement] = [
14 | .picture(Picture(author: "Javier Valdera", imageName: "cat01.jpg")),
15 | .post(Post(author: "H.P. Lovecraft", text: "In its flawless grace and superior self-sufficiency I have seen a symbol of the perfect beauty and bland impersonality of the universe itself, objectively considered, and in its air of silent mystery there resides for me all the wonder and fascination of the unknown.")),
16 | .picture(Picture(author: "Celia Pitarch", imageName: "cat02.jpg")),
17 | .picture(Picture(author: "James Wilkinson", imageName: "cat03.jpg")),
18 | .post(Post(author: "Ernest Hemingway", text: "A cat has absolute emotional honesty: human beings, for one reason or another, may hide their feelings, but a cat does not.")),
19 | .picture(Picture(author: "Juan González", imageName: "cat04.jpg")),
20 | .post(Post(author: "Albert Einstein", text: "You see, wire telegraph is a kind of a very, very long cat. You pull his tail in New York and his head is meowing in Los Angeles. Do you understand this? And radio operates exactly the same way: you send signals here, they receive them there. The only difference is that there is no cat.")),
21 | .post(Post(author: "Walter Lionel George", text: "Cats know how to obtain food without labor, shelter without confinement, and love without penalties.")),
22 | .picture(Picture(author: "Catwoman 😼", imageName: "cat05.jpg")),
23 | .post(Post(author: "Mark Twain", text: "Of all God’s creatures, there is only one that cannot be made slave of the lash. That one is the cat. If man could be crossed with the cat it would improve the man, but it would deteriorate the cat.")),
24 | .picture(Picture(author: "Crazy Cat Lady", imageName: "cat06.jpg")),
25 | .post(Post(author: "Nafisa Joseph", text: "I used to love dogs until I discovered cats.")),
26 | .picture(Picture(author: "Cat Lover 😻", imageName: "cat07.jpg")),
27 | ]
28 |
29 | }
30 |
--------------------------------------------------------------------------------
/GenericCellControllersExample/GenericCellControllersExample/Feed/Models/FeedModels.swift:
--------------------------------------------------------------------------------
1 | //
2 | // FeedModels.swift
3 | // GenericCellControllersExample
4 | //
5 | // Created by Javier Valdera on 28/09/2017.
6 | // Copyright © 2017 Busuu Ltd. All rights reserved.
7 | //
8 |
9 | import Foundation
10 |
11 | enum FeedElement {
12 | case picture(Picture)
13 | case post(Post)
14 | }
15 |
16 | struct Picture {
17 | let author: String
18 | let imageName: String
19 | }
20 |
21 | struct Post {
22 | let author: String
23 | let text: String
24 | }
25 |
--------------------------------------------------------------------------------
/GenericCellControllersExample/GenericCellControllersExample/ImageDetail/ImageDetailViewController.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ImageDetailViewController.swift
3 | // GenericCellControllersExample
4 | //
5 | // Created by Javier Valdera on 28/09/2017.
6 | // Copyright © 2017 Busuu Ltd. All rights reserved.
7 | //
8 |
9 | import UIKit
10 |
11 | class ImageDetailViewController: UIViewController {
12 |
13 | @IBOutlet weak var scrollView: UIScrollView!
14 | @IBOutlet weak var pictureImageView: UIImageView!
15 | @IBOutlet weak var imageViewTrailingConstraint: NSLayoutConstraint!
16 | @IBOutlet weak var imageViewTopConstraint: NSLayoutConstraint!
17 | @IBOutlet weak var imageViewLeadingConstraint: NSLayoutConstraint!
18 | @IBOutlet weak var imageViewBottomConstraint: NSLayoutConstraint!
19 |
20 | private let picture: Picture
21 |
22 | init(picture: Picture) {
23 | self.picture = picture
24 |
25 | super.init(nibName: String(describing: type(of: self)), bundle: Bundle(for: type(of: self)))
26 | }
27 |
28 | required init?(coder aDecoder: NSCoder) {
29 | fatalError("init(coder:) has not been implemented")
30 | }
31 |
32 | override func viewDidLoad() {
33 | super.viewDidLoad()
34 |
35 | title = "\(picture.author)'s Picture"
36 |
37 | pictureImageView.image = UIImage(named: picture.imageName)
38 | }
39 |
40 | override func viewWillAppear(_ animated: Bool) {
41 | super.viewWillAppear(animated)
42 |
43 | updateMinZoomScaleForSize(scrollView.frame.size)
44 | }
45 |
46 | override func updateViewConstraints() {
47 | updateConstraintsForSize(scrollView.frame.size)
48 |
49 | super.updateViewConstraints()
50 | }
51 |
52 | override func viewDidLayoutSubviews() {
53 | super.viewDidLayoutSubviews()
54 | updateMinZoomScaleForSize(scrollView.frame.size)
55 | }
56 |
57 | }
58 |
59 | extension ImageDetailViewController: UIScrollViewDelegate {
60 |
61 | func viewForZooming(in scrollView: UIScrollView) -> UIView? {
62 | return pictureImageView
63 | }
64 |
65 | func scrollViewDidZoom(_ scrollView: UIScrollView) {
66 | updateConstraintsForSize(scrollView.frame.size)
67 | }
68 |
69 | }
70 |
71 | private extension ImageDetailViewController {
72 |
73 | func updateMinZoomScaleForSize(_ size: CGSize) {
74 | guard let image = pictureImageView.image else { return }
75 |
76 | let widthScale = size.width / image.size.width
77 | let heightScale = size.height / image.size.height
78 | let minScale = min(widthScale, heightScale)
79 |
80 | scrollView.minimumZoomScale = minScale
81 | scrollView.zoomScale = minScale
82 |
83 | view.setNeedsUpdateConstraints()
84 | view.updateConstraintsIfNeeded()
85 | }
86 |
87 | func updateConstraintsForSize(_ size: CGSize) {
88 |
89 | let yOffset = max(0, (size.height - pictureImageView.frame.height) / 2)
90 | imageViewTopConstraint.constant = yOffset
91 | imageViewBottomConstraint.constant = yOffset
92 |
93 | let xOffset = max(0, (size.width - pictureImageView.frame.width) / 2)
94 | imageViewLeadingConstraint.constant = xOffset
95 | imageViewTrailingConstraint.constant = xOffset
96 |
97 | view.layoutIfNeeded()
98 | }
99 |
100 | }
101 |
--------------------------------------------------------------------------------
/GenericCellControllersExample/GenericCellControllersExample/ImageDetail/ImageDetailViewController.xib:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
--------------------------------------------------------------------------------
/GenericCellControllersExample/GenericCellControllersExample/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 | APPL
17 | CFBundleShortVersionString
18 | 1.0
19 | CFBundleVersion
20 | 1
21 | LSRequiresIPhoneOS
22 |
23 | UILaunchStoryboardName
24 | LaunchScreen
25 | UIRequiredDeviceCapabilities
26 |
27 | armv7
28 |
29 | UISupportedInterfaceOrientations
30 |
31 | UIInterfaceOrientationPortrait
32 | UIInterfaceOrientationLandscapeLeft
33 | UIInterfaceOrientationLandscapeRight
34 |
35 | UISupportedInterfaceOrientations~ipad
36 |
37 | UIInterfaceOrientationPortrait
38 | UIInterfaceOrientationPortraitUpsideDown
39 | UIInterfaceOrientationLandscapeLeft
40 | UIInterfaceOrientationLandscapeRight
41 |
42 |
43 |
44 |
--------------------------------------------------------------------------------
/GenericCellControllersExample/GenericCellControllersExample/Resources/cat01.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Busuu/generic-cell-controllers/5aeef534d3bca8c567c9dead358fb1a5e98adb89/GenericCellControllersExample/GenericCellControllersExample/Resources/cat01.jpg
--------------------------------------------------------------------------------
/GenericCellControllersExample/GenericCellControllersExample/Resources/cat02.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Busuu/generic-cell-controllers/5aeef534d3bca8c567c9dead358fb1a5e98adb89/GenericCellControllersExample/GenericCellControllersExample/Resources/cat02.jpg
--------------------------------------------------------------------------------
/GenericCellControllersExample/GenericCellControllersExample/Resources/cat03.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Busuu/generic-cell-controllers/5aeef534d3bca8c567c9dead358fb1a5e98adb89/GenericCellControllersExample/GenericCellControllersExample/Resources/cat03.jpg
--------------------------------------------------------------------------------
/GenericCellControllersExample/GenericCellControllersExample/Resources/cat04.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Busuu/generic-cell-controllers/5aeef534d3bca8c567c9dead358fb1a5e98adb89/GenericCellControllersExample/GenericCellControllersExample/Resources/cat04.jpg
--------------------------------------------------------------------------------
/GenericCellControllersExample/GenericCellControllersExample/Resources/cat05.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Busuu/generic-cell-controllers/5aeef534d3bca8c567c9dead358fb1a5e98adb89/GenericCellControllersExample/GenericCellControllersExample/Resources/cat05.jpg
--------------------------------------------------------------------------------
/GenericCellControllersExample/GenericCellControllersExample/Resources/cat06.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Busuu/generic-cell-controllers/5aeef534d3bca8c567c9dead358fb1a5e98adb89/GenericCellControllersExample/GenericCellControllersExample/Resources/cat06.jpg
--------------------------------------------------------------------------------
/GenericCellControllersExample/GenericCellControllersExample/Resources/cat07.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Busuu/generic-cell-controllers/5aeef534d3bca8c567c9dead358fb1a5e98adb89/GenericCellControllersExample/GenericCellControllersExample/Resources/cat07.jpg
--------------------------------------------------------------------------------
/GenericCellControllersExample/GenericCellControllersExample/TextDetail/TextDetailViewController.swift:
--------------------------------------------------------------------------------
1 | //
2 | // TextDetailViewController.swift
3 | // GenericCellControllersExample
4 | //
5 | // Created by Javier Valdera on 28/09/2017.
6 | // Copyright © 2017 Busuu Ltd. All rights reserved.
7 | //
8 |
9 | import UIKit
10 |
11 | class TextDetailViewController: UIViewController {
12 |
13 | @IBOutlet weak var contentLabel: UILabel!
14 | @IBOutlet weak var contentFrameView: UIView!
15 |
16 | private let post: Post
17 |
18 | init(post: Post) {
19 | self.post = post
20 |
21 | super.init(nibName: String(describing: type(of: self)), bundle: Bundle(for: type(of: self)))
22 | }
23 |
24 | required init?(coder aDecoder: NSCoder) {
25 | fatalError("init(coder:) has not been implemented")
26 | }
27 |
28 | override func viewDidLoad() {
29 | super.viewDidLoad()
30 |
31 | title = "\(post.author)'s Post"
32 |
33 | configureContentFrameView()
34 |
35 | contentLabel.text = post.text
36 | }
37 |
38 | }
39 |
40 | private extension TextDetailViewController {
41 |
42 | func configureContentFrameView() {
43 | contentFrameView.layer.cornerRadius = 8
44 | contentFrameView.layer.borderColor = UIColor.darkGray.cgColor
45 | contentFrameView.layer.borderWidth = 1
46 | }
47 |
48 | }
49 |
--------------------------------------------------------------------------------
/GenericCellControllersExample/GenericCellControllersExample/TextDetail/TextDetailViewController.xib:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2017 Busuu Ltd.
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Generic Cell Controllers
2 |
3 | `GenericCellControllers` is a Swift framework that simplifies dealing with [heterogeneous] lists of data in a `UITableView` or `UICollectionView`. Its main goal is to reduce boilerplate and to let you focus on the important bits of your feature.
4 |
5 | ## Motivation
6 | You can find all about the process that led us to create `GenericCellControllers` in our [blog](https://tech.busuu.com/dealing-with-different-kinds-of-cells-in-swift-part-1-of-3-18c6cd10a0b3).
7 |
8 | ## Features
9 | - Handles all boilerplate letting you focus on your features.
10 | - Uses generics to adapt to your types. No need to downcast in your code.
11 | - Highly flexible and expandable.
12 | - Unified API for UITableView and UICollectionView.
13 | - Compiler time checks. Helps you detect mistakes ahead of time.
14 |
15 | ## How to use
16 | The best way to learn how to use it is to [read](https://tech.busuu.com/dealing-with-different-kinds-of-cells-in-swift-part-1-of-3-18c6cd10a0b3) [our](https://tech.busuu.com/dealing-with-different-kinds-of-cells-in-swift-part-2-of-3-3fe73b0c50c6) [blogposts](https://tech.busuu.com/dealing-with-different-kinds-of-cells-in-swift-part-3-of-3-80275e29513b) or have a look at the provided example project.
17 |
18 | A quick summary would be:
19 | 1. Subclass `GenericCellController` and implement the methods you require (all of them are optional).
20 | ```swift
21 | import GenericCellControllers
22 |
23 | class MyCellController: GenericCellController {
24 | func configureCell(_ cell: MyCell) {
25 | // Your configuration code...
26 | }
27 |
28 | func cellSelected() {
29 | // Action to execute when the cell is selected...
30 | }
31 | }
32 | ```
33 | 2. Register your cell controllers in your `UITableView` or `UICollectionView`.
34 | ```swift
35 | MyCellController.registerCell(on: tableView)
36 | MyOtherCellController.registerCell(on: tableView)
37 | ````
38 | 3. Create your list of cell controllers that matches your list of [heterogeneous] data.
39 | ```swift
40 | import GenericCellControllers
41 |
42 | class MyTableController: UITableViewDataSource, UITableViewDelegate {
43 | var tableView: UITableView!
44 | var cellControllers: [TableCellController] = []
45 | ...
46 | func updateTableView() {
47 | cellControllers = createCellControllers()
48 | tableView.delegate = self
49 | tableView.dataSource = self
50 | tableView.reloadData()
51 | }
52 | }
53 | ```
54 | For points **2** and **3** we recommend the use of the [Factory pattern](https://tech.busuu.com/dealing-with-different-kinds-of-cells-in-swift-part-1-of-3-18c6cd10a0b3#4b40).
55 |
56 | 4. Forward the relevant *delegate* and *datasource* methods to the Cell Controllers.
57 | ```swift
58 | ...
59 | func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
60 | return cellControllers[indexPath.row].cellFromReusableCellHolder(tableView, forIndexPath: indexPath)
61 | }
62 |
63 | func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
64 | cellControllers[indexPath.row].cellSelected()
65 | }
66 | ...
67 | ```
68 | Again, refer to our [blog](https://tech.busuu.com/dealing-with-different-kinds-of-cells-in-swift-part-1-of-3-18c6cd10a0b3) (by now you may have figured out that you should really read it 😜) or have a look at the example project for more details.
69 | ### Variable `IndexPath`s
70 | The `GenericCellController`s use the `IndexPath` they are sent while dequeuing cells to be able to locate that cell in other methods, like `currentCell()`. If you insert or delete cells in your `UITableView` or `UICollectionView` without reloading it, remember to manually update the `IndexPath` of the Cell Controllers that may be affected.
71 | ## How to install
72 | ### CocoaPods
73 | 1. Make sure `use_frameworks!` is added to your `Podfile`.
74 | 2. Include the following in your `Podfile`:
75 | ```
76 | pod 'GenericCellControllers'
77 | ```
78 | 3. Run `pod install`.
79 | ### Carthage
80 | Simply add GenericCellControllers to your Cartfile:
81 | ```
82 | github "Busuu/generic-cell-controllers"
83 | ```
84 | ### Manually
85 | 1. Clone, add as a submodule or [download](https://github.com/Busuu/generic-cell-controllers/archive/master.zip).
86 | 2. Drag and drop `GenericCellControllers` project to your workspace.
87 | 3. Add `GenericCellControllers` to Embedded binaries.
88 | ## License
89 | Source code is distributed under MIT license.
90 |
--------------------------------------------------------------------------------