Skip to content

Instantly share code, notes, and snippets.

@rdavisau
Last active November 18, 2022 11:31
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save rdavisau/20bfa9e46e89cf31d409f8796cc41cf1 to your computer and use it in GitHub Desktop.
Save rdavisau/20bfa9e46e89cf31d409f8796cc41cf1 to your computer and use it in GitHub Desktop.
public static class DiffableDataSourceExtensions
{
public static NSDiffableDataSourceSnapshot<IdentifierType<TSection>, IdentifierType<TItem>>
ToDiffableDataSourceSnapshot<TSection, TSectionIdentifier, TItem, TItemIdentifier>(
this IEnumerable<TSection> sections, Func<TSection, TSectionIdentifier> getSectionIdentifier,
Func<TSection, IEnumerable<TItem>> getItems, Func<TItem, TItemIdentifier> getItemIdentifier)
{
var snapshot = new NSDiffableDataSourceSnapshot<IdentifierType<TSection>, IdentifierType<TItem>>();
foreach (var section in sections)
{
var items = getItems(section);
var sectionIdentifier = IdentifierType.For(section, getSectionIdentifier);
var itemIdentifiers = items.Select(item => IdentifierType.For(item, getItemIdentifier)).ToArray();
snapshot.AppendSections(new[] { sectionIdentifier });
snapshot.AppendItems(itemIdentifiers);
}
return snapshot;
}
}
public class EasyUITableViewDiffableDataSource<TSection, TSectionIdentifier, TItem, TItemIdentifier>
: UITableViewDiffableDataSource<IdentifierType<TSection>, IdentifierType<TItem>>, IUITableViewDiffableDataSource<TSection>
{
Func<TSection, TSectionIdentifier> _getSectionIdentifier;
Func<TSection, IEnumerable<TItem>> _getSectionItems;
Func<TItem, TItemIdentifier> _getItemIdentifier;
Func<UITableView, int, TSection, string> _titleForHeader;
Func<UITableView, int, TSection, string> _titleForFooter;
NSDiffableDataSourceSnapshot<IdentifierType<TSection>, IdentifierType<TItem>> _snapshot;
public EasyUITableViewDiffableDataSource(
UITableView tableView,
Func<TSection, TSectionIdentifier> getSectionIdentifier,
Func<TSection, IEnumerable<TItem>> getSectionItems,
Func<TItem, TItemIdentifier> getItemIdentifier,
UITableViewDiffableDataSourceCellProvider cellProvider,
Func<UITableView, int, TSection, string> titleForHeader = null,
Func<UITableView, int, TSection, string> titleForFooter = null
) : base(tableView, cellProvider)
{
_getSectionIdentifier = getSectionIdentifier;
_getSectionItems = getSectionItems;
_getItemIdentifier = getItemIdentifier;
_titleForHeader = titleForHeader;
_titleForFooter = titleForFooter;
}
public void ApplySnapshot(IEnumerable<TSection> sections, bool animatingDifferences)
{
_snapshot = sections.ToDiffableDataSourceSnapshot(
_getSectionIdentifier, _getSectionItems, _getItemIdentifier);
ApplySnapshot(_snapshot, animatingDifferences);
}
public override string TitleForHeader(UITableView tableView, nint section)
=> _titleForHeader?.Invoke(tableView, (int)section, _snapshot.SectionIdentifiers[section].Item);
public override string TitleForFooter(UITableView tableView, nint section)
=> _titleForFooter?.Invoke(tableView, (int)section, _snapshot.SectionIdentifiers[section].Item);
}
public static class EasyUITableViewDiffableDataSource
{
public static IUITableViewDiffableDataSource<TSection> Create<TSection, TSectionIdentifier, TItem, TItemIdentifier>(
UITableView tableView,
List<TSection> items,
Func<TSection, TSectionIdentifier> getSectionIdentifier,
Func<TSection, IEnumerable<TItem>> getSectionItems,
Func<TItem, TItemIdentifier> getItemIdentifier,
Func<UITableView, NSIndexPath, TItem, UITableViewCell> getCell,
Func<UITableView, int, TSection, string> titleForHeader = null,
Func<UITableView, int, TSection, string> titleForFooter = null)
=> new EasyUITableViewDiffableDataSource<TSection, TSectionIdentifier, TItem, TItemIdentifier>
(
tableView, getSectionIdentifier, getSectionItems, getItemIdentifier,
(tv, indexPath, item) => getCell(tv, indexPath, (item as IdentifierType<TItem>).Item),
titleForHeader, titleForFooter
);
}
public class FuncIdentifierType<T,U> : IdentifierType<T>
{
private readonly Func<T, U> _identifierFunc;
public FuncIdentifierType(T item, Func<T,U> identifierFunc) : base(item)
{
_identifierFunc = identifierFunc;
}
// invoke the provided func and hash the result
public override nuint GetNativeHash()
=> (nuint)_identifierFunc(Item).GetHashCode();
public override bool IsEqual(NSObject anObject)
=> GetNativeHash() == anObject.GetNativeHash();
}
public static class IdentifierType
{
public static IdentifierType<T> For<T>(T item) => new IdentifierType<T>(item);
public static IdentifierType<T> For<T,U>(T item, Func<T, U> getIdentifier) => new FuncIdentifierType<T,U>(item, getIdentifier);
}
public class IdentifierType<T> : NSObject
{
public readonly T Item;
public IdentifierType(T item)
{
Item = item;
}
// pass-through the .NET hash
public override nuint GetNativeHash()
=> (nuint) Item.GetHashCode();
public override bool IsEqual(NSObject anObject)
=> GetNativeHash() == anObject.GetNativeHash();
}
public interface IIdentifier
{
string Identifier { get; }
}
public class IIdentifierType<T> : IdentifierType<T>
where T: IIdentifier
{
public IIdentifierType(T item) : base(item) {}
// read the identifier and take its hash
public override nuint GetNativeHash()
=> (System.nuint)Item.Identifier.GetHashCode();
public override bool IsEqual(NSObject anObject)
=> GetNativeHash() == anObject.GetNativeHash();
}
public interface IUITableViewDiffableDataSource<TSection>
{
void ApplySnapshot(IEnumerable<TSection> sections, bool animatingDifferences);
}
public static NSDiffableDataSourceSnapshot<IdentifierType<TSection>, IdentifierType<TItem>>
ToDiffableDataSourceSnapshot<TSection, TSectionIdentifier, TItem, TItemIdentifier>(
this IEnumerable<TSection> sections, Func<TSection, TSectionIdentifier> getSectionIdentifier,
Func<TSection, IEnumerable<TItem>> getItems, Func<TItem, TItemIdentifier> getItemIdentifier)
{
var snapshot = new NSDiffableDataSourceSnapshot<IdentifierType<TSection>, IdentifierType<TItem>>();
foreach (var section in sections)
{
var items = getItems(section);
var sectionIdentifier = IdentifierType.For(section, getSectionIdentifier);
var itemIdentifiers = items.Select(item => IdentifierType.For(item, getItemIdentifier)).ToArray();
snapshot.AppendSections(new[] { sectionIdentifier });
snapshot.AppendItems(itemIdentifiers);
}
return snapshot;
}
public class IdentifierType<T> : NSObject
{
public readonly T Item;
public IdentifierType(T item)
{
Item = item;
}
// pass-through the .NET hash
public override nuint GetNativeHash()
=> (nuint) Item.GetHashCode();
public override bool IsEqual(NSObject anObject)
=> GetNativeHash() == anObject.GetNativeHash();
}
var source =
EasyUITableViewDiffableDataSource.Create(
TableView,
items: Games, // used for type inference, can be null at the time of the call
getSectionIdentifier: game => game.GameName, // return key for section
getSectionItems: game => game.Characters, // return items for section
getItemIdentifier: character => character.CharacterName, // return key for item
getCell: (tv, indexPath, character) => // standard GetCell, but includes typed model parameter (character)
{
// yeah yeah cell reuse
var cell = new UITableViewCell(UITableViewCellStyle.Default, "cell");
cell.TextLabel.Text = character.CharacterName;
return cell;
},
titleForHeader: (tv, sectionIndex, section) => section.GameName);
@rdavisau
Copy link
Author

@am-kh i am using tbc on both Xamarin.iOS and Microsoft.iOS; it works well if you understand the mechanism and what is/isn't possible. But it's not well documented. I will add a sample for Microsoft.iOS!

@am-kh
Copy link

am-kh commented Oct 28, 2022

It will be great. Thank you !

@rdavisau
Copy link
Author

rdavisau commented Oct 28, 2022

@am-kh I added a small ios sample and quick video if it helps. The main issue at the moment is the 'register attribute' hack; since I am doing some Microsoft.iOS work soon I will improve this aspect. I improved that and updated the sample.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment