A Swift SDK and a C# API, With 831 MB in Between
Count the types the HERE iOS SDK exposes through Objective-C and you get a handful, four at last count. Count what it exposes through Swift and you get the entire product: map rendering, search, routing, live traffic, several hundred classes. The iOS half of a tier-one mapping platform is a Swift-only artifact, and .NET has no native way to touch it.
This is the build story of here-sdk-for-maui, our MIT-licensed .NET MAUI bindings for HERE SDK 4.x (Explore Edition). Our claim, which a skeptical interop engineer is welcome to argue with: the hard parts have almost nothing to do with syntax and nearly everything to do with the boundary where Swift's ARC shakes hands with .NET's garbage collector. Tooling moves names across a language border in an afternoon. Custody of native memory is where the design work went.
Android takes a morning. iOS is a different problem.
A .NET MAUI developer who wants HERE's stack is asking for more than a static tile: a map view they can move with real gestures, and routing with maneuvers their operations team already knows how to read. On Android, the HERE SDK ships as an AAR, and .NET's standard binding tooling consumes that format. The job is genuinely a morning.
On iOS the same developer hits a wall of a very specific kind. Objective Sharpie, the tool that generates C# bindings for iOS libraries, reads Objective-C headers. HERE stopped publishing an Objective-C surface with SDK 4.x. For most classes, Swift symbols stay invisible to the Objective-C runtime unless a type is deliberately re-exposed with @objc. The SDK's own public header exposes four types, and none of the API anyone would want to bind. When we checked the usual registries in the spring of 2026, we could not find another maintained .NET binding for HERE SDK 4.x. Nobody was coming to solve this for us, which is how the project started.
Bridge to Swift with @objc, then bind the bridge
The pattern the .NET MAUI Community Toolkit recommends for Swift libraries has three layers. A Swift NativeBridge lives inside a framework and re-declares pieces of the SDK's public API with @objc annotations, which is what makes Xcode emit an Objective-C header for them. Objective Sharpie reads that header and generates C# binding types. On top of both sit hand-written C# wrappers that give .NET developers a surface the raw binding cannot: async methods and events, plus a real dispose story.
Two roads dead-end just before this one. The first was pointing Objective Sharpie directly at the xcframework. Sharpie generates C# from whatever a header declares. That header declares almost nothing useful. The afternoon of tooling produced a binding of near-emptiness. The second was hand-rolled P/Invoke against a minimal Swift C shim we would write and maintain ourselves. It is viable in principle, but every string, every optional, every error value, and every piece of ARC bookkeeping becomes manual work. The @objc bridge already does that marshalling correctly. We had no appetite to re-implement a solved layer badly.
Where ARC shakes hands with the garbage collector
The bridge's real job is flattening. Swift optionals become C# nullable values. Swift treats an enum with associated values as a single type that carries its payload; C# has no equivalent, so the bridge returns a tag alongside plain fields. A search response that Swift describes as "results, or nothing plus a reason" arrives in C# as a status code and an out parameter. It is representable. It is also where the wrapper's API design gets decided, because every flattening choice leaks into the C# a developer types a year later.
Native memory has one rule in this codebase: every native object surfaced in C# is owned by a wrapper that implements IDisposable, and disposing releases the native counterpart. HERE objects are heavy in a specific place. A map view and its layers hold GPU-backed handles, so a skipped dispose does not crash on schedule; it leaks until iOS's jetsam reclaims the memory for you, at a moment you did not choose. We caught our first mistakes by cycling the reference app through its tabs while watching memory in Instruments, which is a low-tech loop that found real leaks. Disposal is also what made the API idiomatic rather than merely functional, because a .NET developer expects using to mean something.
One flattening gap survives openly: tap-to-pick a map object returns null on iOS while the same call works on Android. We list it in the limits below rather than pretending a wrapper hides it. A gap in the NativeBridge is a fact about the product, and facts belong in READMEs.
What a release build does to 831 MB
The iOS xcframework for Explore Edition v4.25.5.0 weighs about 831 MB, and that figure survives a stripping pass we added to the release build. Inside sit platform and architecture slices, including simulator builds that no shipping device will ever touch. What the stripping removes is those simulator slices from release packaging. What it does not remove is the source-level cost, because a repository and its CI cache still pay for the full artifact, and every Xcode invocation pays again. Building the iOS bindings requires macOS, by the way, since Xcode is the only toolchain that produces the framework. We noticed the CI bill long before we noticed the download: 831 MB is larger than most entire apps, and every job that unpacks it pays rent for the privilege.
Two platforms, one C# surface
The design rule that shaped the whole library: if a developer has to write #if IOS to use it, the wrapper failed. Underneath, Android carries the AAR binding and iOS carries the NativeBridge. At the call site there is one API, and it is C# all the way down.
builder.UseHereSdkExplore(new HereSdkOptions
{
AccessKeyId = "YOUR_ACCESS_KEY_ID",
AccessKeySecret = "YOUR_ACCESS_KEY_SECRET"
});
<here:HereMapView x:Name="Map" MapScheme="NormalDay" />
What that surface buys, per the docs site:
- A
MapViewcontrol with camera control and gestures, across map schemes. - Text, category, and autosuggest search with place details.
- Routing across car, truck, pedestrian, bicycle, scooter, and EV profiles, with maneuvers and visual route display.
- Real-time traffic flow and incidents, with a jam-factor legend.
- Cross-platform location through
ILocationService. - Map items: markers, polylines, polygons, arrows, and 3D markers.
The limits, in public where you can test them
- No Windows or macOS Catalyst targets. The support matrix stops at Android (API 24 and up) and iOS (15.2 and up).
- No CarPlay or Android Auto in the unified API. If your product's whole point is the dashboard screen, this is a blocker, and no wrapper hides it.
- Map pick returns null on iOS today, the NativeBridge gap mentioned earlier. On Android it works. Features that need selecting map objects should stay Android-first until the gap closes.
- Location uses MAUI
Geolocationas the primary source rather than HERE's positioning stack, which keeps behavior consistent with the rest of a MAUI app. - Circles are approximated as polygons. Fine for a radius ring around a point; wrong for anything survey-grade.
- Building the iOS bindings requires a Mac and an Xcode install, plus patience with the 831 MB unpacked.
What we would do differently
Two regrets, both with lessons attached. First, we bound what the tooling produced and shaped the C# API afterward, which meant Sharpie's opinions about naming showed up in early commits and had to be worn off. The better order: write the C# surface a MAUI developer would actually type, then cut the Swift bridge to fit it. The bridge is the spec, and we learned it a release too late. Second, the release stripping arrived late, so the repository spent months paying for simulator slices in every build. Measure artifact weight on day one, not when a download scares someone.
Build it from source, then read the limits twice
One install note before the closing argument: the packages were never published to a registry, so the only supported path is building from the repository, with generated API reference on the docs site. On .NET 10, targets are net10.0-android and net10.0-ios. Underneath sits HERE SDK Explore Edition v4.25.5.0. The license is MIT, and the reference app runs straight out of the clone.
If your product needs HERE's routing and traffic and your team writes C#, the binding work described here exists and is published. What you owe it is an afternoon reading the limits above and one honest look at the disposal story in your own code. Interop syntax is a tooling problem. Memory along the border is the design.
Thanks for reading this far. If this sounds like the problem you're wrestling with, tell us about it. A principal reads every message.
Start a conversation