Importing json file and displaying POI's in Mapkit using Xcode 15 and swiftUI

Description:

I am working on an iOS 17 app using Xcode 15 and SwiftUI. The app involves displaying points of interest (POIs) on a MapView based on user proximity and other factors such as hours of operation. The data for the POIs is stored in a JSON file, which I am trying to import and parse in the project. However, I have encountered several issues:

Deprecation Errors:

When attempting to use MapAnnotation, I receive deprecation warnings and errors. It seems that MapAnnotation has been deprecated in iOS 17, and I need to use the new Annotation API along with MapContentBuilder. RandomAccessCollection Conformance:

Errors related to RandomAccessCollection conformance when using Map with SwiftUI. JSON Import and Parsing:

I used Bundle.main.url(forResource: "locations", withExtension: "json") to load the JSON file but faced issues with correctly parsing and mapping the JSON data to my model objects. What I Need:

Guidance on how to correctly use the new Annotation API and MapContentBuilder for displaying POIs on a MapView in iOS 17. Best practices for importing and parsing JSON files in SwiftUI, especially for dynamically updating the MapView based on user proximity and other criteria like hours of operation. Any relevant code snippets or examples would be greatly appreciated. Example of What We Tried

Model:

swift Copy code struct POI: Codable, Identifiable { let id: Int let name: String let latitude: Double let longitude: Double let hours: [String: String] } Loading JSON:

swift Copy code func loadPOIs() -> [POI] { guard let url = Bundle.main.url(forResource: "locations", withExtension: "json"), let data = try? Data(contentsOf: url) else { return [] }

let decoder = JSONDecoder()
return (try? decoder.decode([POI].self, from: data)) ?? []

} Map View:

swift Copy code import SwiftUI import MapKit

struct ContentView: View { @State private var pois: [POI] = loadPOIs() @State private var region = MKCoordinateRegion( center: CLLocationCoordinate2D(latitude: 40.7128, longitude: -74.0060), span: MKCoordinateSpan(latitudeDelta: 0.05, longitudeDelta: 0.05) )

var body: some View {
    Map(coordinateRegion: $region, annotationItems: pois) { poi in
        MapAnnotation(coordinate: CLLocationCoordinate2D(latitude: poi.latitude, longitude: poi.longitude)) {
            VStack {
                Text(poi.name)
                Circle().fill(Color.red).frame(width: 10, height: 10)
            }
        }
    }
}

} Issues Encountered:

MapAnnotation is deprecated. Errors related to RandomAccessCollection conformance. Any advice or solutions for these issues would be greatly appreciated. Thank you!

Answered by Frameworks Engineer in 790251022

MapAnnotation has been deprecated. To avoid the warning, you can instead use Annotation with a Map initializer that takes a MapContentBuilder closure. As with Picker or List, you can use ForEach to specify content for an array of elements in the builder closure.

Here is how your sample code might be adapted to use this API...

struct ContentView: View {
    @State private var pois: [POI] = loadPOIs()
    @State private var position: MapCameraPosition = .region(
        MKCoordinateRegion(
            center: CLLocationCoordinate2D(latitude: 40.7128, longitude: -74.0060),
            span: MKCoordinateSpan(latitudeDelta: 0.05, longitudeDelta: 0.05)
        )
    )
    var body: some View {
        Map(position: $position) {
            ForEach(pois) { poi in
                Annotation(
                    poi.name,
                    coordinate: CLLocationCoordinate2D(
                        latitude: poi.latitude,
                        longitude: poi.longitude
                    )
                ) {
                    Circle().fill(Color.red).frame(width: 10, height: 10)
                }
            }
        }
    }
}
Accepted Answer

MapAnnotation has been deprecated. To avoid the warning, you can instead use Annotation with a Map initializer that takes a MapContentBuilder closure. As with Picker or List, you can use ForEach to specify content for an array of elements in the builder closure.

Here is how your sample code might be adapted to use this API...

struct ContentView: View {
    @State private var pois: [POI] = loadPOIs()
    @State private var position: MapCameraPosition = .region(
        MKCoordinateRegion(
            center: CLLocationCoordinate2D(latitude: 40.7128, longitude: -74.0060),
            span: MKCoordinateSpan(latitudeDelta: 0.05, longitudeDelta: 0.05)
        )
    )
    var body: some View {
        Map(position: $position) {
            ForEach(pois) { poi in
                Annotation(
                    poi.name,
                    coordinate: CLLocationCoordinate2D(
                        latitude: poi.latitude,
                        longitude: poi.longitude
                    )
                ) {
                    Circle().fill(Color.red).frame(width: 10, height: 10)
                }
            }
        }
    }
}
Importing json file and displaying POI's in Mapkit using Xcode 15 and swiftUI
 
 
Q