Skip to content

Instantly share code, notes, and snippets.

@rdavisau
Last active November 18, 2022 11:31
Show Gist options
  • Select an option

  • Save rdavisau/20bfa9e46e89cf31d409f8796cc41cf1 to your computer and use it in GitHub Desktop.

Select an option

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);
@ivmirx
Copy link
Copy Markdown

ivmirx commented Apr 23, 2021

The code is super helpful and your article explained the diffable data sources much better than "native" articles about them! Thanks a lot!

@am-kh
Copy link
Copy Markdown

am-kh commented Oct 28, 2022

Thank you for this article
I have error at this line

IdentifierType.For(section, getSectionIdentifier);

What is For méthod? is this an extension?

Do you have a full sample?

Thank you for your help

@rdavisau
Copy link
Copy Markdown
Author

@am-kh Unfortunately I don't have the sample from the blogpost anymore.
Looks like this gist was missing at least one file which I have just added - IdentifierType.cs. If you add this hopefully you are up and running.

@am-kh
Copy link
Copy Markdown

am-kh commented Oct 28, 2022

Yes! Thank you a lot.
Tell me, is it possible to have Hot Reload with Xamarin.iOS or the new Microsoft.iOS?
I saw you had it in your videos.

@rdavisau
Copy link
Copy Markdown
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
Copy Markdown

am-kh commented Oct 28, 2022

It will be great. Thank you !

@rdavisau
Copy link
Copy Markdown
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