Apple platform
iOS
Framework version
net10.0-*
Affected platform version
.NET SDK 10.0.300, iOS workload 26.5.10284, Xcode 26.5, iOS Simulator 26.5
Description
MapKit.MKDistanceFormatter throws UIKit.UIKitThreadAccessException when constructed on a background thread in .NET iOS.
This has been observed on iOS only. The same app code path does not reproduce on macOS.
The exception appears to come from the managed iOS binding, not from native MapKit. I verified this with a native Swift iOS simulator repro that constructs and uses MKDistanceFormatter on a background queue. The native repro succeeds, including when launched with Xcode's Main Thread Checker injected and MTC_CRASH_ON_REPORT=1.
The .NET binding appears to call UIApplication.EnsureUIThread() in MapKit.MKDistanceFormatter..ctor before sending the native init message. IL inspection of Microsoft.iOS.dll shows:
MapKit.MKDistanceFormatter..ctor
IL_0007: call Void EnsureUIThread()
...
IL_002a: call IntPtr IntPtr_objc_msgSend(IntPtr, IntPtr)
Version information
- Apple platform: iOS
- Target framework:
net10.0-ios
- .NET SDK:
10.0.300
- iOS workload:
26.5.10284
- Xcode:
26.5
- iOS Simulator:
26.5
Expected behavior
MKDistanceFormatter can be constructed and used on a background thread, unless Apple's native framework requires main-thread access for this type.
Actual behavior
The .NET iOS binding throws before native MKDistanceFormatter init is called:
UIKit.UIKitThreadAccessException: UIKit Consistency error: you are calling a UIKit method that can only be invoked from the UI thread.
at UIKit.UIApplication.EnsureUIThread()
at MapKit.MKDistanceFormatter..ctor()
Steps to Reproduce
Create a net10.0-ios app and run this from app startup or any UI callback:
_ = Task.Run(() =>
{
var formatter = new MapKit.MKDistanceFormatter();
var value = formatter.StringFromDistance(1234);
Console.WriteLine(value);
});
Result:
UIKit.UIKitThreadAccessException
A minimal repro project is attached.
Native Swift comparison
This native Swift iOS simulator repro succeeds on a background queue:
DispatchQueue.global(qos: .utility).async {
print("background queue; main thread =", Thread.isMainThread)
let formatter = MKDistanceFormatter()
let formatted = formatter.string(fromDistance: 1234)
print("native MKDistanceFormatter background result =", formatted)
}
Observed output on iOS Simulator 26.5:
background queue; main thread = false
native MKDistanceFormatter background result = 1,2 km
The same native app also succeeds with Xcode's Main Thread Checker injected and configured to crash on reports.
Repro project output
The attached .NET iOS repro prints:
MKDistanceFormatterBindingBugRepro.zip
Background thread: 3, main thread: False
Caught exception: UIKit.UIKitThreadAccessException
UIKit Consistency error: you are calling a UIKit method that can only be invoked from the UI thread.
at UIKit.UIApplication.EnsureUIThread() in /Users/builder/azdo/_work/1/s/macios/src/UIKit/UIApplication.cs:line 134
at MapKit.MKDistanceFormatter..ctor() in /Users/builder/azdo/_work/1/s/macios/src/build/dotnet/ios/generated-sources/MapKit/MKDistanceFormatter.g.cs:line 71
at MKDistanceFormatterBindingBugRepro.AppDelegate.RunBackgroundRepro() in /private/tmp/MKDistanceFormatterBindingBugRepro/Program.cs:line 51
Did you find any workaround?
Workarounds are to marshal MKDistanceFormatter usage to the main thread, disable UIKit cross-thread checks globally, or avoid MKDistanceFormatter and use custom distance formatting.
The first adds unnecessary main-thread work for background processing, and disabling checks globally is not desirable.
Relevant logs
No response
Apple platform
iOS
Framework version
net10.0-*
Affected platform version
.NET SDK 10.0.300, iOS workload 26.5.10284, Xcode 26.5, iOS Simulator 26.5
Description
MapKit.MKDistanceFormatterthrowsUIKit.UIKitThreadAccessExceptionwhen constructed on a background thread in .NET iOS.This has been observed on iOS only. The same app code path does not reproduce on macOS.
The exception appears to come from the managed iOS binding, not from native MapKit. I verified this with a native Swift iOS simulator repro that constructs and uses
MKDistanceFormatteron a background queue. The native repro succeeds, including when launched with Xcode's Main Thread Checker injected andMTC_CRASH_ON_REPORT=1.The .NET binding appears to call
UIApplication.EnsureUIThread()inMapKit.MKDistanceFormatter..ctorbefore sending the nativeinitmessage. IL inspection ofMicrosoft.iOS.dllshows:Version information
net10.0-ios10.0.30026.5.1028426.526.5Expected behavior
MKDistanceFormattercan be constructed and used on a background thread, unless Apple's native framework requires main-thread access for this type.Actual behavior
The .NET iOS binding throws before native
MKDistanceFormatter initis called:Steps to Reproduce
Create a
net10.0-iosapp and run this from app startup or any UI callback:Result:
A minimal repro project is attached.
Native Swift comparison
This native Swift iOS simulator repro succeeds on a background queue:
Observed output on iOS Simulator 26.5:
The same native app also succeeds with Xcode's Main Thread Checker injected and configured to crash on reports.
Repro project output
The attached .NET iOS repro prints:
MKDistanceFormatterBindingBugRepro.zip
Did you find any workaround?
Workarounds are to marshal
MKDistanceFormatterusage to the main thread, disable UIKit cross-thread checks globally, or avoidMKDistanceFormatterand use custom distance formatting.The first adds unnecessary main-thread work for background processing, and disabling checks globally is not desirable.
Relevant logs
No response