'Show multiple users' location stored in a database on a map with SwiftUI and Firebase

As said in the title, I would like to show the location of my users on a map in my app. I would like something like Snapshat map :

Snapshat Map Picture

I used SwiftUI and Firebase.

Here is what I have done so far :

import SwiftUI
import Firebase
import CoreLocation
import MapKit

struct mapTimelineView: View {
    
    @StateObject private var locationViewModel = LocationViewModel.shared
    
    @State private var showNewPostView = false
    
    @ObservedObject var viewModel = TimelineViewModel()
    
    @ObservedObject var authViewModel = AuthViewModel()
        
    @ObservedObject var obs = observer()
    
    var body: some View {
        ZStack (alignment: .bottomTrailing) {
            
//            Map(coordinateRegion: $locationViewModel.region, showsUserLocation: true)
//                .accentColor(Color("accentColor"))
//                .edgesIgnoringSafeArea(.all)
            
            mapView(geopoints: self.obs.data["data"] as! [String : GeoPoint]) <--- /!\ the problem occurs here /!\
            
            Button {
                showNewPostView.toggle()
            } label: {
                Image(systemName: "plus")
                    .resizable()
                    .renderingMode(.template)
                    .frame(width: 30, height: 30)
                    .font(.system(size: 30, weight: .bold, design: .default))
                    .padding()
            }
            .background(Color("accentColor"))
            .foregroundColor(Color("backgroundColor"))
            .clipShape(Circle())
            .padding()
            .shadow(radius: 20)
            .fullScreenCover(isPresented: $showNewPostView) {
                uploadPostView()
            }
            
        }
        .navigationBarTitleDisplayMode(.inline)
        .navigationTitle("home")
        .background(Color("backgroundColor"))
    }
}

struct mapTimelineView_Previews: PreviewProvider {
    static var previews: some View {
        mapTimelineView()
    }
}

struct mapView: UIViewRepresentable {
    
    @ObservedObject var authViewModel = AuthViewModel()
    
    var geopoints : [String: GeoPoint]
        
    func makeCoordinator() -> Coordinator {
        
        return mapView.Coordinator(parent1: self)
    }
    
    let map = MKMapView()
    let manager = CLLocationManager()
    
    func makeUIView(context: Context) -> MKMapView {
        
        manager.delegate = context.coordinator
        manager.startUpdatingLocation()
        map.showsUserLocation = true
        let region = MKCoordinateRegion(center: CLLocationCoordinate2D(latitude: 48.856614, longitude: 2.3522219), span: MKCoordinateSpan(latitudeDelta: 0.05, longitudeDelta: 0.05))
        map.region = region
        return map
    }
    
    func updateUIView(_ uiView: MKMapView, context: Context) {
                
        for i in geopoints {
            
            let point = MKPointAnnotation()
            point.coordinate = CLLocationCoordinate2D(latitude: i.value.latitude, longitude: i.value.longitude)
            point.title = i.key
            uiView.removeAnnotations(uiView.annotations)
            uiView.addAnnotation(point)
            
        }
    }
    
    class Coordinator: NSObject, CLLocationManagerDelegate {
        
        @ObservedObject var authViewModel = AuthViewModel()
        
        var parent: mapView
        
        init(parent1: mapView) {
            
            parent = parent1
            
        }
        
        func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
            
            guard let uid = self.authViewModel.userSession?.uid else { return }
                                    
            let last = locations.last
            
            Firestore.firestore().collection("locations").document("coordinate").setData(["updates" : [uid : GeoPoint(latitude: (last?.coordinate.latitude)!, longitude: (last?.coordinate.longitude)!)]],merge: true) { (err) in
                
                
                if err != nil{
                    
                    print((err?.localizedDescription)!)
                    return
                }
                print("success")
            }
        }
    }
}

class observer : ObservableObject{

    @Published var data = [String : Any]()

    init() {

        let db = Firestore.firestore()

        db.collection("locations").document("coordinate").addSnapshotListener { (snap, err) in

            if err != nil{

                print((err?.localizedDescription)!)
                return
            }

            let updates = snap?.get("updates") as! [String : GeoPoint]

            self.data["data"] = updates
        }
    }
}

It shows a map and the user location. But my problem is that my app crash because there is "nil" on this line :

mapView(geopoints: self.obs.data["data"] as! [String : GeoPoint])

I got this error message :

Thread 1: Fatal error: Unexpectedly found nil while unwrapping an Optional value

And, I don't understand this issue because I do have data in my database :

Screenshot of the database

So, if anyone has a solution, I would like to know it.



Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source