SlideShare a Scribd company logo
Chainable
Data Sources
or how I stopped worrying and
started abusing table view updates
Amadour Griffais
CocoaHeads Paris – 8 September 2016
@ * [ : ] ;
Disclaimer
🍓
UITableView *tableView;
- (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;
}
😎
Animations 😍
[tableView beginUpdates];
[self.fruits removeObjectAtIndex:indexPath.row];
[tableView deleteRowsAtIndexPaths:@[indexPath]
withRowAnimation:UITableViewRowAnimationAutomatic];
[tableView endUpdates];
//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;
}
🤓
- (UITableViewCell*) tableView:(UITableView *)tableView
cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
FruitCell* cell = (FruitCell*)[tableView
dequeueReusableCellWithIdentifier:@"fruit-cell"
forIndexPath:indexPath];
cell.fruit = fruit;
return cell;
}
🤔
- (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];
...
😒
- (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: {
...
😟
- (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];
...
😨
- (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];
...
😰
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
...
[tableView reloadData];
😢
Chainable datasource
UITableViewDataSource
UITableViewDataSource
🍓 🍆
💵
M
C
V
UITableViewDataSource
💩
🍓 🍆
💵
M
C
V
MCV
🍓
🍆
+
💵
<
🍓
🍓
🍆
🍓
💵
🍆
🍓
🍆
+
💵
<
ChainableDataSource
@protocol ChainableDataSource <NSObject>
- (NSInteger) numberOfSectionsInDataSource;
- (NSInteger) numberOfObjectsInDataSourceSection:(NSInteger)section;
- (id) dataSourceObjectAtIndexPath:(NSIndexPath*)indexPath;
- (NSString*) nameForDataSourceSectionAtIndex:(NSInteger)section;
@property (weak) id<ChainableDataSourceDelegate> dataSourceDelegate;
@end
🍓
🍆
💵
M
🍓
🍆
💵
M
NSArray (ChainableDataSource)
FetchedResultsDataSource
<#YourChainableDataSource#>
MCV
🍓
🍆
+
💵
<
🍓
🍓
🍆
🍓
💵
🍆
Chainable datasource
🍓
CellDataSource
🍓
CellDataSource
@interface CellDataSource : NSObject
<ChainableDataSource, UITableViewDataSource>
- (NSString*) cellIdentifierForObject:(id)object;
- (void) configureCell:(UIView<GenericCell>*)cell withObject:(id)object;
MCV
🍓
🍆
+
💵
<
🍓
🍓
🍆
🍓
💵
🍆
+
<
+
<
<#YourChainableDataSource#>
+
<
TransformDataSource
<#YourChainableDataSource#>
@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
+
<
TransformDataSource
<#YourChainableDataSource#>
+
TransformDataSource
<#YourChainableDataSource#>
ConcatenatedSectionsDataSource
<
TransformDataSource
<#YourChainableDataSource#>
ConcatenatedSectionsDataSource
InsertionDataSource
TransformDataSource
<#YourChainableDataSource#>
ConcatenatedSectionsDataSource
InsertionDataSource
🎉SwitchDataSource
FlattenedDataSource
FilterDataSource
PlaceholderDataSource
EmptySectionFilterDataSource
<#YourTransformDataSource#>
Updates
MCV
🍓
🍆
+
💵
<
🍓
🍓
🍆
🍓
💵
🍆
🍓
🍆
+
💵
<
🍓
🍓
🍆
🍓
💵
🍆
ChainableDataSourceDelegate
@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
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]
withRowAnimation:UITableViewRowAnimationAutomatic];
[tableView endUpdates];
//OK
The truth about updates
[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
🍓
🍆
+
💵
<
🍓
🍓
🍆
🍓
💵
🍆
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) preRefreshTranslateSourceUpdateCache:(UpdateCache*)sourceUpdateCache
fromDataSource:(id<ChainableDataSource>)dataSource
toUpdateCache:(UpdateCache*)updateCache;
- (void) postRefreshTranslateSourceUpdateCache:(UpdateCache*)sourceUpdateCache
fromDataSource:(id<ChainableDataSource>)dataSource
toUpdateCache:(UpdateCache*)updateCache;
🍓
🍓
🍓
🍓
🍓
🍓
🍓
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) configureCell:(UITableViewCell*)cell withObject:(id)object
{
if ([cell.reuseIdentifier isEqual:@"Fruit"]) {
//...
} else if ([cell.reuseIdentifier isEqual:@"Vegetable"]) {
//...
}
// 😢
}
🍓
🍆
+
💵
<
🍓
🍓
🍆
🍓
💵
🍆
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]) {
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];
}
🍓
🍆
+
💵
<
🍓
🍓
🍆
🍓
💵
🍆
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
🍓
🍆
+
💵
<
🍓
🍓
🍆
🍓
💵
🍆
It’s alive!
Demo
Open source*
*before this holiday season
Open source*
?
amadour

More Related Content

PDF
What's new in iOS9
PPTX
AngularJS $Provide Service
PPTX
Upgrading from Angular 1.x to Angular 2.x
PDF
節子、それViewControllerやない...、FatViewControllerや...。
PDF
VC「もしかして...」Model「私たち...」「「入れ替わってるー!?」」を前前前世から防ぐ方法
PPTX
Angular 1.x vs. Angular 2.x
PDF
Blending Culture in Twitter Client
PPTX
Angular 2 Architecture
What's new in iOS9
AngularJS $Provide Service
Upgrading from Angular 1.x to Angular 2.x
節子、それViewControllerやない...、FatViewControllerや...。
VC「もしかして...」Model「私たち...」「「入れ替わってるー!?」」を前前前世から防ぐ方法
Angular 1.x vs. Angular 2.x
Blending Culture in Twitter Client
Angular 2 Architecture

What's hot (20)

PDF
Standford 2015 week4: 1.Protocols and Delegation, Gestures 2. Multiple MVCs
KEY
CocoaHeads Toulouse - Guillaume Cerquant - UIView
PDF
描画とビジネスをクリーンに分ける(公開用)
PDF
Daniel Jalkut - dotSwift 2019
PPTX
Workshop 1: Good practices in JavaScript
PDF
Unit testing UIView
PPTX
Http Communication in Angular 2.0
PPTX
Performance Optimization In Angular 2
PPTX
Angular 2 - Ahead of-time Compilation
PPTX
Routing And Navigation
PPTX
AngularJS Compile Process
PDF
Chaining et composition de fonctions avec lodash / underscore
PDF
How to build an AngularJS backend-ready app WITHOUT BACKEND
PDF
UI 모듈화로 워라밸 지키기
PDF
Cyclejs introduction
PDF
mobl
PPT
GHC Participant Training
PDF
How to test complex SaaS applications - The family july 2014
PDF
Webapps without the web
Standford 2015 week4: 1.Protocols and Delegation, Gestures 2. Multiple MVCs
CocoaHeads Toulouse - Guillaume Cerquant - UIView
描画とビジネスをクリーンに分ける(公開用)
Daniel Jalkut - dotSwift 2019
Workshop 1: Good practices in JavaScript
Unit testing UIView
Http Communication in Angular 2.0
Performance Optimization In Angular 2
Angular 2 - Ahead of-time Compilation
Routing And Navigation
AngularJS Compile Process
Chaining et composition de fonctions avec lodash / underscore
How to build an AngularJS backend-ready app WITHOUT BACKEND
UI 모듈화로 워라밸 지키기
Cyclejs introduction
mobl
GHC Participant Training
How to test complex SaaS applications - The family july 2014
Webapps without the web
Ad

Viewers also liked (20)

PPTX
La sécurité sur iOS par Arnaud de Bock
PDF
MVC-RS par Grégoire Lhotelier
PDF
Design like a developer
PDF
J'ai fait une app native en React Native
PDF
Rebranding an ios application
PDF
PDF
CONTINUOUS DELIVERY WITH FASTLANE
PDF
Handle the error
PDF
SwiftyGPIO
PDF
Programme MFI retour d'expérience
PDF
How to communicate with Smart things?
PDF
Présentation de HomeKit
PDF
Quoi de neuf dans iOS 10.3
PDF
L'intégration continue avec Bitrise
PDF
Monads in Swift
PDF
CloudKit as a backend
PDF
Comment faire de HLS votre solution vidéo préférée ?
PDF
BitTorrent on iOS
PDF
Project Entourage
PDF
IoT Best practices
La sécurité sur iOS par Arnaud de Bock
MVC-RS par Grégoire Lhotelier
Design like a developer
J'ai fait une app native en React Native
Rebranding an ios application
CONTINUOUS DELIVERY WITH FASTLANE
Handle the error
SwiftyGPIO
Programme MFI retour d'expérience
How to communicate with Smart things?
Présentation de HomeKit
Quoi de neuf dans iOS 10.3
L'intégration continue avec Bitrise
Monads in Swift
CloudKit as a backend
Comment faire de HLS votre solution vidéo préférée ?
BitTorrent on iOS
Project Entourage
IoT Best practices
Ad

Similar to Chainable datasource (20)

PDF
201104 iphone navigation-based apps
PDF
I os 04
PDF
I os 11
PDF
FormsKit: reactive forms driven by state. UA Mobile 2017.
PDF
So I already have most of the code and now I have to1. create an .pdf
PDF
Optimize CollectionView Scrolling
KEY
Django quickstart
PDF
A multi submission importer for easyform
KEY
занятие7
PDF
Formacion en movilidad: Conceptos de desarrollo en iOS (IV)
PDF
Your Second iPhone App - Code Listings
PDF
Smooth scrolling in UITableView and UICollectionView
PPTX
Swift Tableview iOS App Development
DOC
Oops lab manual2
ZIP
First Steps in Drupal Code Driven Development
PDF
20180721 code defragment
PDF
Apple Templates Considered Harmful
PDF
How to build a debug view almost for free
PPTX
DOODB_LAB.pptx
201104 iphone navigation-based apps
I os 04
I os 11
FormsKit: reactive forms driven by state. UA Mobile 2017.
So I already have most of the code and now I have to1. create an .pdf
Optimize CollectionView Scrolling
Django quickstart
A multi submission importer for easyform
занятие7
Formacion en movilidad: Conceptos de desarrollo en iOS (IV)
Your Second iPhone App - Code Listings
Smooth scrolling in UITableView and UICollectionView
Swift Tableview iOS App Development
Oops lab manual2
First Steps in Drupal Code Driven Development
20180721 code defragment
Apple Templates Considered Harmful
How to build a debug view almost for free
DOODB_LAB.pptx

More from CocoaHeads France (13)

PDF
Mutation testing for a safer Future
PDF
iOS App Group for Debugging
PDF
Asynchronous swift
PDF
Visual accessibility in iOS11
PDF
My script - One year of CocoaHeads
PDF
Ui testing dealing with push notifications
PDF
Super combinators
PDF
Build a lego app with CocoaPods
PDF
Let's migrate to Swift 3.0
PDF
Découvrir dtrace en ligne de commande.
PDF
Un retour d'expérience sur Apple Pay
PDF
Firebase par nicolas lehovetzki
PDF
Safari app extensions cleared up by Sanaa Squalli
Mutation testing for a safer Future
iOS App Group for Debugging
Asynchronous swift
Visual accessibility in iOS11
My script - One year of CocoaHeads
Ui testing dealing with push notifications
Super combinators
Build a lego app with CocoaPods
Let's migrate to Swift 3.0
Découvrir dtrace en ligne de commande.
Un retour d'expérience sur Apple Pay
Firebase par nicolas lehovetzki
Safari app extensions cleared up by Sanaa Squalli

Recently uploaded (20)

PPTX
Reimagine Home Health with the Power of Agentic AI​
PDF
Wondershare Filmora 15 Crack With Activation Key [2025
PPTX
CHAPTER 2 - PM Management and IT Context
PPTX
Transform Your Business with a Software ERP System
PDF
Why TechBuilder is the Future of Pickup and Delivery App Development (1).pdf
PDF
Design an Analysis of Algorithms II-SECS-1021-03
PPTX
Agentic AI Use Case- Contract Lifecycle Management (CLM).pptx
PDF
Raksha Bandhan Grocery Pricing Trends in India 2025.pdf
PPTX
ai tools demonstartion for schools and inter college
PDF
T3DD25 TYPO3 Content Blocks - Deep Dive by André Kraus
PDF
Upgrade and Innovation Strategies for SAP ERP Customers
PPTX
Introduction to Artificial Intelligence
PDF
AI in Product Development-omnex systems
PDF
2025 Textile ERP Trends: SAP, Odoo & Oracle
PDF
How Creative Agencies Leverage Project Management Software.pdf
PDF
How to Migrate SBCGlobal Email to Yahoo Easily
PPTX
Agentic AI : A Practical Guide. Undersating, Implementing and Scaling Autono...
PDF
Odoo Companies in India – Driving Business Transformation.pdf
PDF
System and Network Administration Chapter 2
PDF
Audit Checklist Design Aligning with ISO, IATF, and Industry Standards — Omne...
Reimagine Home Health with the Power of Agentic AI​
Wondershare Filmora 15 Crack With Activation Key [2025
CHAPTER 2 - PM Management and IT Context
Transform Your Business with a Software ERP System
Why TechBuilder is the Future of Pickup and Delivery App Development (1).pdf
Design an Analysis of Algorithms II-SECS-1021-03
Agentic AI Use Case- Contract Lifecycle Management (CLM).pptx
Raksha Bandhan Grocery Pricing Trends in India 2025.pdf
ai tools demonstartion for schools and inter college
T3DD25 TYPO3 Content Blocks - Deep Dive by André Kraus
Upgrade and Innovation Strategies for SAP ERP Customers
Introduction to Artificial Intelligence
AI in Product Development-omnex systems
2025 Textile ERP Trends: SAP, Odoo & Oracle
How Creative Agencies Leverage Project Management Software.pdf
How to Migrate SBCGlobal Email to Yahoo Easily
Agentic AI : A Practical Guide. Undersating, Implementing and Scaling Autono...
Odoo Companies in India – Driving Business Transformation.pdf
System and Network Administration Chapter 2
Audit Checklist Design Aligning with ISO, IATF, and Industry Standards — Omne...

Chainable datasource