Skip to content

ui: Lazy-load samples through generated catalog#1807

Merged
imalcolm1 merged 6 commits into
Esri:mainfrom
mandel-capuchin:dev/manuel/lazy-samples
May 21, 2026
Merged

ui: Lazy-load samples through generated catalog#1807
imalcolm1 merged 6 commits into
Esri:mainfrom
mandel-capuchin:dev/manuel/lazy-samples

Conversation

@mandel-capuchin

Copy link
Copy Markdown
Collaborator

Description

Add a Roslyn source generator that emits an ISampleCatalogProvider with sample metadata and sample factories at build time. The generated provider lets startup build the category shell and Featured list without reflecting over every sample type or constructing every SampleInfo up front. This reduces launch work and helps avoid the iOS watchdog as the sample set grows.

Generated catalog shape:

    public sealed class GeneratedSampleCatalog : ISampleCatalogProvider
    {
        public IReadOnlyList<string> GetCategories() => Categories;
        public IReadOnlyList<string> GetSampleNames() => SampleNames;

        public SampleInfo CreateSampleInfo(string formalName)
        {
            switch (formalName)
            {
                case "DisplayMap":
                    return new SampleInfo(
                        formalName: "DisplayMap",
                        sampleName: "Display map",
                        category: "Map",
                        sampleType: typeof(DisplayMap));
                default:
                    throw new ArgumentException(...);
            }
        }

        public object CreateSample(string formalName)
        {
            switch (formalName)
            {
                case "DisplayMap":
                    return new DisplayMap();
                default:
                    throw new ArgumentException(...);
            }
        }
    }

Old startup flow:

    AppShell / flyout start
        |
        v
    SampleManager.Initialize()
        |
        v
    Assembly.GetTypes()
        |
        v
    Read attributes and create every SampleInfo
        |
        v
    Build full tree, Featured, Favorites
        |
        v
    First page can render

New startup flow:

    AppShell / flyout start
        |
        v
    SampleManager.Initialize()
        |
        v
    GeneratedSampleCatalog via partial hook
        |
        v
    Load sample names and category names only
        |
        v
    Build category shell and materialize Featured
        |
        v
    First page can render
        |
        v
    Category, search, offline data, and samples load on demand

Route SampleManager through the catalog provider API for sample lookup, category loading, all-sample materialization, and sample creation. Search, offline data, and full sample lists still materialize on demand when those flows need them instead of during boot.

Keep ReflectionSampleCatalogProvider as a plan B for consumers or targets that do not wire in the generator. The fallback preserves the old reflection-based discovery behavior behind the same ISampleCatalogProvider contract while normal app builds use the generated catalog and avoid startup reflection.

Type of change

  • Sample viewer enhancement

Platforms tested on

  • WPF .NET 8
  • WinUI
  • MAUI WinUI
  • MAUI Android
  • MAUI iOS
  • MAUI MacCatalyst

Checklist

  • Self-review of changes
  • All changes work as expected on all affected platforms
  • There are no warnings related to changes
  • Code is commented and follows .NET conventions and standards

Add a Roslyn source generator that emits an ISampleCatalogProvider with
sample metadata and sample factories at build time. The generated provider
lets startup build the category shell and Featured list without reflecting
over every sample type or constructing every SampleInfo up front. This
reduces launch work and helps avoid the iOS watchdog as the sample set grows.

Generated catalog shape:

```csharp
    public sealed class GeneratedSampleCatalog : ISampleCatalogProvider
    {
        public IReadOnlyList<string> GetCategories() => Categories;
        public IReadOnlyList<string> GetSampleNames() => SampleNames;

        public SampleInfo CreateSampleInfo(string formalName)
        {
            switch (formalName)
            {
                case "DisplayMap":
                    return new SampleInfo(
                        formalName: "DisplayMap",
                        sampleName: "Display map",
                        category: "Map",
                        sampleType: typeof(DisplayMap));
                default:
                    throw new ArgumentException(...);
            }
        }

        public object CreateSample(string formalName)
        {
            switch (formalName)
            {
                case "DisplayMap":
                    return new DisplayMap();
                default:
                    throw new ArgumentException(...);
            }
        }
    }
```

Old startup flow:

```text
    AppShell / flyout start
        |
        v
    SampleManager.Initialize()
        |
        v
    Assembly.GetTypes()
        |
        v
    Read attributes and create every SampleInfo
        |
        v
    Build full tree, Featured, Favorites
        |
        v
    First page can render
```

New startup flow:

```text
    AppShell / flyout start
        |
        v
    SampleManager.Initialize()
        |
        v
    GeneratedSampleCatalog via partial hook
        |
        v
    Load sample names and category names only
        |
        v
    Build category shell and materialize Featured
        |
        v
    First page can render
        |
        v
    Category, search, offline data, and samples load on demand
```

Route SampleManager through the catalog provider API for sample lookup,
category loading, all-sample materialization, and sample creation. Search,
offline data, and full sample lists still materialize on demand when those
flows need them instead of during boot.

Keep ReflectionSampleCatalogProvider as a plan B for consumers or targets
that do not wire in the generator. The fallback preserves the old
reflection-based discovery behavior behind the same ISampleCatalogProvider
contract while normal app builds use the generated catalog and avoid startup
reflection.

@imalcolm1 imalcolm1 left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good, loading is much faster everywhere now!

@imalcolm1 imalcolm1 merged commit 9ef1c44 into Esri:main May 21, 2026
2 of 5 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants