mapbox / mapbox-maps-ios

Interactive, thoroughly customizable maps for iOS powered by vector tiles and Metal
https://www.mapbox.com/mapbox-mobile-sdk
Other
475 stars 153 forks source link

Map Style does not load intermittently. #2245

Closed paulsUsername closed 1 week ago

paulsUsername commented 1 week ago

Environment

Observed behavior and steps to reproduce

Map is entirely blank intermittently and is not properly loaded (i.e. onMapLoaded is never called) but I can see annotations on the map, for example user location or added lines or markers but no tiles ever load onto the map.

While in its error state: mapView.mapboxMap.styleURI correctly returns the expected style URI. mapView.mapboxMap.isStyleLoaded returns false

Expected behavior

I expect the style to load and the map to show its map tiles.

Notes / preliminary analysis

This happens regardless of wether the style pack is downloaded for offline. I have a strong internet connect at the time so its not the issue at hand.

Additional links and references

Our style sheet is version 8.

Screenshot 2024-10-01 at 07 29 45 Device Screen shot

I am getting this earning on layers that will not load.

This is the most consistent Item I am seeing in relation to the issue!

[Warning, maps-core]: {}[General]: The outdated resource https://b.source.hiik...{I-have-hidden-this}.wu0ZqTO2 shall be updated explcitly using Offline API

This warning is not showing on styles that continue to work.

Highly suspect its something to do with this!:

Failed to load style: Cached resource is unusable

Some Code:


import MapboxMaps
import UIKit
import FittedSheets
import RevenueCat
import FirebaseAuth

class MainMapViewController: HiikerViewController, MapButtonsViewProtocol,
                         MapStylesBottomSheetProtocol {

    lazy var waypointManager = WaypointManager(viewController: self, mapView: mapView)
    lazy var liveLocatorManager = LiveLocatorManager(viewController: self, mapView: mapView)
    var mapButtonsViewController: MapButtonsViewController?
    var optionsDialogViewController: OptionsDialogViewController?

    var mapStyleViewController: MapStylesBottomSheetViewController?

    @IBOutlet var miniCoordsBarContainer: UIView!

    let viewModel = MainMapViewModel()
    lazy var mainMapCoordinator = MainMapCoordinator(
        mapView: mapView, viewController: self,
        userLocationButton: self.mapButtonsViewController!.userLocationButton()
    )

    var mapView: MapboxMaps.MapView!

    var miniCoordsBarViewController: MiniCoordsBarViewController!

    override func viewDidLoad() {
        super.viewDidLoad()
        mapView = MapView(frame: view.bounds)
        mapView.translatesAutoresizingMaskIntoConstraints = false
        view.insertSubview(mapView!, at: 0)
        NSLayoutConstraint.activate([
            mapView.topAnchor.constraint(equalTo: view.topAnchor),
            mapView.bottomAnchor.constraint(equalTo: view.bottomAnchor),
            mapView.leadingAnchor.constraint(equalTo: view.leadingAnchor),
            mapView.trailingAnchor.constraint(equalTo: view.trailingAnchor)
        ])

        mapView.mapboxMap.onMapLoaded.observe { [weak self] _ in
            guard let self = self else {return}
            self.addMapGestureRecognizers()
            self.mapView.ornaments.options.scaleBar.visibility = .visible
            self.mainMapCoordinator.restoreMap()

            self.mapView.ornaments.options.scaleBar.position = .topLeft
            self.mapView.adjustCompassPosition(margin: 40)
            self.mapView.adjustScaleBarPosition(margin: 40)
            self.mapView.addCrossHairViewController()
        }.store(in: &self.mainMapCoordinator.cancelables)
        addMapStats()

        Router.shared.saveUrl(
            url: URL(string: ApiConstants.shared.getAPIDomain() + "/\(BaseRoute.map.description)")!
        )

        viewModel.onWaypointLoaded = { [weak self] waypoint in
            guard let self = self else {return}
            let waypointModel = waypoint.toWaypoint()
            self.removeLoadingView()
            self.waypointManager.showWaypointDetail(on: self, waypoint: waypointModel)
            self.mapView?.setCenter(waypointModel.coordinates(), zoomLevel: 16, animated: true)
            self.waypointManager.waypointMapCoordinator.waypoint(addAllWaypoints: true)
        }
    }

    @objc func showWaypoint(notification: Notification) {
        DispatchQueue.main.async {
            self.addLoadingView()
            if let waypointClientId = notification.userInfo?["waypoint"] as? String {
                self.viewModel.getWaypointByIdOrClientIdQuery(waypointClientId: waypointClientId)
            }
        }
    }

    func addMapGestureRecognizers() {
        let tapGest = UITapGestureRecognizer(target: self, action: #selector(mapTapped))
        mapView.addGestureRecognizer(tapGest)
    }

    @objc func mapTapped(sender: UITapGestureRecognizer) {
        self.waypointManager.mapTapped(sender)
        removeMapStyles()
        toggleViews()
    }

    func toggleViews() {
        if self.mapButtonsViewController?.view.isHidden == true {
            self.mapButtonsViewController?.view.fadeIn()
            removeBackToMapLabel()
        } else {
            self.mapButtonsViewController?.view.fadeOut()
            addBackToMapLabel()
        }
    }

    override func viewWillAppear(_ animated: Bool) {
        super.viewWillAppear(animated)
        topLevelNavigationController?.setNavigationBarHidden(true, animated: false)
        try? mapView.restoreMapPosition()
    }

    override func viewDidDisappear(_ animated: Bool) {
        super.viewDidDisappear(animated)
        optionsDialogViewController?.dismiss(animated: false)
        self.mainMapCoordinator.addOfflinePolygons(mapStyle: .restore())
        try? mapView.saveMapViewPosition()
    }

    override func viewDidAppear(_ animated: Bool) {
        super.viewDidAppear(animated)
        topLevelNavigationController?.setNavigationBarHidden(true, animated: false)
        self.mainMapCoordinator.restoreMap()
    }

    func addMapStats() {
        miniCoordsBarViewController = MiniCoordsBarViewController.newInstance(mapView: mapView)
        let frame = miniCoordsBarContainer.frame
        miniCoordsBarViewController.view.frame = CGRect(x: 0, y: 0, width: frame.width, height: frame.height)
        miniCoordsBarContainer.addSubview(miniCoordsBarViewController.view)
    }
}
paulsUsername commented 1 week ago

Just an update on this.

The issue was on our backend. We had the incorrect cache policy for our styles. Once we fixed the cache policy the issue was resolved!

I do think it is worth updating the release notes to specify that from 11.4.0 onwards there is strict adherance around cache policy for styles jsons.