Make Health Connect records assignable to IRecord#1464
Merged
jonathanpeppers merged 3 commits intoJun 9, 2026
Conversation
Co-authored-by: jonathanpeppers <840039+jonathanpeppers@users.noreply.github.com>
Copilot
AI
changed the title
[WIP] Fix Xamarin.AndroidX.Health.Connect ConnectClient record assignability
Fix Health Connect record classes not assignable to IRecord
Jun 8, 2026
…rfaces public
IntervalRecord, InstantaneousRecord, and SeriesRecord are `@PublishedApi
internal` in upstream Kotlin source. The previous fix forced
visibility="public" on them, which both:
- Tripped `metadata-verify` ("Preventing exposing/surfacing interfaces with
default package accessibility as public").
- Surfaced AndroidX-internal types in the public binding API surface,
contradicting upstream's intent.
Replace the visibility attrs (and the SeriesRecord/getSamples remove-node,
which is no longer needed) with a single `<add-node>` that injects
`implements Record` directly onto every concrete record class that originally
implemented one of the three internal intermediaries. This preserves the
`SleepSessionRecord : IRecord` (etc.) chain that callers need without
republishing the internal interfaces.
PublicAPI.Unshipped.txt: drop the IInstantaneousRecord/IIntervalRecord/
ISeriesRecord entries that will no longer be generated; IRecord and the
existing concrete record APIs are retained.
Also document the pattern in .github/copilot-instructions.md so future
changes hit the supported approach first.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
There was a problem hiding this comment.
Pull request overview
This PR updates the androidx.health.connect:connect-client binding to restore the expected public interface chain so concrete Health Connect record types can be used where AndroidX.Health.Connect.Client.Records.IRecord is required.
Changes:
- Injects
androidx.health.connect.client.records.Recordonto concrete record classes via a metadata<add-node>transform to restoreIRecordassignability. - Bumps
Xamarin.AndroidX.Health.Connect.ConnectClientNuGet revision from1.1.0.2to1.1.0.3. - Documents the recommended fix pattern in
.github/copilot-instructions.mdfor cases where Kotlin@PublishedApi internalintermediates are dropped by the generator.
Reviewed changes
Copilot reviewed 3 out of 3 changed files in this pull request and generated 1 comment.
| File | Description |
|---|---|
| source/androidx.health.connect/connect-client/Transforms/Metadata.xml | Adds an XML transform to re-attach Record to concrete record types when intermediate interfaces are dropped. |
| config.json | Increments the ConnectClient NuGet revision to publish the binding fix. |
| .github/copilot-instructions.md | Adds guidance for handling dropped intermediate interfaces without surfacing internal types. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Concrete
Xamarin.AndroidX.Health.Connect.ConnectClientrecord types (SleepSessionRecord,WeightRecord,HeartRateRecord,RestingHeartRateRecord, …) could not be passed asAndroidX.Health.Connect.Client.Records.IRecord, blocking shared helpers written against the common record abstraction.Root cause
In upstream AndroidX, the intermediate Kotlin interfaces are
@PublishedApi internal:class-parsetherefore correctly emits an emptyvisibility=""for them, the binding generator drops them, and that strips theimplementschain (SleepSessionRecord → IntervalRecord → Record) from every concrete record — leaving the records with noIRecord.Forcing
<attr ... name="visibility">public</attr>on the dropped interfaces (the original approach) is rejected bymetadata-verify(build/cake/validations.cake):That guard is correct: it stops us from republishing types upstream explicitly marked internal.
Fix
Inject
implements Recorddirectly onto every concrete record that originally implemented one of the three internal intermediaries, mirroring the pattern insource/GPS.Metadata.Common.xml(ReflectedParcelable/Parcelable):This keeps the public
IRecordchain on every concrete record without surfacing the internal intermediaries, sometadata-verifypasses and the upstream@PublishedApi internalcontract is honored.The fix DOES add API — even though PublicAPI.txt has no additions
This is the important part to understand when reviewing the diff. The actual API surface change is in the generated class declarations, not in
PublicAPI.Unshipped.txt:Verified locally on a real build of
androidx.health.connect.connect-client.csproj— 41 concrete record types now declare: IRecordinobj/.../generated/src/*.cs(all 41 listed below).Why PublicAPI.Unshipped.txt only loses lines: the Roslyn
PublicApiAnalyzer(RS0016/RS0017) tracks declared members — types, methods, properties, fields, events, ctors. It does not track base-type or interface-implementation lists on already-declared types. Adding: IRecordto an existing public class is invisible to it (no RS0016 warnings fire on the 41 changed classes, and the build is clean). The 9 lines removed in this PR are theIInstantaneousRecord/IIntervalRecord/ISeriesRecordentries from the previous attempt — those interfaces are no longer generated, and they shouldn't be: they correspond to upstream's@PublishedApi internaltypes.Verification (compiles 0 errors, 0 warnings)
This is the exact snippet from the user's reported scenario, built against the freshly-produced
Xamarin.AndroidX.Health.Connect.ConnectClient.dllfrom this PR:Before this PR every
GetPackageName(...)call is a CS1503. After this PR all 6 calls (spanning instantaneous, interval, and series record categories) compile.All 41 concrete record types now implementing
IRecordActiveCaloriesBurnedRecord, BasalBodyTemperatureRecord, BasalMetabolicRateRecord, BloodGlucoseRecord, BloodPressureRecord, BodyFatRecord, BodyTemperatureRecord, BodyWaterMassRecord, BoneMassRecord, CervicalMucusRecord, CyclingPedalingCadenceRecord, DistanceRecord, ElevationGainedRecord, ExerciseSessionRecord, FloorsClimbedRecord, HeartRateRecord, HeartRateVariabilityRmssdRecord, HeightRecord, HydrationRecord, IntermenstrualBleedingRecord, LeanBodyMassRecord, MenstruationFlowRecord, MenstruationPeriodRecord, MindfulnessSessionRecord, NutritionRecord, OvulationTestRecord, OxygenSaturationRecord, PlannedExerciseSessionRecord, PowerRecord, RespiratoryRateRecord, RestingHeartRateRecord, SexualActivityRecord, SkinTemperatureRecord, SleepSessionRecord, SpeedRecord, StepsCadenceRecord, StepsRecord, TotalCaloriesBurnedRecord, Vo2MaxRecord, WeightRecord, WheelchairPushesRecord.
Changes
Transforms/Metadata.xml: replace the threevisibility="public"attrs and theSeriesRecord/getSamples<remove-node>with the single<add-node>above. ThegetSamplesworkaround is no longer needed becauseISeriesRecordis no longer generated, so the CS0738 collision can't occur.PublicAPI.Unshipped.txt: drop theIInstantaneousRecord/IIntervalRecord/ISeriesRecordentries that will no longer be generated.IRecorditself remains. No additions are needed (see "The fix DOES add API" above).config.json: keepXamarin.AndroidX.Health.Connect.ConnectClient1.1.0.2 → 1.1.0.3..github/copilot-instructions.md: document the@PublishedApi internalpattern and the supported<add-node><implements/></add-node>fix so future changes hit the right approach first.metadata-verifypasses locally andandroidx.health.connect.connect-clientbuilds clean.