Home > Enterprise >  Swift - Receiving Null Island Error In Console
Swift - Receiving Null Island Error In Console

Time:01-07

I am displaying a map in a view showing a pin location of an address.

Here's what I've got:

import Combine
import CoreLocation
import MapKit
import SwiftUI

struct MapViewLocation: Identifiable {
    let id = UUID()
    var coordinate: CLLocationCoordinate2D
}

struct MapViewSmall: View {
    @State var address: String
    @State private var mapCoordinate: CLLocationCoordinate2D? = nil
    @State private var mapIsLoading: Bool = true
    @State private var mapLocations: [MapViewLocation] = []
    @State private var mapRegion: MKCoordinateRegion = MKCoordinateRegion()
    @State private var mapSuccess: Bool = false

    var body: some View {
        ZStack {
            if mapIsLoading {
                ProgressView()
                    .progressViewStyle(CircularProgressViewStyle())
                    .scaleEffect(0.8)
            } else {
                if self.mapSuccess {
                    Map(
                        coordinateRegion: $mapRegion,
                        interactionModes: [.pan],
                        annotationItems: mapLocations,
                        annotationContent: { location in
                            MapPin(
                                coordinate: self.mapCoordinate ?? CLLocationCoordinate2D(),
                                tint: .red
                            )
                        }
                    )
                    .frame(width: 100, height: 100)
                    .cornerRadius(9)
                    .onTapGesture {
                        let request = MKLocalSearch.Request()
                        request.naturalLanguageQuery = self.address
                        let search = MKLocalSearch(request: request)
                        search.start { response, error in
                            if let response = response {
                                let mapItem = MKMapItem(placemark: response.mapItems[0].placemark)
                                mapItem.name = self.address
                                mapItem.openInMaps(launchOptions: [MKLaunchOptionsDirectionsModeKey : MKLaunchOptionsDirectionsModeDriving])
                            }
                        }
                    }
                } else {
                    Image(systemName: "mappin.slash.circle")
                        .resizable()
                        .aspectRatio(contentMode: .fit)
                        .frame(width: 100, height: 100)
                        .foregroundColor(.red)
                }
            }
        }
        .onAppear {
            self.mapIsLoading = true
            Task {
                await self.makeMap()
            }
        }

    }

func getLocation(address: String, completion: @escaping (_ location: CLLocationCoordinate2D?) -> Void) {
        let geocoder = CLGeocoder()
        geocoder.geocodeAddressString(address) { (placemarks, error) in
            if let error = error {
                print(error.localizedDescription)
                completion(nil)
                return
            }
            guard let placemarks = placemarks,
            let location = placemarks.first?.location?.coordinate else {
                completion(nil)
                return
            }
            completion(location)
        } 
    }

    func makeMap() async {

        // Convert the address
        self.getLocation(address: self.address) { location in

            // Handle Bad Address Errors
            if location == nil {
                self.mapSuccess = false
                self.mapIsLoading = false
                return
            }

            self.mapCoordinate = CLLocationCoordinate2D(
                                    latitude: location?.latitude ?? 0.0,
                                    longitude: location?.longitude ?? 0.0
                                )

            // Set the Region
            self.mapRegion = MKCoordinateRegion(
                                center: self.mapCoordinate ?? CLLocationCoordinate2D(),
                                span: MKCoordinateSpan(
                                    latitudeDelta: 0.01,
                                    longitudeDelta: 0.01
                                )
                            )

            // Create the MapViewLocation
            self.mapLocations.append(
                MapViewLocation(
                    coordinate: self.mapCoordinate ?? CLLocationCoordinate2D()
                )
            )

            // Set the Loading State
            self.mapSuccess = true
            self.mapIsLoading = false

        }
    }

}

struct TestView: View {
    var body: some View {
        MapViewSmall(address: "1 Infinite Loop, Cupertino CA 95014, United States")
    }
}

The errors in the console when I go to the view are:

2023-01-05 16:22:05.512925-0600 BLAH[14113:3111741] [Client] {"msg":"#NullIsland Received a latitude or longitude from getLocationForBundleID that was exactly zero", "latIsZero":0, "lonIsZero":0, "location":{"floor":2147483647,"lifespan":-1,"rawLat":0,"integrity":0,"referenceFrame":"Unknown","lon":0,"speed":-1,"type":"Unknown","altitude":0,"rawCourse":-1,"confidence":0,"suitability":{"type":"decode failure","raw value":1797990240,"expected type":"CLClientLocationSuitability"},"ellipsoidalAltitude":0,"timestamp":-1,"rawReferenceFrame":"Unknown","lat":4.9406564584124654e-324,"verticalAccuracy":-1,"rawLon":0,"horizontalAccuracy":-1,"speedAccuracy":-1,"courseAccuracy":-1,"fromSimulationController":false,"course":-1}}

2023-01-05 16:22:05.588983-0600 BLAH[14113:3111741] [core] "Error returned from daemon: Error Domain=com.apple.accounts Code=7 "(null)""

2023-01-05 16:22:05.875299-0600 BLAH[14113:3111397] [PipelineLibrary] Mapping the pipeline data cache failed, errno 22


I also tried rewriting the functions using Combine to see if that helped and nothing changed so I reverted back.

This all works and the user experience is great, but I would very much like to resolve the errors and understand what I'm doing wrong.

CodePudding user response:

Remove the Task wrapper around the makeMap() function, because it is not necessary.

Add a onLongPressGesture modifier to the Map view, so that it can handle long press gestures.

Wrap the makeMap() function in a DispatchQueue.main.async block, to ensure that it is called on the main thread. This should fix the "Mapping the pipeline data cache failed, errno 22" error.

 .onAppear {
        self.mapIsLoading = true
        DispatchQueue.main.async {
            self.makeMap()
        }
    }

CodePudding user response:

The error you're seeing is nothing to do with displaying a map, it's generated when doing the geocoding. It seems like the geocoder likes to know where you are when it is geocoding, presumably so it can order results somehow. If your app asks for in-use location permissions, the warning goes away.

If you get console warnings you don't understand, it's always a good idea to set a breakpoint and step through the code to find out exactly where the error is generated.

Incidentally, you can rewrite getLocation like this:

func getLocation(address: String) async -> CLLocationCoordinate2D? {
    let geocoder = CLGeocoder()
    do {
        let marks = try await geocoder.geocodeAddressString(address)
        return marks.first?.location?.coordinate
    } catch {
        return nil
    }
}

And makeMap() like this:

@MainActor
func makeMap() async {

    // Convert the address
    let location = await getLocation(address: self.address)
    
    // Handle Bad Address Errors
    guard let location = location else {
        self.mapSuccess = false
        self.mapIsLoading = false
        return
    }

    self.mapCoordinate = location

    // Set the Region
    self.mapRegion = MKCoordinateRegion(
        center: location,
        span: MKCoordinateSpan(
            latitudeDelta: 0.01,
            longitudeDelta: 0.01
        )
    )

    // Create the MapViewLocation
    self.mapLocations.append(MapViewLocation(coordinate: location))
    
    // Set the Loading State
    self.mapSuccess = true
    self.mapIsLoading = false
}

Call it all from .task instead of onAppear:

.task {
    await self.makeMap()
}

Which makes your code a little cleaner.

  • Related