Ce diaporama a bien été signalé.
Nous utilisons votre profil LinkedIn et vos données d’activité pour vous proposer des publicités personnalisées et pertinentes. Vous pouvez changer vos préférences de publicités à tout moment.
Chainable
Data Sources
or how I stopped worrying and
started abusing table view updates
Amadour Griffais
CocoaHeads Paris ...
@ * [ : ] ;
Disclaimer
🍓
UITableView *tableView;
- (UITableViewCell*) tableView:(UITableView *)tableView
cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
UITableViewCell* ...
Animations 😍
[tableView beginUpdates];
[self.fruits removeObjectAtIndex:indexPath.row];
[tableView deleteRowsAtIndexPaths:...
//Controller
- (UITableViewCell*) tableView:(UITableView *)tableView
cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
UITa...
- (UITableViewCell*) tableView:(UITableView *)tableView
cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
FruitCell* cell =...
- (UITableViewCell*) tableView:(UITableView *)tableView
cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
if (indexPath.sec...
- (UITableViewCell*) tableView:(UITableView *)tableView
cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
switch (indexPath...
- (UITableViewCell*) tableView:(UITableView *)tableView
cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
if (indexPath.sec...
- (UITableViewCell*) tableView:(UITableView *)tableView
cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
if (indexPath.sec...
Animations 😱
2016-09-06 14:07:23.167 MyBeautifulTableView[89346:883532] *** Terminating app due to
uncaught exception 'NSI...
[tableView reloadData];
😢
UITableViewDataSource
UITableViewDataSource
🍓 🍆
💵
M
C
V
UITableViewDataSource
💩
🍓 🍆
💵
M
C
V
MCV
🍓
🍆
+
💵
<
🍓
🍓
🍆
🍓
💵
🍆
🍓
🍆
+
💵
<
ChainableDataSource
@protocol ChainableDataSource <NSObject>
- (NSInteger) numberOfSectionsInDataSource;
- (NSInteger) numberOfObjectsInDataSo...
🍓
🍆
💵
M
🍓
🍆
💵
M
NSArray (ChainableDataSource)
FetchedResultsDataSource
<#YourChainableDataSource#>
MCV
🍓
🍆
+
💵
<
🍓
🍓
🍆
🍓
💵
🍆
🍓
CellDataSource
🍓
CellDataSource
@interface CellDataSource : NSObject
<ChainableDataSource, UITableViewDataSource>
- (NSString*) cellIdent...
MCV
🍓
🍆
+
💵
<
🍓
🍓
🍆
🍓
💵
🍆
+
<
+
<
<#YourChainableDataSource#>
+
<
TransformDataSource
<#YourChainableDataSource#>
@interface TransformDataSource : NSObject <ChainableDataSource>
@property (nonatomic, copy) NSArray<id<ChainableDataSource...
+
<
TransformDataSource
<#YourChainableDataSource#>
+
TransformDataSource
<#YourChainableDataSource#>
ConcatenatedSectionsDataSource
<
TransformDataSource
<#YourChainableDataSource#>
ConcatenatedSectionsDataSource
InsertionDataSource
TransformDataSource
<#YourChainableDataSource#>
ConcatenatedSectionsDataSource
InsertionDataSource
🎉SwitchDataSource
Flatt...
Updates
MCV
🍓
🍆
+
💵
<
🍓
🍓
🍆
🍓
💵
🍆
🍓
🍆
+
💵
<
🍓
🍓
🍆
🍓
💵
🍆
ChainableDataSourceDelegate
@protocol ChainableDataSourceDelegate <NSObject>
- (void) dataSourceDidReload:(id<ChainableDataSource>)dataSource;
- (void...
A
B
E
D
C
F
A
C
H
D
G
The truth about updates
A
B
E
D
C
F
A
C
H
D
G
The truth about updates
delete: 1, 4, 5 insert: 2, 4
A
B
E
D
C
F
A
C
H
D
G
The truth about updates
update: 1
[tableView beginUpdates];
[self.fruits removeObjectAtIndex:indexPath.row];
[tableView deleteRowsAtIndexPaths:@[indexPath]
...
[tableView beginUpdates];
[self.fruits removeObjectAtIndex:indexPath.row];
[tableView deleteRowsAtIndexPaths:@[indexPath]
...
🍓
🍆
+
💵
<
🍓
🍓
🍆
🍓
💵
🍆
ChainableDataSourceDelegate
🍓
🍓
🍆
🍓
💵
🍆
UITableView <ChainableDataSourceDelegate>
UICollectionView <ChainableDataSourceDelegate>
🍓
🍆
+
💵
<
🍓
🍓
🍆
🍓
💵
🍆
ChainableDataSourceDelegate
CellDataSource
didUpdateObjectsAtIndexPaths -> configureCell:withObject:
everything else -> forward
🍓
🍆
+
💵
<
🍓
🍓
🍆
🍓
💵
🍆
ChainableDataSourceDelegate
🍓
🍆
💵
<#YourChainableDataSource#>
FetchedResultsDatasource
🍓
🍆
+
💵
<
🍓
🍓
🍆
🍓
💵
🍆
ChainableDataSourceDelegate
+
<
basic mapping -> automatic
TransformDataSource
TransformDataSource
advanced mapping:
tweak update set pre and post upstream updates
- (void) preRefreshTranslateSourceUpd...
🍓
🍓
🍓
🍓
🍓
🍓
🍓
dataSourceDidReload
🍓
🍓
🍓
🍓
🍓
🍓
🍓
dataSourceDidReload
dataSourceDidReload
(reloadData - 😥 )
🍓Δ
🍓
🍓
🍓
🍓
🍓
🍓
DeltaUpdateDataSource
dataSourceDidReload
A
B
E
D
C
F
A
C
H
D
G
A
B
E
D
C
F
A
C
H
D
G
- = ?
A
B
E
D
C
F
A
C
H
D
G
delete: 1, 4, 5 insert: 2, 4
diff algorithm
Longest Common Subsequence
🍓Δ
🍓
🍓
🍓
🍓
🍓
🍓
DeltaUpdateDataSource
dataSourceDidReload
🍓Δ
🍓
🍓
🍓
🍓
🍓
🍓
DeltaUpdateDataSource
dataSourceDidReload
dataSourceWillUpdate
dataSource…∆
dataSourceDidUpdate😁
Updates
//TODO: Nothing
🍓
🍆
+
💵
<
🍓
🍓
🍆
🍓
💵
🍆
CellDataSource
CellDataSource
- (NSString*) cellIdentifierForObject:(id)object
{
return NSStringFromClass([object class]);
}
- (void) con...
🍓
🍆
+
💵
<
🍓
🍓
🍆
🍓
💵
🍆
CellDataSource
🍓
🍆
+
💵
<
🍓
🍓
🍆
🍓
💵
🍆
CellDataSource:
alternative approach
🍓
🍆
+
💵
<
🍓
🍓
🍆
🍓
💵
🍆
TransformDataSource (CellDataSource)
forward delegate/data source methods
based on index mapping
TransformDataSource (CellDataSource)
forward delegate/data source methods
based on index mapping
(My dirty secret)
- (void) forwardInvocation:(NSInvocation *)anInvocation
{
if ([self isForwardableDelegateSelector:anInvocation.selector]) ...
🍓
🍆
+
💵
<
🍓
🍓
🍆
🍓
💵
🍆
CellDataSource
@implementation FruitCellDataSource
- (NSString*) cellIdentifierForObject:(id)object
{
return @"fruit-cell"...
🍓
🍆
+
💵
<
🍓
🍓
🍆
🍓
💵
🍆
It’s alive!
Demo
Open source*
*before this holiday season
Open source*
?
amadour
Chainable datasource
Chainable datasource
Prochain SlideShare
Chargement dans…5
×

Chainable datasource

3 416 vues

Publié le

Une approche qui permet d'alimenter des table views de manière déclarative, d'y combiner des données hétérogènes, et de gérer facilement et surtout de manière fiable les updates, par Amadour Griffais.

Publié dans : Logiciels
  • Soyez le premier à commenter

  • Soyez le premier à aimer ceci

Chainable datasource

  1. 1. Chainable Data Sources or how I stopped worrying and started abusing table view updates Amadour Griffais CocoaHeads Paris – 8 September 2016
  2. 2. @ * [ : ] ; Disclaimer 🍓
  3. 3. UITableView *tableView;
  4. 4. - (UITableViewCell*) tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { UITableViewCell* cell = [tableView dequeueReusableCellWithIdentifier:@"fruit-cell" forIndexPath:indexPath]; Fruit* fruit = self.fruits[0]; cell.titleLabel.text = fruit.name; return cell; } 😎
  5. 5. Animations 😍 [tableView beginUpdates]; [self.fruits removeObjectAtIndex:indexPath.row]; [tableView deleteRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationAutomatic]; [tableView endUpdates];
  6. 6. //Controller - (UITableViewCell*) tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { UITableViewCell* cell = [tableView dequeueReusableCellWithIdentifier:@"fruit-cell" forIndexPath:indexPath]; Fruit* fruit = self.fruits[0]; //Model cell.titleLabel.text = fruit.name; //View return cell; } 🤓
  7. 7. - (UITableViewCell*) tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { FruitCell* cell = (FruitCell*)[tableView dequeueReusableCellWithIdentifier:@"fruit-cell" forIndexPath:indexPath]; cell.fruit = fruit; return cell; } 🤔
  8. 8. - (UITableViewCell*) tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { if (indexPath.section == FRUIT_SECTION) { UITableViewCell* cell = [tableView dequeueReusableCellWithIdentifier:@"fruit-cell" forIndexPath:indexPath]; Fruit* fruit = self.fruits[0]; cell.titleLabel.text = fruit.name; return cell; } else if (indexPath.section == VEGETABLE_SECTION) { UITableViewCell* cell = [tableView dequeueReusableCellWithIdentifier:@"veg-cell" forIndexPath:indexPath]; ... 😒
  9. 9. - (UITableViewCell*) tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { switch (indexPath.section) { case FRUIT_SECTION: { UITableViewCell* cell = [tableView dequeueReusableCellWithIdentifier:@"fruit-cell" forIndexPath:indexPath]; Fruit* fruit = self.fruits[0]; cell.titleLabel.text = fruit.name; return cell; } break; case VEGETABLE_SECTION: { ... 😟
  10. 10. - (UITableViewCell*) tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { if (indexPath.section == AD_SECTION && indexPath.row == AD_ROW) { return [tableView dequeueReusableCellWithIdentifier:@"ad-cell" forIndexPath:indexPath]; } indexPath = [self offsetIndexPath:indexPath ifAfter:AD_INDEX_PATH]; switch (indexPath.section) { case FRUIT_SECTION: { UITableViewCell* cell = [tableView dequeueReusableCellWithIdentifier:@"fruit-cell" forIndexPath:indexPath]; ... 😨
  11. 11. - (UITableViewCell*) tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { if (indexPath.section == AD_SECTION && indexPath.row == AD_ROW && self.isAdLoaded) { return [tableView dequeueReusableCellWithIdentifier:@"ad-cell" forIndexPath:indexPath]; } indexPath = [self offsetIndexPath:indexPath ifAfter:AD_INDEX_PATH]; switch (indexPath.section) { case FRUIT_SECTION: { UITableViewCell* cell = [tableView dequeueReusableCellWithIdentifier:@"fruit-cell" forIndexPath:indexPath]; ... 😰
  12. 12. Animations 😱 2016-09-06 14:07:23.167 MyBeautifulTableView[89346:883532] *** Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'Invalid update: invalid number of rows in section 0. The number of rows contained in an existing section after the update (1) must be equal to the number of rows contained in that section before the update (9), plus or minus the number of rows inserted or deleted from that section (0 inserted, 0 deleted) and plus or minus the number of rows moved into or out of that section (0 moved in, 0 moved out).' *** First throw call stack: ( 0 CoreFoundation 0x000000010eba034b __exceptionPreprocess + 171 1 libobjc.A.dylib 0x000000010e60121e objc_exception_throw + 48 2 CoreFoundation 0x000000010eba4442 +[NSException raise:format:arguments:] + 98 3 Foundation 0x0000000109620edd -[NSAssertionHandler handleFailureInMethod:object:file:lineNumber:description:] + 195 4 UIKit 0x000000010c2172f4 -[UITableView _endCellAnimationsWithContext:] + 17558 ...
  13. 13. [tableView reloadData]; 😢
  14. 14. UITableViewDataSource
  15. 15. UITableViewDataSource 🍓 🍆 💵 M C V
  16. 16. UITableViewDataSource 💩 🍓 🍆 💵 M C V
  17. 17. MCV 🍓 🍆 + 💵 < 🍓 🍓 🍆 🍓 💵 🍆
  18. 18. 🍓 🍆 + 💵 < ChainableDataSource
  19. 19. @protocol ChainableDataSource <NSObject> - (NSInteger) numberOfSectionsInDataSource; - (NSInteger) numberOfObjectsInDataSourceSection:(NSInteger)section; - (id) dataSourceObjectAtIndexPath:(NSIndexPath*)indexPath; - (NSString*) nameForDataSourceSectionAtIndex:(NSInteger)section; @property (weak) id<ChainableDataSourceDelegate> dataSourceDelegate; @end
  20. 20. 🍓 🍆 💵 M
  21. 21. 🍓 🍆 💵 M NSArray (ChainableDataSource) FetchedResultsDataSource <#YourChainableDataSource#>
  22. 22. MCV 🍓 🍆 + 💵 < 🍓 🍓 🍆 🍓 💵 🍆
  23. 23. 🍓 CellDataSource
  24. 24. 🍓 CellDataSource @interface CellDataSource : NSObject <ChainableDataSource, UITableViewDataSource> - (NSString*) cellIdentifierForObject:(id)object; - (void) configureCell:(UIView<GenericCell>*)cell withObject:(id)object;
  25. 25. MCV 🍓 🍆 + 💵 < 🍓 🍓 🍆 🍓 💵 🍆
  26. 26. + <
  27. 27. + < <#YourChainableDataSource#>
  28. 28. + < TransformDataSource <#YourChainableDataSource#>
  29. 29. @interface TransformDataSource : NSObject <ChainableDataSource> @property (nonatomic, copy) NSArray<id<ChainableDataSource>>* dataSources; - (NSIndexPath*) sourceIndexPathForIndexPath:(NSIndexPath*)indexPath; - (NSIndexPath*) indexPathForSourceIndexPath:(NSIndexPath*)sourceIndexPath inDataSource:(id<ChainableDataSource>)sourceDataSource; - (NSInteger) sectionIndexForSourceSectionIndex:(NSInteger)sourceSection inDataSource:(id<ChainableDataSource>)sourceDataSource; - (NSIndexPath*) sourceSectionIndexPathForSectionIndex:(NSInteger)section; @end
  30. 30. + < TransformDataSource <#YourChainableDataSource#>
  31. 31. + TransformDataSource <#YourChainableDataSource#> ConcatenatedSectionsDataSource
  32. 32. < TransformDataSource <#YourChainableDataSource#> ConcatenatedSectionsDataSource InsertionDataSource
  33. 33. TransformDataSource <#YourChainableDataSource#> ConcatenatedSectionsDataSource InsertionDataSource 🎉SwitchDataSource FlattenedDataSource FilterDataSource PlaceholderDataSource EmptySectionFilterDataSource <#YourTransformDataSource#>
  34. 34. Updates
  35. 35. MCV 🍓 🍆 + 💵 < 🍓 🍓 🍆 🍓 💵 🍆
  36. 36. 🍓 🍆 + 💵 < 🍓 🍓 🍆 🍓 💵 🍆 ChainableDataSourceDelegate
  37. 37. @protocol ChainableDataSourceDelegate <NSObject> - (void) dataSourceDidReload:(id<ChainableDataSource>)dataSource; - (void) dataSourceWillUpdate:(id<ChainableDataSource>)dataSource; - (void) dataSourceDidUpdate:(id<ChainableDataSource>)dataSource; - (void) dataSource:(id<ChainableDataSource>)dataSource didDeleteSectionsAtIndexes:(NSIndexSet*)sectionIndexes; - (void) dataSource:(id<ChainableDataSource>)dataSource didInsertSectionsAtIndexes:(NSIndexSet*)sectionIndexes; - (void) dataSource:(id<ChainableDataSource>)dataSource didDeleteObjectsAtIndexPaths:(NSArray<NSIndexPath*>*)indexPath; - (void) dataSource:(id<ChainableDataSource>)dataSource didInsertObjectsAtIndexPaths:(NSArray<NSIndexPath*>*)indexPath; - (void) dataSource:(id<ChainableDataSource>)dataSource didUpdateObjectsAtIndexPaths:(NSArray<NSIndexPath*>*)indexPath; @end
  38. 38. A B E D C F A C H D G The truth about updates
  39. 39. A B E D C F A C H D G The truth about updates delete: 1, 4, 5 insert: 2, 4
  40. 40. A B E D C F A C H D G The truth about updates update: 1
  41. 41. [tableView beginUpdates]; [self.fruits removeObjectAtIndex:indexPath.row]; [tableView deleteRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationAutomatic]; [tableView endUpdates]; //OK The truth about updates
  42. 42. [tableView beginUpdates]; [self.fruits removeObjectAtIndex:indexPath.row]; [tableView deleteRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationAutomatic]; [tableView deleteRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationAutomatic]; [tableView deleteRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationAutomatic]; [tableView deleteRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationAutomatic]; [tableView endUpdates]; //Still OK The truth about updates
  43. 43. 🍓 🍆 + 💵 < 🍓 🍓 🍆 🍓 💵 🍆 ChainableDataSourceDelegate
  44. 44. 🍓 🍓 🍆 🍓 💵 🍆 UITableView <ChainableDataSourceDelegate> UICollectionView <ChainableDataSourceDelegate>
  45. 45. 🍓 🍆 + 💵 < 🍓 🍓 🍆 🍓 💵 🍆 ChainableDataSourceDelegate
  46. 46. CellDataSource didUpdateObjectsAtIndexPaths -> configureCell:withObject: everything else -> forward
  47. 47. 🍓 🍆 + 💵 < 🍓 🍓 🍆 🍓 💵 🍆 ChainableDataSourceDelegate
  48. 48. 🍓 🍆 💵 <#YourChainableDataSource#> FetchedResultsDatasource
  49. 49. 🍓 🍆 + 💵 < 🍓 🍓 🍆 🍓 💵 🍆 ChainableDataSourceDelegate
  50. 50. + < basic mapping -> automatic TransformDataSource
  51. 51. TransformDataSource advanced mapping: tweak update set pre and post upstream updates - (void) preRefreshTranslateSourceUpdateCache:(UpdateCache*)sourceUpdateCache fromDataSource:(id<ChainableDataSource>)dataSource toUpdateCache:(UpdateCache*)updateCache; - (void) postRefreshTranslateSourceUpdateCache:(UpdateCache*)sourceUpdateCache fromDataSource:(id<ChainableDataSource>)dataSource toUpdateCache:(UpdateCache*)updateCache;
  52. 52. 🍓 🍓 🍓 🍓 🍓 🍓 🍓 dataSourceDidReload
  53. 53. 🍓 🍓 🍓 🍓 🍓 🍓 🍓 dataSourceDidReload dataSourceDidReload (reloadData - 😥 )
  54. 54. 🍓Δ 🍓 🍓 🍓 🍓 🍓 🍓 DeltaUpdateDataSource dataSourceDidReload
  55. 55. A B E D C F A C H D G
  56. 56. A B E D C F A C H D G - = ?
  57. 57. A B E D C F A C H D G delete: 1, 4, 5 insert: 2, 4 diff algorithm Longest Common Subsequence
  58. 58. 🍓Δ 🍓 🍓 🍓 🍓 🍓 🍓 DeltaUpdateDataSource dataSourceDidReload
  59. 59. 🍓Δ 🍓 🍓 🍓 🍓 🍓 🍓 DeltaUpdateDataSource dataSourceDidReload dataSourceWillUpdate dataSource…∆ dataSourceDidUpdate😁
  60. 60. Updates //TODO: Nothing
  61. 61. 🍓 🍆 + 💵 < 🍓 🍓 🍆 🍓 💵 🍆 CellDataSource
  62. 62. CellDataSource - (NSString*) cellIdentifierForObject:(id)object { return NSStringFromClass([object class]); } - (void) configureCell:(UITableViewCell*)cell withObject:(id)object { if ([cell.reuseIdentifier isEqual:@"Fruit"]) { //... } else if ([cell.reuseIdentifier isEqual:@"Vegetable"]) { //... } // 😢 }
  63. 63. 🍓 🍆 + 💵 < 🍓 🍓 🍆 🍓 💵 🍆 CellDataSource
  64. 64. 🍓 🍆 + 💵 < 🍓 🍓 🍆 🍓 💵 🍆 CellDataSource: alternative approach
  65. 65. 🍓 🍆 + 💵 < 🍓 🍓 🍆 🍓 💵 🍆 TransformDataSource (CellDataSource) forward delegate/data source methods based on index mapping
  66. 66. TransformDataSource (CellDataSource) forward delegate/data source methods based on index mapping (My dirty secret)
  67. 67. - (void) forwardInvocation:(NSInvocation *)anInvocation { if ([self isForwardableDelegateSelector:anInvocation.selector]) { NSMethodSignature* signature = anInvocation.methodSignature; NSArray* components = [NSStringFromSelector(anInvocation.selector)componentsSeparatedByString:@":"]; for (NSInteger argIndex = 0; argIndex < signature.numberOfArguments; argIndex++) { //skip self and cmd if (argIndex < 2) { continue; } //won't work with targetIndexPathForMoveFromRowAtIndexPath, since two indexPath are present const char* argType = [signature getArgumentTypeAtIndex:argIndex]; if (strcmp(argType, @encode(NSIndexPath*)) == 0) { __unsafe_unretained id arg; [anInvocation getArgument:&arg atIndex:argIndex]; if ([arg isKindOfClass:[NSIndexPath class]]) { NSIndexPath* indexPath = arg; NSIndexPath* fullIndexPath = [self sourceIndexPathForIndexPath:indexPath]; id<ChainableDataSource> dataSource = self.dataSources[[fullIndexPath indexAtPosition:0]]; NSIndexPath* dsIndexPath = [NSIndexPath indexPathForRow:[fullIndexPath indexAtPosition:2] inSection: [fullIndexPath indexAtPosition:1]]; [anInvocation setArgument:&dsIndexPath atIndex:argIndex]; if ([dataSource respondsToSelector:anInvocation.selector]) { [anInvocation invokeWithTarget:dataSource]; } return; } } else if (strcmp(argType, @encode(NSInteger)) == 0 && [components[argIndex-2] hasSuffix:@"Section"]) { NSInteger section; [anInvocation getArgument:&section atIndex:argIndex]; NSIndexPath* sourceSectionIndexPath = [self sourceSectionIndexPathForSectionIndex:section]; if (sourceSectionIndexPath) { id<ChainableDataSource> dataSource = self.dataSources[[sourceSectionIndexPath indexAtPosition:0]]; NSInteger sourceSection = [sourceSectionIndexPath indexAtPosition:1]; [anInvocation setArgument:&sourceSection atIndex:argIndex]; if ([dataSource respondsToSelector:anInvocation.selector]) { [anInvocation invokeWithTarget:dataSource]; } } return; } } return; } return [super forwardInvocation:anInvocation]; }
  68. 68. 🍓 🍆 + 💵 < 🍓 🍓 🍆 🍓 💵 🍆
  69. 69. CellDataSource @implementation FruitCellDataSource - (NSString*) cellIdentifierForObject:(id)object { return @"fruit-cell"; } - (void) configureCell:(UITableViewCell*)cell withObject:(id)object { Fruit* fruit = object; cell.titleLabel.text = fruit.name; } - (void) tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath { Fruit* fruit = [self.objectsDataSource dataSourceObjectAtIndexPath:indexPath]; //... } @end
  70. 70. 🍓 🍆 + 💵 < 🍓 🍓 🍆 🍓 💵 🍆 It’s alive!
  71. 71. Demo
  72. 72. Open source*
  73. 73. *before this holiday season Open source*
  74. 74. ? amadour

×