mapbox / mapbox/mapbox-maps-ios

Help needed: Threading crashes/deadlocks in SwiftUI UIViewRepresentable on iOS [18/17/16] (Mapbox Maps SDK v11)

Open
#2,407 0 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

auto-triaged bug :beetle:
Dominant language
Swift
Stars
601
Forks
196
PR merge metrics
No merged PRs in 30d

Description

Hi Mapbox team 👋,

I'm running into some frustrating threading issues and race conditions when using the Mapbox Maps SDK for iOS (v11.8) wrapped in a SwiftUI UIViewRepresentable, specifically happening on iOS 26

🛑 The Problem
We have a SwiftUI view (MapboxRouteView) that displays a route using PolylineAnnotationManager and user location using PointAnnotationManager. Our Coordinator is marked with @MainActor to safely bridge state to SwiftUI. However, we are seeing race conditions, deadlocks, or UI unresponsiveness stemming from Mapbox's closures and publishers responding on what seems to be background threads, and then deadlocking when we dispatch back to the main thread.

Specific areas causing trouble:

mapView.mapboxMap.onStyleLoaded.observeNext { ... }
mapView.mapboxMap.onMapLoadingError.observeNext { ... }
mapView.location.onLocationChange.observe { ... }
When these events fire, we sometimes update the SwiftUI state or our Mapbox annotations (annotationManager.annotations = [...]). Wrapping these updates in DispatchQueue.main.async inside the observers isn't fully solving the race conditions, especially as the user's location changes rapidly or the view disappears.

💻 Our Setup
Mapbox SDK Version: v11.8
Integration Profile: SwiftUI UIViewRepresentable

A Snippet of what we are doing:

swift
@MainActor
final class Coordinator {
private var cancellables = Set()
// ...
func bind(to mapView: MapView) {
mapView.mapboxMap.onStyleLoaded.observeNext { [weak self, weak mapView] _ in
DispatchQueue.main.async {
guard let self, let mapView else { return }
self.styleLoaded = true
// Render annotations...
}
}.store(in: &cancellables)

    mapView.location.onLocationChange.observe { [weak self, weak mapView] locations in
        DispatchQueue.main.async {
            guard let self, let mapView, let location = locations.last else { return }
            // Update point annotations...
        }
    }.store(in: &cancellables)
}

}

❓ Questions
Are Mapbox's observe and observeNext callbacks explicitly guaranteed to be called on a specific thread (e.g., main), or do they bounce to background threads?
What is the recommended best practice for safely updating Mapbox AnnotationManagers or moving the camera from within these observation callbacks when in a heavily concurrent environment like SwiftUI?
Are there any known threading/deadlock issues related to onLocationChange or onStyleLoaded specifically on iOS 26?
Any pointers to avoid these race conditions when bridging to SwiftUI would be hugely appreciated! Thank you! 🙏

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start with MapboxRouteView and the Coordinator.bind(to:) entry point, then inspect the onStyleLoaded, onMapLoadingError, and onLocationChange observers in the provided v11.8 setup. Reproduce the behavior on iOS 26 while the view appears and disappears, checking callback threading, annotation updates, and cancellation. Done means establishing the callback thread guarantees and a reliable way to avoid the reported races or deadlocks.

Written by the indexing model from the issue text.

Assessment

Tech stack
ios, swift
Domain
mobile
Issue type
Bug
Difficulty
5/5
Estimated time
Over a week
Activity status
Stale
Clarity
Needs clarification
Newbie friendliness
25/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.