RileyDev
RileyDev

Reputation: 2525

Saving and retrieving Map data using SwiftUI Map

I am trying to display a location in Map, allow user to search a location and update the location, then save the location.

When retrieving the data from model.object.location is is not displaying in Map, the data is saved.

LocationObject

struct Location: Identifiable, Codable {

    static func == (lhs: Location, rhs: Location) -> Bool {
        return lhs.id == rhs.id
    }
    
    var id = UUID().uuidString
    
    var geopoint = GeoPoint(latitude: 51.510357, longitude: -0.116773)
    
    var postcode: String = ""
    var street: String = ""
    var city: String = ""
    var region: String = ""
    var country: String = ""
    var title: String? = ""
    var subtitle: String? = ""
    var longitude: Double? = -0.116773
    var latitude: Double? = 51.510357

    var coordinate: CLLocationCoordinate2D {
        return CLLocationCoordinate2D(latitude: latitude ?? 51.510357, longitude: longitude ?? -0.116773)
    }
}

This is used with a ViewModel, for example model.object.location

MapView

Show the map with data from Location object, and update view from SearchView.

@EnvironmentObject var model: Model

@State var selectedLocation: LocalSearchViewData? = nil

@State var position = MapCameraPosition.region(
    MKCoordinateRegion(
        center: CLLocationCoordinate2D(latitude: 51.510357, longitude: -0.116773),
        latitudinalMeters: 500, longitudinalMeters: 500)
)

Map(position: $position) {
    if selectedLocation != nil {
        Marker("\(selectedLocation!.street)", coordinate: selectedLocation!.coordinate)
            .tint(Color("Accent"))
    }
}
.sheet(isPresented: $showDetail) {
    SearchLocationView(selectedLocation: $selectedLocation, position: $position)
}
.onAppear {
    // Location
    if model.object!.location != nil {
        print(LOCATION") 
        print(model.object.location!) <--- THIS PRINTS THE LOCATION CORRECT, SHOWING DATA DOES SAVE
        
        selectedLocation?.latitude = (model.object!.location!.latitude)!
        selectedLocation?.latitude = (model.object!.location!.longitude)!
        
        if let long = model.object?.location?.longitude {
            if let lat = model.object?.location?.latitude {

                print(long) <--- This logs correct 

                position = MapCameraPosition.region(
                    MKCoordinateRegion(
                        center: CLLocationCoordinate2D(latitude: lat, longitude: long),
                        latitudinalMeters: 500, longitudinalMeters: 500)
                )
            }
        }
    }
}

SearchView

struct SearchLocationView: View {

@Environment(\.presentationMode) var presentationMode

@StateObject private var viewModel = LocationModel()

@Binding var selectedLocation: LocalSearchViewData?
@Binding var position: MapCameraPosition

var body: some View {
    GeometryReader { geometryProxy in
        NavigationStack() {
            ZStack(alignment: .bottom) {
                ScrollView() {
                    VStack(alignment: .leading) {
                        HStack(alignment: .center, spacing: 16) {
                            TextField("Enter Address", text: $viewModel.cityText)
                                .modifier(SearchTextFieldStyle())
                        }

                        List(viewModel.viewData) { item in
                            VStack(alignment: .leading) {
                                Text(item.title)
                                Text(item.subtitle).foregroundColor(.secondary)
                            }
                            .hoverEffect()
                            .onTapGesture {
                                self.selectedLocation = item
                                
                                position = MapCameraPosition.region(
                                    MKCoordinateRegion(
                                        center: CLLocationCoordinate2D(latitude: selectedLocation?.latitude ?? 5555.5, longitude: selectedLocation?.longitude ?? 5555.5 ),
                                        latitudinalMeters: 500, longitudinalMeters: 500))
                                
                                self.presentationMode.wrappedValue.dismiss()
                            }
                        }
                        
                    }
                    .padding(.horizontal)
                    .padding(.top, 60)
                    .frame(
                        width: geometryProxy.size.width,
                        height: geometryProxy.size.height,
                        alignment: .center
                    )
                }
            }
            .frame(
                width: geometryProxy.size.width,
                height: geometryProxy.size.height,
                alignment: .center
            )
            .navigationBarBackButtonHidden(true)
            .toolbar {
                ToolbarItem(placement: .topBarLeading) {
                    Button {
                        presentationMode.wrappedValue.dismiss()
                    } label: {
                        Image(systemName: "arrow.left")
                            .font(.system(size: 20, weight: .semibold))
                    }
                    .buttonStyle(TopNavButton())
                }
            }
            .toolbar {
                ToolbarItem(placement: .topBarTrailing) {
                    Button {
                        viewModel.cityText = ""
                        viewModel.viewData.removeAll()
                    } label: {
                        Text("Clear")
                        
                    }
                    .buttonStyle(.plain)
                    .hoverEffect()
                }
            }
        }
    }
    #if os(visionOS)
    .frame(minWidth: 520)
    .frame(minHeight: 680)
    #endif
}
}

struct LocalSearchViewData: Identifiable {
var id = UUID()
var title: String
var subtitle: String
var street: String
var city: String
var country: String
var postcode: String

var longitude: Double = 0.0
var latitude: Double = 0.0

var coordinate: CLLocationCoordinate2D {
    return CLLocationCoordinate2D(latitude: latitude, longitude: longitude)
}

init(mapItem: MKMapItem) {
    self.title = mapItem.name ?? ""
    self.subtitle = mapItem.placemark.title ?? ""
    
    if let s = mapItem.placemark.postalAddress?.street {
        self.street = s
    } else {
        self.street = ""
    }
    
    if let s = mapItem.placemark.postalAddress?.city {
        self.city = s
    } else {
        self.city = ""
    }
    if let s = mapItem.placemark.postalAddress?.country {
        self.country = s
    } else {
        self.country = ""
    }
    
    if let s = mapItem.placemark.postalAddress?.postalCode {
        self.postcode = s
    } else {
        self.postcode = ""
    }
    
    longitude = mapItem.placemark.coordinate.longitude
    latitude = mapItem.placemark.coordinate.latitude
    
}
}



final class LocalSearchService {
let localSearchPublisher = PassthroughSubject<[MKMapItem], Never>()
private let center: CLLocationCoordinate2D
private let radius: CLLocationDistance

init(in center: CLLocationCoordinate2D,
     radius: CLLocationDistance = 350_000) {
    self.center = center
    self.radius = radius
}

public func searchCities(searchText: String) {
    request(resultType: .address, searchText: searchText)
}

public func searchPointOfInterests(searchText: String) {
    request(searchText: searchText)
}

private func request(resultType: MKLocalSearch.ResultType = .pointOfInterest,
                     searchText: String) {
    let request = MKLocalSearch.Request()
    request.naturalLanguageQuery = searchText
    request.pointOfInterestFilter = .includingAll
    request.resultTypes = resultType
    request.region = MKCoordinateRegion(center: center,
                                        latitudinalMeters: radius,
                                        longitudinalMeters: radius)
    let search = MKLocalSearch(request: request)
    
    search.start { [weak self](response, _) in
        guard let response = response else {
            return
        }
        
        self?.localSearchPublisher.send(response.mapItems)
    }
}
}

final class LocationModel: ObservableObject {
private var cancellable: AnyCancellable?

@Published var cityText = "" {
    didSet {
        searchForCity(text: cityText)
    }
}

@Published var poiText = "" {
    didSet {
        searchForPOI(text: poiText)
    }
}

@Published var viewData = [LocalSearchViewData]()

var service: LocalSearchService

init() {
    //        New York
    let center = CLLocationCoordinate2D(latitude: 40.730610, longitude: -73.935242)
    service = LocalSearchService(in: center)
    
    cancellable = service.localSearchPublisher.sink { mapItems in
        self.viewData = mapItems.map({ LocalSearchViewData(mapItem: $0) })
    }
}

private func searchForCity(text: String) {
    service.searchCities(searchText: text)
}

private func searchForPOI(text: String) {
    service.searchPointOfInterests(searchText: text)
}
}

Save

func saveLocation() {
        if selectedLocation != nil {
            model.object?.location?.street = selectedLocation!.street
            model.object?.location?.city = selectedLocation!.city
            model.object?.location?.country = selectedLocation!.country
            model.object?.location?.postcode = selectedLocation!.postcode
            model.object?.location?.title = selectedLocation!.title
            model.object?.location?.subtitle = selectedLocation!.subtitle

            model.object?.location?.latitude = selectedLocation!.latitude
            model.object?.location?.longitude = selectedLocation!.longitude
        }
}

Upvotes: 0

Views: 111

Answers (0)

Related Questions