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