Skip to content

Avoid KVO on CALayer by introducing an Apple PlatformInterop#30861

Merged
PureWeen merged 6 commits intodotnet:inflight/currentfrom
albyrock87:avoid-kvo-on-calayer
Dec 30, 2025
Merged

Avoid KVO on CALayer by introducing an Apple PlatformInterop#30861
PureWeen merged 6 commits intodotnet:inflight/currentfrom
albyrock87:avoid-kvo-on-calayer

Conversation

@albyrock87
Copy link
Contributor

@albyrock87 albyrock87 commented Jul 26, 2025

Description of Change

On a recent macios workload, a regression caused issues with WeakReferences bringing lots of crashes in our app, all tied to KVOs.

It potentially causes an issue with hot-reload which may or may not be related.

System.ObjectDisposedException: Cannot access a disposed object.
Object name: 'Microsoft.Maui.Platform.StaticCAShapeLayer'.
   at ObjCRuntime.ThrowHelper.ThrowObjectDisposedException(Object o) in /Users/builder/azdo/_work/2/s/macios/src/ObjCRuntime/ThrowHelper.cs:line 52
   at Foundation.NSObject.get_SuperHandle() in /Users/builder/azdo/_work/2/s/macios/src/Foundation/NSObject2.cs:line 660
   at CoreAnimation.CALayer.RemoveFromSuperLayer() in /Users/builder/azdo/_work/2/s/macios/src/build/dotnet/ios/generated-sources/CoreAnimation/CALayer.g.cs:line 783
   at Microsoft.Maui.Platform.StaticCAShapeLayer.RemoveFromSuperLayer()
   at Microsoft.Maui.Platform.ContentView.RemoveContentMask()
   at Microsoft.Maui.Platform.ContentView.WillRemoveSubview(UIView uiview)
   at Foundation.NSObject.ReleaseManagedRef() in /Users/builder/azdo/_work/2/s/macios/src/Foundation/NSObject2.cs:line 458
   at Foundation.NSObject.NSObject_Disposer.Drain(NSObject ctx) in /Users/builder/azdo/_work/2/s/macios/src/Foundation/NSObject2.cs:line 1072

Given we're never unsubscribing from the KVO legacy observer, what we're doing may lead to issues.

This PR introduces an .xcframework for Apple platforms which could really help in the future also for other purposes.

I moved the observer to a Swift implementation which relies on the new KVO API which lets you skip the unsubscribe and all those managed weak-refs.

Security

For security concerns, the MAUI team should use the attached script to re-build the .xcframework.zip and commit that on my behalf like we did with Android in the past.

image

This is how the project looks like:
image

@albyrock87 albyrock87 requested a review from a team as a code owner July 26, 2025 17:59
@dotnet-policy-service dotnet-policy-service bot added the community ✨ Community Contribution label Jul 26, 2025
@PureWeen PureWeen requested a review from rolfbjarne July 26, 2025 22:30
Copy link
Member

@rolfbjarne rolfbjarne left a comment

Choose a reason for hiding this comment

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

On a recent macios workload, a regression caused issues with WeakReferences bringing lots of crashes in our app, all tied to KVOs.

What were those crashes? Was a bug report filed?

On a more general note: in my experience swizzling causes more problems than it solves, so I would recommend against it.

@albyrock87
Copy link
Contributor Author

What were those crashes? Was a bug report filed?

I don't have full proof, but this was the Sentry message in our app:

_isKVOA > Attempt to use unknown class 0x30326ede0. > objc[2293]: Attempt to use unknown class 0x30326ede0.

It wasn't related to CALayerAutosizeObserver though.

But the fact is: CALayerAutosizeObserver is never disposed from C# (or at least it's rarely disposed), because when we disconnect the handler there's nothing removing the sublayer.

So CALayerAutosizeObserver does not leak and does not crash the app only thanks to a combination of things such as:

  • holding a weak ref to a different object readonly WeakReference<CALayer> _layerReference; (not being observed)
  • not having a reference to the observed object (either weak or non-weak)

If we change any of these characteristics it starts crashing.
This is why I'm saying this works, but who knows if in the future this may cause crashes with different UIKit versions.

Being able to override the base implementation of CALayer.setBounds on the other hand provides more safety considering we don't have to deal with KVO and weak references at all.

@albyrock87 albyrock87 force-pushed the avoid-kvo-on-calayer branch from f8a56f8 to 8bac9d0 Compare October 23, 2025 15:06
@albyrock87 albyrock87 changed the base branch from main to net10.0 October 23, 2025 15:07
@albyrock87 albyrock87 force-pushed the avoid-kvo-on-calayer branch from 8bac9d0 to 0e11125 Compare October 23, 2025 17:28
@albyrock87
Copy link
Contributor Author

I've rebased this to net10.0 branch and applied the .xcframework.zip strategy mentioned above in the comments.
I think this looks sleek!

@albyrock87 albyrock87 force-pushed the avoid-kvo-on-calayer branch from e1be7b4 to 2d3cd3f Compare November 3, 2025 12:03
@albyrock87
Copy link
Contributor Author

@rolfbjarne thanks for your review, I've applied your suggestions making sure:

  • we use a maui prefix to ensure uniqueness
  • symlinks and metadata are preserved (I'm using ditto because zip --symlinks gave me issues during MAUI build)

@rolfbjarne
Copy link
Member

  • I'm using ditto because zip --symlinks gave me issues during MAUI build

Just out of curiosity: what kind of issues?

@albyrock87
Copy link
Contributor Author

  • I'm using ditto because zip --symlinks gave me issues during MAUI build

Just out of curiosity: what kind of issues?

Building the UITests host app was failing during the linker phase saying that it was unable to extract the xcframework zip contents.

I will try again and let you know the precise message.

@albyrock87
Copy link
Contributor Author

  • I'm using ditto because zip --symlinks gave me issues during MAUI build

Just out of curiosity: what kind of issues?

Building the UITests host app was failing during the linker phase saying that it was unable to extract the xcframework zip contents.

I will try again and let you know the precise message.

@rolfbjarne here's the error

12>Xamarin.Shared.targets(155,3): Error  : unzip exited with code 50: Archive:  /Users/albyrock87/github/maui-repo/maui/artifacts/bin/Core/Debug/net10.0-ios26.0/Microsoft.Maui.resources/PlatformInterop.xcframework.zip   inflating: /Users/albyrock87/github/maui-repo/maui/artifacts/obj/Controls.TestCases.HostApp/Debug/net10.0-ios/iossimulator-arm64/Microsoft.Maui.resources/PlatformInterop.xcframework/ios-arm64_x86_64-simulator/PlatformInterop.framework/_CodeSignature/CodeResources     inflating: /Users/albyrock87/github/maui-repo/maui/artifacts/obj/Controls.TestCases.HostApp/Debug/net10.0-ios/iossimulator-arm64/Microsoft.Maui.resources/PlatformInterop.xcframework/ios-arm64_x86_64-simulator/PlatformInterop.framework/_CodeSignature/CodeDirectory     inflating: /Users/albyrock87/github/maui-repo/maui/artifacts/obj/Controls.TestCases.HostApp/Debug/net10.0-ios/iossimulator-arm64/Microsoft.Maui.resources/PlatformInterop.xcframework/ios-arm64_x86_64-simulator/PlatformInterop.framework/_CodeSignature/CodeRequirements-1    extracting: /Users/albyrock87/g...

@rolfbjarne
Copy link
Member

unzip exited with code 50

According to man unzip, that's because:

the disk is (or was) full during extraction.

Strange, but in any case ditto works too, so no real problem here I think.

Copy link
Member

@rolfbjarne rolfbjarne left a comment

Choose a reason for hiding this comment

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

This looks much better, and no swizzling makes me happy :)

Just a few minor points now

@PureWeen PureWeen added this to the .NET 10.0 SR3 milestone Dec 2, 2025
@github-project-automation github-project-automation bot moved this to Changes Requested in MAUI SDK Ongoing Dec 2, 2025
@PureWeen PureWeen moved this from Changes Requested to Ready To Review in MAUI SDK Ongoing Dec 2, 2025
@github-actions
Copy link
Contributor

github-actions bot commented Dec 10, 2025

🚀 Dogfood this PR with:

⚠️ WARNING: Do not do this without first carefully reviewing the code of this PR to satisfy yourself it is safe.

curl -fsSL https://raw.githubusercontent.com/dotnet/maui/main/eng/scripts/get-maui-pr.sh | bash -s -- 30861

Or

  • Run remotely in PowerShell:
iex "& { $(irm https://raw.githubusercontent.com/dotnet/maui/main/eng/scripts/get-maui-pr.ps1) } 30861"

@github-project-automation github-project-automation bot moved this from Ready To Review to Approved in MAUI SDK Ongoing Dec 10, 2025
@PureWeen
Copy link
Member

/azp run

@azure-pipelines
Copy link

Azure Pipelines successfully started running 3 pipeline(s).

@PureWeen
Copy link
Member

/rebase

@PureWeen PureWeen changed the base branch from main to inflight/current December 30, 2025 14:35
@PureWeen PureWeen merged commit 2e4d3dc into dotnet:inflight/current Dec 30, 2025
159 of 161 checks passed
@github-project-automation github-project-automation bot moved this from Approved to Done in MAUI SDK Ongoing Dec 30, 2025
@albyrock87 albyrock87 deleted the avoid-kvo-on-calayer branch December 30, 2025 15:06
github-actions bot pushed a commit that referenced this pull request Dec 30, 2025
### Description of Change

On a recent `macios` workload, a regression caused issues with
`WeakReference`s bringing lots of crashes in our app, all tied to
`KVO`s.

It potentially causes an issue with hot-reload which may or may not be
related.
```
System.ObjectDisposedException: Cannot access a disposed object.
Object name: 'Microsoft.Maui.Platform.StaticCAShapeLayer'.
   at ObjCRuntime.ThrowHelper.ThrowObjectDisposedException(Object o) in /Users/builder/azdo/_work/2/s/macios/src/ObjCRuntime/ThrowHelper.cs:line 52
   at Foundation.NSObject.get_SuperHandle() in /Users/builder/azdo/_work/2/s/macios/src/Foundation/NSObject2.cs:line 660
   at CoreAnimation.CALayer.RemoveFromSuperLayer() in /Users/builder/azdo/_work/2/s/macios/src/build/dotnet/ios/generated-sources/CoreAnimation/CALayer.g.cs:line 783
   at Microsoft.Maui.Platform.StaticCAShapeLayer.RemoveFromSuperLayer()
   at Microsoft.Maui.Platform.ContentView.RemoveContentMask()
   at Microsoft.Maui.Platform.ContentView.WillRemoveSubview(UIView uiview)
   at Foundation.NSObject.ReleaseManagedRef() in /Users/builder/azdo/_work/2/s/macios/src/Foundation/NSObject2.cs:line 458
   at Foundation.NSObject.NSObject_Disposer.Drain(NSObject ctx) in /Users/builder/azdo/_work/2/s/macios/src/Foundation/NSObject2.cs:line 1072
```

Given we're never unsubscribing from the KVO legacy observer, what we're
doing may lead to issues.

This PR introduces an `.xcframework` for Apple platforms which could
really help in the future also for other purposes.

I moved the observer to a `Swift` implementation which relies on the new
KVO API which lets you skip the unsubscribe and all those managed
weak-refs.

### Security

For security concerns, the MAUI team should use the attached script to
re-build the `.xcframework.zip` and commit that on my behalf like we did
with Android in the past.

<img width="759" height="62" alt="image"
src="https://github.com/user-attachments/assets/bd372451-9e39-4a24-9d59-b0dbdadd5258"
/>

This is how the project looks like:
<img width="410" height="406" alt="image"
src="https://github.com/user-attachments/assets/7be12553-40ec-4afc-a88e-3109c5c0eb2e"
/>

---------

Co-authored-by: Rolf Bjarne Kvinge <rolf@xamarin.com>
PureWeen pushed a commit that referenced this pull request Jan 5, 2026
### Description of Change

On a recent `macios` workload, a regression caused issues with
`WeakReference`s bringing lots of crashes in our app, all tied to
`KVO`s.

It potentially causes an issue with hot-reload which may or may not be
related.
```
System.ObjectDisposedException: Cannot access a disposed object.
Object name: 'Microsoft.Maui.Platform.StaticCAShapeLayer'.
   at ObjCRuntime.ThrowHelper.ThrowObjectDisposedException(Object o) in /Users/builder/azdo/_work/2/s/macios/src/ObjCRuntime/ThrowHelper.cs:line 52
   at Foundation.NSObject.get_SuperHandle() in /Users/builder/azdo/_work/2/s/macios/src/Foundation/NSObject2.cs:line 660
   at CoreAnimation.CALayer.RemoveFromSuperLayer() in /Users/builder/azdo/_work/2/s/macios/src/build/dotnet/ios/generated-sources/CoreAnimation/CALayer.g.cs:line 783
   at Microsoft.Maui.Platform.StaticCAShapeLayer.RemoveFromSuperLayer()
   at Microsoft.Maui.Platform.ContentView.RemoveContentMask()
   at Microsoft.Maui.Platform.ContentView.WillRemoveSubview(UIView uiview)
   at Foundation.NSObject.ReleaseManagedRef() in /Users/builder/azdo/_work/2/s/macios/src/Foundation/NSObject2.cs:line 458
   at Foundation.NSObject.NSObject_Disposer.Drain(NSObject ctx) in /Users/builder/azdo/_work/2/s/macios/src/Foundation/NSObject2.cs:line 1072
```

Given we're never unsubscribing from the KVO legacy observer, what we're
doing may lead to issues.

This PR introduces an `.xcframework` for Apple platforms which could
really help in the future also for other purposes.

I moved the observer to a `Swift` implementation which relies on the new
KVO API which lets you skip the unsubscribe and all those managed
weak-refs.

### Security

For security concerns, the MAUI team should use the attached script to
re-build the `.xcframework.zip` and commit that on my behalf like we did
with Android in the past.

<img width="759" height="62" alt="image"
src="https://github.com/user-attachments/assets/bd372451-9e39-4a24-9d59-b0dbdadd5258"
/>

This is how the project looks like:
<img width="410" height="406" alt="image"
src="https://github.com/user-attachments/assets/7be12553-40ec-4afc-a88e-3109c5c0eb2e"
/>

---------

Co-authored-by: Rolf Bjarne Kvinge <rolf@xamarin.com>
@PureWeen PureWeen mentioned this pull request Jan 7, 2026
PureWeen pushed a commit that referenced this pull request Jan 9, 2026
### Description of Change

On a recent `macios` workload, a regression caused issues with
`WeakReference`s bringing lots of crashes in our app, all tied to
`KVO`s.

It potentially causes an issue with hot-reload which may or may not be
related.
```
System.ObjectDisposedException: Cannot access a disposed object.
Object name: 'Microsoft.Maui.Platform.StaticCAShapeLayer'.
   at ObjCRuntime.ThrowHelper.ThrowObjectDisposedException(Object o) in /Users/builder/azdo/_work/2/s/macios/src/ObjCRuntime/ThrowHelper.cs:line 52
   at Foundation.NSObject.get_SuperHandle() in /Users/builder/azdo/_work/2/s/macios/src/Foundation/NSObject2.cs:line 660
   at CoreAnimation.CALayer.RemoveFromSuperLayer() in /Users/builder/azdo/_work/2/s/macios/src/build/dotnet/ios/generated-sources/CoreAnimation/CALayer.g.cs:line 783
   at Microsoft.Maui.Platform.StaticCAShapeLayer.RemoveFromSuperLayer()
   at Microsoft.Maui.Platform.ContentView.RemoveContentMask()
   at Microsoft.Maui.Platform.ContentView.WillRemoveSubview(UIView uiview)
   at Foundation.NSObject.ReleaseManagedRef() in /Users/builder/azdo/_work/2/s/macios/src/Foundation/NSObject2.cs:line 458
   at Foundation.NSObject.NSObject_Disposer.Drain(NSObject ctx) in /Users/builder/azdo/_work/2/s/macios/src/Foundation/NSObject2.cs:line 1072
```

Given we're never unsubscribing from the KVO legacy observer, what we're
doing may lead to issues.

This PR introduces an `.xcframework` for Apple platforms which could
really help in the future also for other purposes.

I moved the observer to a `Swift` implementation which relies on the new
KVO API which lets you skip the unsubscribe and all those managed
weak-refs.

### Security

For security concerns, the MAUI team should use the attached script to
re-build the `.xcframework.zip` and commit that on my behalf like we did
with Android in the past.

<img width="759" height="62" alt="image"
src="https://github.com/user-attachments/assets/bd372451-9e39-4a24-9d59-b0dbdadd5258"
/>

This is how the project looks like:
<img width="410" height="406" alt="image"
src="https://github.com/user-attachments/assets/7be12553-40ec-4afc-a88e-3109c5c0eb2e"
/>

---------

Co-authored-by: Rolf Bjarne Kvinge <rolf@xamarin.com>
PureWeen pushed a commit that referenced this pull request Jan 9, 2026
### Description of Change

On a recent `macios` workload, a regression caused issues with
`WeakReference`s bringing lots of crashes in our app, all tied to
`KVO`s.

It potentially causes an issue with hot-reload which may or may not be
related.
```
System.ObjectDisposedException: Cannot access a disposed object.
Object name: 'Microsoft.Maui.Platform.StaticCAShapeLayer'.
   at ObjCRuntime.ThrowHelper.ThrowObjectDisposedException(Object o) in /Users/builder/azdo/_work/2/s/macios/src/ObjCRuntime/ThrowHelper.cs:line 52
   at Foundation.NSObject.get_SuperHandle() in /Users/builder/azdo/_work/2/s/macios/src/Foundation/NSObject2.cs:line 660
   at CoreAnimation.CALayer.RemoveFromSuperLayer() in /Users/builder/azdo/_work/2/s/macios/src/build/dotnet/ios/generated-sources/CoreAnimation/CALayer.g.cs:line 783
   at Microsoft.Maui.Platform.StaticCAShapeLayer.RemoveFromSuperLayer()
   at Microsoft.Maui.Platform.ContentView.RemoveContentMask()
   at Microsoft.Maui.Platform.ContentView.WillRemoveSubview(UIView uiview)
   at Foundation.NSObject.ReleaseManagedRef() in /Users/builder/azdo/_work/2/s/macios/src/Foundation/NSObject2.cs:line 458
   at Foundation.NSObject.NSObject_Disposer.Drain(NSObject ctx) in /Users/builder/azdo/_work/2/s/macios/src/Foundation/NSObject2.cs:line 1072
```

Given we're never unsubscribing from the KVO legacy observer, what we're
doing may lead to issues.

This PR introduces an `.xcframework` for Apple platforms which could
really help in the future also for other purposes.

I moved the observer to a `Swift` implementation which relies on the new
KVO API which lets you skip the unsubscribe and all those managed
weak-refs.

### Security

For security concerns, the MAUI team should use the attached script to
re-build the `.xcframework.zip` and commit that on my behalf like we did
with Android in the past.

<img width="759" height="62" alt="image"
src="https://github.com/user-attachments/assets/bd372451-9e39-4a24-9d59-b0dbdadd5258"
/>

This is how the project looks like:
<img width="410" height="406" alt="image"
src="https://github.com/user-attachments/assets/7be12553-40ec-4afc-a88e-3109c5c0eb2e"
/>

---------

Co-authored-by: Rolf Bjarne Kvinge <rolf@xamarin.com>
PureWeen pushed a commit that referenced this pull request Jan 9, 2026
### Description of Change

On a recent `macios` workload, a regression caused issues with
`WeakReference`s bringing lots of crashes in our app, all tied to
`KVO`s.

It potentially causes an issue with hot-reload which may or may not be
related.
```
System.ObjectDisposedException: Cannot access a disposed object.
Object name: 'Microsoft.Maui.Platform.StaticCAShapeLayer'.
   at ObjCRuntime.ThrowHelper.ThrowObjectDisposedException(Object o) in /Users/builder/azdo/_work/2/s/macios/src/ObjCRuntime/ThrowHelper.cs:line 52
   at Foundation.NSObject.get_SuperHandle() in /Users/builder/azdo/_work/2/s/macios/src/Foundation/NSObject2.cs:line 660
   at CoreAnimation.CALayer.RemoveFromSuperLayer() in /Users/builder/azdo/_work/2/s/macios/src/build/dotnet/ios/generated-sources/CoreAnimation/CALayer.g.cs:line 783
   at Microsoft.Maui.Platform.StaticCAShapeLayer.RemoveFromSuperLayer()
   at Microsoft.Maui.Platform.ContentView.RemoveContentMask()
   at Microsoft.Maui.Platform.ContentView.WillRemoveSubview(UIView uiview)
   at Foundation.NSObject.ReleaseManagedRef() in /Users/builder/azdo/_work/2/s/macios/src/Foundation/NSObject2.cs:line 458
   at Foundation.NSObject.NSObject_Disposer.Drain(NSObject ctx) in /Users/builder/azdo/_work/2/s/macios/src/Foundation/NSObject2.cs:line 1072
```

Given we're never unsubscribing from the KVO legacy observer, what we're
doing may lead to issues.

This PR introduces an `.xcframework` for Apple platforms which could
really help in the future also for other purposes.

I moved the observer to a `Swift` implementation which relies on the new
KVO API which lets you skip the unsubscribe and all those managed
weak-refs.

### Security

For security concerns, the MAUI team should use the attached script to
re-build the `.xcframework.zip` and commit that on my behalf like we did
with Android in the past.

<img width="759" height="62" alt="image"
src="https://github.com/user-attachments/assets/bd372451-9e39-4a24-9d59-b0dbdadd5258"
/>

This is how the project looks like:
<img width="410" height="406" alt="image"
src="https://github.com/user-attachments/assets/7be12553-40ec-4afc-a88e-3109c5c0eb2e"
/>

---------

Co-authored-by: Rolf Bjarne Kvinge <rolf@xamarin.com>
PureWeen pushed a commit that referenced this pull request Jan 13, 2026
### Description of Change

On a recent `macios` workload, a regression caused issues with
`WeakReference`s bringing lots of crashes in our app, all tied to
`KVO`s.

It potentially causes an issue with hot-reload which may or may not be
related.
```
System.ObjectDisposedException: Cannot access a disposed object.
Object name: 'Microsoft.Maui.Platform.StaticCAShapeLayer'.
   at ObjCRuntime.ThrowHelper.ThrowObjectDisposedException(Object o) in /Users/builder/azdo/_work/2/s/macios/src/ObjCRuntime/ThrowHelper.cs:line 52
   at Foundation.NSObject.get_SuperHandle() in /Users/builder/azdo/_work/2/s/macios/src/Foundation/NSObject2.cs:line 660
   at CoreAnimation.CALayer.RemoveFromSuperLayer() in /Users/builder/azdo/_work/2/s/macios/src/build/dotnet/ios/generated-sources/CoreAnimation/CALayer.g.cs:line 783
   at Microsoft.Maui.Platform.StaticCAShapeLayer.RemoveFromSuperLayer()
   at Microsoft.Maui.Platform.ContentView.RemoveContentMask()
   at Microsoft.Maui.Platform.ContentView.WillRemoveSubview(UIView uiview)
   at Foundation.NSObject.ReleaseManagedRef() in /Users/builder/azdo/_work/2/s/macios/src/Foundation/NSObject2.cs:line 458
   at Foundation.NSObject.NSObject_Disposer.Drain(NSObject ctx) in /Users/builder/azdo/_work/2/s/macios/src/Foundation/NSObject2.cs:line 1072
```

Given we're never unsubscribing from the KVO legacy observer, what we're
doing may lead to issues.

This PR introduces an `.xcframework` for Apple platforms which could
really help in the future also for other purposes.

I moved the observer to a `Swift` implementation which relies on the new
KVO API which lets you skip the unsubscribe and all those managed
weak-refs.

### Security

For security concerns, the MAUI team should use the attached script to
re-build the `.xcframework.zip` and commit that on my behalf like we did
with Android in the past.

<img width="759" height="62" alt="image"
src="https://github.com/user-attachments/assets/bd372451-9e39-4a24-9d59-b0dbdadd5258"
/>

This is how the project looks like:
<img width="410" height="406" alt="image"
src="https://github.com/user-attachments/assets/7be12553-40ec-4afc-a88e-3109c5c0eb2e"
/>

---------

Co-authored-by: Rolf Bjarne Kvinge <rolf@xamarin.com>
PureWeen pushed a commit that referenced this pull request Jan 13, 2026
### Description of Change

On a recent `macios` workload, a regression caused issues with
`WeakReference`s bringing lots of crashes in our app, all tied to
`KVO`s.

It potentially causes an issue with hot-reload which may or may not be
related.
```
System.ObjectDisposedException: Cannot access a disposed object.
Object name: 'Microsoft.Maui.Platform.StaticCAShapeLayer'.
   at ObjCRuntime.ThrowHelper.ThrowObjectDisposedException(Object o) in /Users/builder/azdo/_work/2/s/macios/src/ObjCRuntime/ThrowHelper.cs:line 52
   at Foundation.NSObject.get_SuperHandle() in /Users/builder/azdo/_work/2/s/macios/src/Foundation/NSObject2.cs:line 660
   at CoreAnimation.CALayer.RemoveFromSuperLayer() in /Users/builder/azdo/_work/2/s/macios/src/build/dotnet/ios/generated-sources/CoreAnimation/CALayer.g.cs:line 783
   at Microsoft.Maui.Platform.StaticCAShapeLayer.RemoveFromSuperLayer()
   at Microsoft.Maui.Platform.ContentView.RemoveContentMask()
   at Microsoft.Maui.Platform.ContentView.WillRemoveSubview(UIView uiview)
   at Foundation.NSObject.ReleaseManagedRef() in /Users/builder/azdo/_work/2/s/macios/src/Foundation/NSObject2.cs:line 458
   at Foundation.NSObject.NSObject_Disposer.Drain(NSObject ctx) in /Users/builder/azdo/_work/2/s/macios/src/Foundation/NSObject2.cs:line 1072
```

Given we're never unsubscribing from the KVO legacy observer, what we're
doing may lead to issues.

This PR introduces an `.xcframework` for Apple platforms which could
really help in the future also for other purposes.

I moved the observer to a `Swift` implementation which relies on the new
KVO API which lets you skip the unsubscribe and all those managed
weak-refs.

### Security

For security concerns, the MAUI team should use the attached script to
re-build the `.xcframework.zip` and commit that on my behalf like we did
with Android in the past.

<img width="759" height="62" alt="image"
src="https://github.com/user-attachments/assets/bd372451-9e39-4a24-9d59-b0dbdadd5258"
/>

This is how the project looks like:
<img width="410" height="406" alt="image"
src="https://github.com/user-attachments/assets/7be12553-40ec-4afc-a88e-3109c5c0eb2e"
/>

---------

Co-authored-by: Rolf Bjarne Kvinge <rolf@xamarin.com>
PureWeen added a commit that referenced this pull request Jan 13, 2026
## What's Coming

.NET MAUI inflight/candidate introduces significant improvements across
all platforms with focus on quality, performance, and developer
experience. This release includes 27 commits with various improvements,
bug fixes, and enhancements.

## CollectionView
- [iOS][CV2] Fix page can be dragged down, and it would cause an extra
space between Header and EmptyView text by @devanathan-vaithiyanathan in
#31840
  <details>
  <summary>🔧 Fixes</summary>

- [I8_Header_and_Footer_Null - The page can be dragged down, and it
would cause an extra space between Header and EmptyView
text.](#31465)
  </details>

- [iOS] Fixed the Items not displayed properly in CarouselView2 by
@Ahamed-Ali in #31336
  <details>
  <summary>🔧 Fixes</summary>

- [[iOS] Items are not updated properly in
CarouselView2.](#31148)
  </details>

## Docs
- Improve Controls Core API docs by @jfversluis in
#33240

## Editor
- [iOS] Fixed an issue where an Editor with a small height inside a
ScrollView would cause the entire page to scroll by
@Tamilarasan-Paranthaman in #27948
  <details>
  <summary>🔧 Fixes</summary>

- [[iOS][Editor] An Editor that has not enough height and resides inside
a ScrollView/CollectionView will scroll the entire
page](#27750)
  </details>

## Image
- [Android] Image control crashes on Android when image width exceeds
height by @KarthikRajaKalaimani in
#33045
  <details>
  <summary>🔧 Fixes</summary>

- [Image control crashes on Android when image width exceeds
height](#32869)
  </details>

## Mediapicker
- [Android 🤖] Add a log telling why the request is cancelled by @pictos
in #33295
  <details>
  <summary>🔧 Fixes</summary>

- [MediaPicker.PickPhotosAsync throwing TaskCancelledException in
net10-android](#33283)
  </details>

## Navigation
- [Android] Fix for App Hang When PopModalAsync Is Called Immediately
After PushModalAsync with Task.Yield() by @BagavathiPerumal in
#32479
  <details>
  <summary>🔧 Fixes</summary>

- [App hangs if PopModalAsync is called after PushModalAsync with single
await Task.Yield()](#32310)
  </details>

- [iOS 26] Navigation hangs after rapidly open and closing new page
using Navigation.PushAsync - fix by @kubaflo in
#32456
  <details>
  <summary>🔧 Fixes</summary>

- [[iOS 26] Navigation hangs after rapidly open and closing new page
using Navigation.PushAsync](#32425)
  </details>

## Pages
- [iOS] Fix ContentPage BackgroundImageSource not working by
@Shalini-Ashokan in #33297
  <details>
  <summary>🔧 Fixes</summary>

- [.Net MAUI- Page.BackgroundImageSource not working for
iOS](#21594)
  </details>

## RadioButton
- [Issue-Resolver] Fix #33264 - RadioButtonGroup not working with
Collection View by @kubaflo in #33343
  <details>
  <summary>🔧 Fixes</summary>

- [RadioButtonGroup not working with
CollectionView](#33264)
  </details>

## SafeArea
- [Android] Fixed Label Overlapped by Android Status Bar When Using
SafeAreaEdges="Container" in .NET MAUI by @NirmalKumarYuvaraj in
#33285
  <details>
  <summary>🔧 Fixes</summary>

- [SafeAreaEdges works correctly only on the first tab in Shell. Other
tabs have content colliding with the display cutout in the landscape
mode.](#33034)
- [Label Overlapped by Android Status Bar When Using
SafeAreaEdges="Container" in .NET
MAUI](#32941)
- [[MAUI 10] Layout breaks on first navigation (Shell // route) until
soft keyboard appears/disappears (Android +
iOS)](#33038)
  </details>

## ScrollView
- [Windows, Android] Fix ScrollView Content Not Removed When Set to Null
by @devanathan-vaithiyanathan in
#33069
  <details>
  <summary>🔧 Fixes</summary>

- [[Windows, Android] ScrollView Content Not Removed When Set to
Null](#33067)
  </details>

## Searchbar
- Fix Android crash when changing shared Drawable tint on Searchbar by
@tritter in #33071
  <details>
  <summary>🔧 Fixes</summary>

- [[Android] Crash on changing Tint of
Searchbar](#33070)
  </details>

## Shell
- [iOS] - Fix Custom FlyoutIcon from Being Overridden to Default Color
in Shell by @prakashKannanSf3972 in
#27580
  <details>
  <summary>🔧 Fixes</summary>

- [Change the flyout icon
color](#6738)
  </details>

- [iOS] Fix Shell NavBarIsVisible updates when switching ShellContent by
@Vignesh-SF3580 in #33195
  <details>
  <summary>🔧 Fixes</summary>

- [[iOS] Shell NavBarIsVisible is not updated when changing
ShellContent](#33191)
  </details>

## Slider
- [C] Fix Slider and Stepper property order independence by
@StephaneDelcroix in #32939
  <details>
  <summary>🔧 Fixes</summary>

- [Slider Binding Initialization Order Causes Incorrect Value Assignment
in XAML](#32903)
- [Slider is very broken, Value is a mess when setting
Minimum](#14472)
- [Slider is buggy depending on order of
properties](#18910)
- [Stepper Value is incorrectly clamped to default min/max when using
bindableproperties in MVVM
pattern](#12243)
- [[Issue-Resolver] Fix #32903 - Sliderbinding initialization order
issue](#32907)
  </details>

## Stepper
- [Windows] Maui Stepper: Clamp minimum and maximum value by @OomJan in
#33275
  <details>
  <summary>🔧 Fixes</summary>

- [[Windows] Maui Stepper is not clamped to minimum or maximum
internally](#33274)
  </details>

- [iOS] Fixed the UIStepper Value from being clamped based on old higher
MinimumValue - Candidate PR test failure fix- 33363 by @Ahamed-Ali in
#33392

## TabbedPage
- [windows] Fixed Rapid change of selected tab results in crash. by
@praveenkumarkarunanithi in #33113
  <details>
  <summary>🔧 Fixes</summary>

- [Rapid change of selected tab results in crash on
Windows.](#32824)
  </details>

## Titlebar
- [Mac] Fix TitleBar Content Overlapping with Traffic Light Buttons on
Latest macOS Version by @devanathan-vaithiyanathan in
#33157
  <details>
  <summary>🔧 Fixes</summary>

- [TitleBar Content Overlapping with Traffic Light Buttons on Latest
macOS Version](#33136)
  </details>

## Xaml
- Fix for Control does not update from binding anymore after
MultiBinding.ConvertBack is called by @BagavathiPerumal in
#33128
  <details>
  <summary>🔧 Fixes</summary>

- [Control does not update from binding anymore after
MultiBinding.ConvertBack is
called](#24969)
- [The issue with the MultiBinding converter with two way binding mode
does not work properly when changing the
values.](#20382)
  </details>


<details>
<summary>🔧 Infrastructure (1)</summary>

- Avoid KVO on CALayer by introducing an Apple PlatformInterop by
@albyrock87 in #30861

</details>

<details>
<summary>🧪 Testing (2)</summary>

- [Testing] Enable UITest Issue18193 on MacCatalyst by @NafeelaNazhir in
#31653
  <details>
  <summary>🔧 Fixes</summary>

- [Test Issue18193 was disabled on Mac
Catalyst](#27206)
  </details>
- Set the CV2 handlers as the default by @Ahamed-Ali in
#33177

</details>

<details>
<summary>📦 Other (3)</summary>

- Update WindowsAppSDK to 1.8 by @mattleibow in
#32174
  <details>
  <summary>🔧 Fixes</summary>

- [Update to WindowsAppSDK](#30858)
  </details>
- Fix command dependency reentrancy by @simonrozsival in
#33129
- Fix SafeArea AdjustPan handling and add AdjustNothing mode tests by
@PureWeen via @Copilot in #33354

</details>
**Full Changelog**:
main...inflight/candidate
kubaflo pushed a commit to kubaflo/maui that referenced this pull request Jan 16, 2026
…30861)

### Description of Change

On a recent `macios` workload, a regression caused issues with
`WeakReference`s bringing lots of crashes in our app, all tied to
`KVO`s.

It potentially causes an issue with hot-reload which may or may not be
related.
```
System.ObjectDisposedException: Cannot access a disposed object.
Object name: 'Microsoft.Maui.Platform.StaticCAShapeLayer'.
   at ObjCRuntime.ThrowHelper.ThrowObjectDisposedException(Object o) in /Users/builder/azdo/_work/2/s/macios/src/ObjCRuntime/ThrowHelper.cs:line 52
   at Foundation.NSObject.get_SuperHandle() in /Users/builder/azdo/_work/2/s/macios/src/Foundation/NSObject2.cs:line 660
   at CoreAnimation.CALayer.RemoveFromSuperLayer() in /Users/builder/azdo/_work/2/s/macios/src/build/dotnet/ios/generated-sources/CoreAnimation/CALayer.g.cs:line 783
   at Microsoft.Maui.Platform.StaticCAShapeLayer.RemoveFromSuperLayer()
   at Microsoft.Maui.Platform.ContentView.RemoveContentMask()
   at Microsoft.Maui.Platform.ContentView.WillRemoveSubview(UIView uiview)
   at Foundation.NSObject.ReleaseManagedRef() in /Users/builder/azdo/_work/2/s/macios/src/Foundation/NSObject2.cs:line 458
   at Foundation.NSObject.NSObject_Disposer.Drain(NSObject ctx) in /Users/builder/azdo/_work/2/s/macios/src/Foundation/NSObject2.cs:line 1072
```

Given we're never unsubscribing from the KVO legacy observer, what we're
doing may lead to issues.

This PR introduces an `.xcframework` for Apple platforms which could
really help in the future also for other purposes.

I moved the observer to a `Swift` implementation which relies on the new
KVO API which lets you skip the unsubscribe and all those managed
weak-refs.

### Security

For security concerns, the MAUI team should use the attached script to
re-build the `.xcframework.zip` and commit that on my behalf like we did
with Android in the past.

<img width="759" height="62" alt="image"
src="https://github.com/user-attachments/assets/bd372451-9e39-4a24-9d59-b0dbdadd5258"
/>

This is how the project looks like:
<img width="410" height="406" alt="image"
src="https://github.com/user-attachments/assets/7be12553-40ec-4afc-a88e-3109c5c0eb2e"
/>

---------

Co-authored-by: Rolf Bjarne Kvinge <rolf@xamarin.com>
@github-actions github-actions bot locked and limited conversation to collaborators Jan 30, 2026
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

community ✨ Community Contribution

Projects

Status: Done

Development

Successfully merging this pull request may close these issues.

4 participants