0

Maps (from MapBox API) were working just fine in our app, but about a month they stopped displaying on a swiftui view. BUT the tricky thing is that the maps in some phones still work (or works partially but being cut off), and they still show the annotations/pins (see screenshot).

We have the MapBox Maps SDK for iOS installed as cocoapods into Xcode.

It's a simple app, it just detects a vehicle trip and add event points to the map (eg. point A as trip start, and point B as trip end).

Versions: Xcode: 13.2.1 pod 'Mapbox-iOS-SDK', '~> 6.4.1'

pod 'MapboxNavigation', '~> 1.4.2'

mapbox map not showing

Basically, the following is our main code to show the map.

Note: It also adds speeding, hard brake and rapid accelaration annotations to the map.

Does someone have any idea why the map is not showing properly? Thanks much in advance.


import SwiftUI
import Mapbox
import MapboxDirections
import Polyline

struct TestMapDriveView: View {
    var selectedTrip: VehicleTripData // The selected trip that needs to be shown
    @Binding var speedingCount: Int? // the number of speeding events in the selected trip
    @Binding var showCallouts: Bool // Whether to show callout when a point annotation is selected
    @Binding var showInfoSheet: Bool // whether show the event details view or not (show the details when the user tap on a specific marker on the map)
    @Binding var isFullScreen: Bool // whether show full screen map or not
    @Binding var selectedEncodedPolyline: String // The encoded polyline for the trip
    
    @State var alreadyClean: Bool = false
    @State var changed: Bool = false
    @State var showSpecificPoint = false // Whether focues on a specific trip point or not
    
    let action: () -> () //  Used to toggle the full screen state variable
    
    var body: some View {
        
        VStack {
            NavigationLink(destination: EmptyView(), label: {EmptyView()})
            NavigationLink(destination: EventDetails(tripPoint: selectedTripPoint, selectedTrip: selectedTrip, typeInfo: .constant(selectedTypeInfo))
                           , isActive: $showInfoSheet, label: {EmptyView()}).navigationTitle("").navigationBarTitle("").navigationBarHidden(true)
            
            MapboxMap(selectedTrip: selectedTrip, speedingCount: $speedingCount, showCallouts: $showCallouts, showInfoSheet: $showInfoSheet, isFullScreen: $isFullScreen, selectedEncodedPolyline: $selectedEncodedPolyline, alreadyClean: $alreadyClean, changed: $changed, showSpecificPoint: $showSpecificPoint)
                .onAppear {
                    showDetails = false
                    // The reason for adding a timer here is that since showDetails is a global variable,
                    // and it is not a state variable, we canot trigger its value
                    // We need a timer so that we can check the value of the showDetails every 0.1 second
                    Timer.scheduledTimer(withTimeInterval: 0.1, repeats: true) { timer in
                        showInfoSheet = showDetails
                        if showInfoSheet {
                            isFullScreen = false
                            findTripPoint()
                            timer.invalidate()
                        }
                    }
                }
        }
    }
}

// MARK: - New Map Provided by MapBox
// MARK: - Since MapBox is designed to be implemented with the UIView, for SwiftUI, we need to transfer the UIView to View
struct MapboxMap: UIViewRepresentable {
    var selectedTrip: VehicleTripData
    @Binding var speedingCount: Int?
    @Binding var showCallouts: Bool
    @Binding var showInfoSheet: Bool
    @Binding var isFullScreen: Bool
    @Binding var selectedEncodedPolyline: String
    @Binding var alreadyClean: Bool
    @Binding var changed: Bool
    @Binding var showSpecificPoint: Bool
    
    private let mapView: MGLMapView = MGLMapView(frame: .zero, styleURL: MGLStyle.streetsStyleURL)
    
    class Coordinator: NSObject, MGLMapViewDelegate {
        var showCallouts: Bool
        
        init(showcallouts: Bool) {
            self.showCallouts = showcallouts
        }
        
        func mapView(_ mapView: MGLMapView, shapeAnnotationIsEnabled annotation: MGLShape) -> Bool {
            return annotation.title != nil && annotation.title != "Speeding"
        }
        
        // When you tap on a point on the map, whether show the callout or not
        func mapView(_ mapView: MGLMapView, annotationCanShowCallout annotation: MGLAnnotation) -> Bool {
            if let title = annotation.title {
                return (!(title == "Speeding") && showCallouts)
            }
            else {
                return false
            }
        }
        
        // MARK: - This function is used to replace the default marker icon with our icons for specific events
        func mapView(_ mapView: MGLMapView, viewFor annotation: MGLAnnotation) -> MGLAnnotationView? {
            guard annotation is MGLPointAnnotation else {
                return nil
            }
            
            guard annotation.title != nil else {return nil}
            guard annotation.title! != nil else {return nil}
            
            if annotation.title != ""{
                
                
                let identifier = annotation.title!!
                print("error \(annotation.title)")
                var image = UIImage()
                if identifier.hasPrefix("Speeding") {
                    image = UIImage(named: "Speeding Marker")!
                } else if identifier.hasPrefix("Hard"){
                    image = UIImage(named: "Hard Brake Pin")!
                } else if identifier.hasPrefix("Rapid") {
                    image = UIImage(named: "Rapid Accel Pin")!
                } else {
                    image = UIImage(named: identifier)!
                }
                
                var annotationView = mapView.dequeueReusableAnnotationView(withIdentifier: identifier)
                
                if annotationView == nil {
                    annotationView = MGLAnnotationView(annotation: annotation, reuseIdentifier: identifier as! String)
                    let imageView = UIImageView(image: image)
                    annotationView!.frame = CGRect(x: 0, y: 0, width: image.size.width, height: image.size.height)
                    annotationView?.addSubview(imageView)
                    annotationView?.centerOffset = CGVector(dx: 0, dy: -image.size.height / 2.0)
                }
                
                return annotationView
            }
            return nil
        }
        
        
        // MARK: - This function is executed when the user taps on "View Details"
        // In fact, we should set showInfoSheet to be true in order to show the EventDetailView
        // However, since we are using a coordinator here, we cannnot change showInfoSheet directly
        // Therefore, we change the global variable "showDetails" to be true
        @objc func labelAction(_ sender: UITapGestureRecognizer) {
            showDetails = true
        }
        
        // MARK: - Add the "View Details" Label in the Callout View
        // MARK: - We should use UIButtion, however, if we use UIButtion, it will never be shown. Need further tests
        func mapView(_ mapView: MGLMapView, leftCalloutAccessoryViewFor annotation: MGLAnnotation) -> UIView? {
            
            guard let title = annotation.title else {return nil}
            
            if title != nil {
                if title!.hasPrefix("Speeding:") {
                    selectedTypeInfo = "speeding"
                } else if title!.hasPrefix("Hard") {
                    selectedTypeInfo = "hardBrake"
                } else if title!.hasPrefix("Rapid") {
                    selectedTypeInfo = "rapidAccel"
                } else {
                    return nil
                }
                let labelTap = UITapGestureRecognizer(target: self, action: #selector(labelAction(_:)))
                labelTap.numberOfTapsRequired = 1
                // Callout height is fixed; width expands to fit its content.
                let label = UILabel(frame: CGRect(x: 0, y: 0, width: 60, height: 50))
                label.textAlignment = .center
                label.textColor = UIColor(named: "Blue")
                label.text = "View Details"
                label.numberOfLines = 2
                label.font = UIFont(name: "NotoSans-Regular", size: 12)
                label.isUserInteractionEnabled = true
                label.addGestureRecognizer(labelTap)
                selectedCoor = annotation.coordinate
                return label
            }
            return nil
        }
        
        // MARK: - This function is used to change the color of the polyline based on the event name
        func mapView(_ mapView: MGLMapView, strokeColorForShapeAnnotation annotation: MGLShape) -> UIColor {
            guard let title = annotation.title else {return UIColor(named: "Blue")!}
            if title.hasPrefix("Speeding") {
                return UIColor(named: "MapRed")!
            } else {
                return UIColor(named: "Blue")!
            }
        }
    }
    
    func makeCoordinator() -> MapboxMap.Coordinator {
        return Coordinator(showcallouts: showCallouts)
    }
    
    // MARK: - Initialize the MapView
    func makeUIView(context: Context) -> MGLMapView {
        mapView.autoresizingMask = [.flexibleWidth, .flexibleHeight]
        mapView.setCenter(CLLocationCoordinate2D(latitude: selectedTrip.startedLatitude!, longitude: selectedTrip.startedLongitude!), animated: true)
        mapView.delegate = context.coordinator
        
        return mapView
    }
    
    func updateUIView(_ uiView: MGLMapView, context: Context) {
        if (uiView.annotations ?? []).count == 0 {
            mapViewDidFinishLoadingMap(uiView, didFinishLoading: MGLStyle())
        }
    }
    
    func mapViewDidFinishLoadingMap(_ mapView: MGLMapView, didFinishLoading style: MGLStyle) {
        mapView.isRotateEnabled = false
        
        // MARK: - Get the result from the encoded polyline string
        let result = Polyline.init(encodedPolyline: selectedEncodedPolyline, encodedLevels: .none)
        
        // MARK: - Get the coordinates in the polyline for the trip
        let coordinates = result.coordinates
        
        guard let tripPoints = coordinates else {return}
        
        // MARK: - Get the start and end point
        let startPoint: CLLocationCoordinate2D = coordinates?.first ?? CLLocationCoordinate2D(latitude: 0, longitude: 0)
        let endPoint: CLLocationCoordinate2D = coordinates?.last ?? CLLocationCoordinate2D(latitude: 0, longitude: 0)
        
        var startTripPoint = MGLPointAnnotation()
        var endTripPoint = MGLPointAnnotation()
        
        startTripPoint.title = "Starting Point"
        startTripPoint.coordinate = startPoint
        endTripPoint.title = "End Point"
        endTripPoint.coordinate = endPoint
        mapView.addAnnotation(startTripPoint)
        mapView.addAnnotation(endTripPoint)
        
        let tryLine = MGLPolyline(coordinates: coordinates!, count: UInt(coordinates!.count))
        mapView.addAnnotation(tryLine)
        
        var speedingArray = [[TripPoint]]()
        
        DispatchQueue.global(qos: .background).async {
            // MARK: - Deal with the speeding events in the trip
            // MARK: - The speeding icon will be placed at the point which has the maximum speed for each speeding polyline
            var ctr = 0
            
            while ctr < selectedTrip.speedingPoints!.count{
                var speedPath = [TripPoint]()
                speedPath.append(selectedTrip.speedingPoints![ctr])
                while ctr + 1 < selectedTrip.speedingPoints!.count && selectedTrip.speedingPoints![ctr + 1].timeStamp!.timeIntervalSince1970 - selectedTrip.speedingPoints![ctr].timeStamp!.timeIntervalSince1970 < 10{
                    speedPath.append(selectedTrip.speedingPoints![ctr+1])
                    ctr += 1
                }
                speedingArray.append(speedPath)
                ctr += 1
            }
            
            for speedingLine in speedingArray {
                var maxDelta = 0.0
                var maxDeltaPoint:TripPoint? = nil
                var path: [CLLocationCoordinate2D] = []
                for speedingPoint in speedingLine{
                    path.append(CLLocationCoordinate2D(latitude: speedingPoint.latitude!, longitude: speedingPoint.longitude!))
                    if speedingPoint.speed_limit_delta! > maxDelta{
                        maxDelta = speedingPoint.speed_limit_delta!
                        maxDeltaPoint = speedingPoint
                        
                    }
                }
                
                if let markerPoint = maxDeltaPoint{
                    tripPointsArray.append(markerPoint)
                    var speedMarker = MGLPointAnnotation()
                    speedMarker.coordinate = CLLocationCoordinate2D(latitude: markerPoint.latitude!, longitude: markerPoint.longitude!)
                    speedMarker.title = "Speeding: \(Int(markerPoint.speed!)) " + markerPoint.speedUnit!
                    let speedLimitBR = Double(markerPoint.speed!) - markerPoint.speed_limit_delta! //before rounding
                    var speedLimit = 10 * Int((speedLimitBR / 10.0).rounded())//round to nearest 10
                    speedMarker.subtitle = "Speed Limit: ~ \(speedLimit) " + markerPoint.speedUnit!
                    
                    DispatchQueue.main.async {
                        mapView.addAnnotation(speedMarker)
                    }
                }
                
                let speedingPolyline = MGLPolyline(coordinates: path, count: UInt(path.count))
                speedingPolyline.title = "Speeding"
                DispatchQueue.main.async {
                    mapView.addAnnotation(speedingPolyline)
                }
                
            }
            speedingCount = speedingArray.count
            
            // MARK: - Deal with hard brakes in the trip
            for hardBrakePoint in selectedTrip.hardBrakePoints!{
                tripPointsArray.append(hardBrakePoint)
                let hardBrakeMarker = MGLPointAnnotation()
                hardBrakeMarker.coordinate = CLLocationCoordinate2D(latitude: hardBrakePoint.latitude!, longitude: hardBrakePoint.longitude!)
                hardBrakeMarker.title = "Hard Brake"
                hardBrakeMarker.subtitle = "Acceleration: \(String(format: "%.1f", hardBrakePoint.acceleration! * 3.6)) " + "km/h/s"
                DispatchQueue.main.async {
                    mapView.addAnnotation(hardBrakeMarker)
                }
            }
            
            
            // MARK: - Deal with rapid accel in the trip
            for rapidAccelPoint in selectedTrip.rapidAccelPoints!{
                tripPointsArray.append(rapidAccelPoint)
                let rapidAccelMarker = MGLPointAnnotation()
                rapidAccelMarker.coordinate = CLLocationCoordinate2D(latitude: rapidAccelPoint.latitude!, longitude: rapidAccelPoint.longitude!)
                rapidAccelMarker.title = "Rapid Accel"
                rapidAccelMarker.subtitle = "Acceleration: \(String(format: "%.1f", rapidAccelPoint.acceleration! * 3.6)) " + "km/h/s"
                DispatchQueue.main.async {
                    mapView.addAnnotation(rapidAccelMarker)
                }
            }
        }
        
        // MARK: - If we are not in EventDetailView, then the mapView.showAnnotations will help us to zoom the map to the proper level so that all the markers and annotations in the map will be shown
        if !showSpecificPoint {
            Timer.scheduledTimer(withTimeInterval: 0.1, repeats: false) { timer in
                guard let annotations = mapView.annotations else {return}
                mapView.showAnnotations(annotations, edgePadding: .init(top: 60, left: 40, bottom: 10, right: 40), animated: true, completionHandler: nil)
            }
            
        } else {
            // MARK: - If we need to zoom into a specific point, we need to find which point it is among all the trip points and zoom into that specific point
            var alreadyAdded = false
            var point = MGLPointAnnotation()
            
            point.coordinate = CLLocationCoordinate2D(latitude: selectedTripPoint.latitude ?? 0, longitude: selectedTripPoint.longitude ?? 0)
            
            for hardBrakePoint in selectedTrip.hardBrakePoints! {
                if hardBrakePoint.latitude == selectedTripPoint.latitude && hardBrakePoint.longitude == selectedTripPoint.longitude {
                    point.title = "Hard Brake"
                    point.subtitle = "Acceleration: \(String(format: "%.1f", hardBrakePoint.acceleration! * 3.6)) " + "km/h/s"
                    mapView.addAnnotation(point)
                    alreadyAdded = true
                    break
                }
            }
            
            if !alreadyAdded {
                for rapidAccelPoint in selectedTrip.rapidAccelPoints!{
                    
                    if rapidAccelPoint.latitude == selectedTripPoint.latitude && rapidAccelPoint.longitude == selectedTripPoint.longitude {
                        point.title = "Rapid Accel"
                        point.title! += "Acceleration: \(String(format: "%.1f", rapidAccelPoint.acceleration! * 3.6)) " + "km/h/s"
                        mapView.addAnnotation(point)
                        alreadyAdded = true
                        break
                    }
                }
            }
            
            if !alreadyAdded {
                var presentPolyLine: MGLPolyline = MGLPolyline()
                for speedingLine in speedingArray{
                    var maxDelta = 0.0
                    var maxDeltaPoint:TripPoint? = nil
                    var path: [CLLocationCoordinate2D] = []
                    for speedingPoint in speedingLine{
                        path.append(CLLocationCoordinate2D(latitude: speedingPoint.latitude!, longitude: speedingPoint.longitude!))
                        if speedingPoint.speed_limit_delta! > maxDelta{
                            maxDelta = speedingPoint.speed_limit_delta!
                            maxDeltaPoint = speedingPoint
                        }
                    }
                    
                    if let markerPoint = maxDeltaPoint{
                        if selectedTripPoint.latitude == markerPoint.latitude && selectedTripPoint.longitude == markerPoint.longitude {
                            let specificMarker = MGLPointAnnotation()
                            specificMarker.coordinate = CLLocationCoordinate2D(latitude: markerPoint.latitude ?? 0, longitude: markerPoint.longitude ?? 0)
                            var speedMarker = MGLPointAnnotation()
                            speedMarker.coordinate = CLLocationCoordinate2D(latitude: markerPoint.latitude!, longitude: markerPoint.longitude!)
                            speedMarker.title = "Speeding: \(Int(markerPoint.speed!)) " + markerPoint.speedUnit!
                            let speedLimitBR = Double(markerPoint.speed!) - markerPoint.speed_limit_delta! //before rounding
                            var speedLimit = 10 * Int((speedLimitBR / 10.0).rounded())//round to nearest 10
                            speedMarker.subtitle = "Speed Limit: ~ \(speedLimit) " + markerPoint.speedUnit!
                            point = speedMarker
                            DispatchQueue.main.async {
                                let speedingPolyline = MGLPolyline(coordinates: path, count: UInt(path.count))
                                speedingPolyline.title = "Speeding"
                                presentPolyLine = speedingPolyline
                            }
                            break
                        } else {
                            path.removeAll()
                        }
                    }
                }
                
                mapView.showAnnotations([point], edgePadding: .init(top: 60, left: 40, bottom: 10, right: 40), animated: true, completionHandler: nil)
            } else {
                mapView.showAnnotations([point], edgePadding: .init(top: -10, left: -10, bottom: -10, right: -10), animated: true, completionHandler: nil)
            }
        }
    }
}

I tried updating the pods from 6.3 to 6.4.1 but it didn't work.

I know MapBox has a new framework as of v6.4.1 called v10, but I haven't installed yet cause that would change the whole code. I am expecting to see the map as it was displaying before this new bug. eg. see screenshot mapbox map

ccmsd18
  • 58
  • 7
  • Found myself the solution. It needed a new token from mapbox. So all I had to do was login the mapbox account, create a new token and update it into the info.plist file. Cheers! – ccmsd18 Jan 18 '23 at 22:51

0 Answers0