-3

I created the function getAnnotations and I get no errors when calling that function inside of my view, or even when I define the function, but the Map gets 2 error:

Initializer 'init(coordinateRegion:interactionModes:showsUserLocation:userTrackingMode:annotationItems:annotationContent:)' requires that 'MKPointAnnotation.Type.Element' conform to 'Identifiable'

and

Type 'MKPointAnnotation.Type' cannot conform to 'RandomAccessCollection'

Code for the function getAnnotations:

func getAnnotations(completion: @escaping (_ annotations: [MKPointAnnotation]?) -> Void) {
        let db = Firestore.firestore()
        
        db.collection("annotations").addSnapshotListener { (querySnapshot, err) in
            guard let snapshot = querySnapshot else {
                if let err = err {
                    print(err)
                }
                completion(nil) // return nil if error
                return
            }
            guard !snapshot.isEmpty else {
                completion([]) // return empty if no documents
                return
            }
            
            var annotations = [MKPointAnnotation]()
            
            for doc in snapshot.documents {
                if let lat = doc.get("lat") as? String,
                   let lon = doc.get("long") as? String,
                   let latitude =  Double(lat),
                   let longitude = Double(lon) {
                    let coord = CLLocationCoordinate2D(latitude: latitude, longitude: longitude)
                    let annotation = MKPointAnnotation()
                    annotation.coordinate = coord
                    annotations.append(annotation)
                }
            }
            completion(annotations) // return array
        }
    }

And my view:

ZStack (alignment: .bottom) {
                Map(coordinateRegion: $viewModel.region, 
showsUserLocation: true, annotationItems: MKPointAnnotation) { annotations in
                    MapAnnotation(coordinate: annotations.coordinate) {
                        Circle()
                    }
                }
                .ignoresSafeArea()
                .tint(.pink)
                
                LocationButton(.currentLocation) {
                    viewModel.requestAllowOnceLocationPermission()
                }
                .foregroundColor(.white)
                .cornerRadius(8)
                .labelStyle(.iconOnly)
                .symbolVariant(.fill)
                .tint(.pink)
                .padding(.bottom)
                .padding(.trailing, 300)
            }
            .onAppear {
                getAnnotations({ (annotations) in
                    if let annotations = annotations {
                        print(annotations)
                    }
                })
            }

I tried to make this a synchronous function, but I'm still getting the 2 errors. Is it possible to resolve those first so I could see if my getAnnotations function actually works or no?

  • you have to do something like this https://www.hackingwithswift.com/books/ios-swiftui/sending-and-receiving-codable-data-with-urlsession-and-swiftui, I mean inside your Stack you only have to add Views not logic like getAnnotations. – cristian_064 Aug 15 '22 at 03:32
  • you cannot use an `asynchronous` function (getAnnotations) to return a View like you do. SwiftUI is based on changing states, so re-structure your code to change a state, for example, an array of `MapAnnotation`, or better still, use an `ObservableObject` class to hold your data and update the view as it changes. I suggest, you read up on how to setup and use `ObservableObject` class. – workingdog support Ukraine Aug 15 '22 at 03:37
  • i updated the code snippet above. I've tried to keep the logic outside of the view, but at the point of `.task { }` I don't really know if I'm supposed to append an array? –  Aug 15 '22 at 05:05
  • just adding `async` at the end of a function is not the way to go. Learn more about how to use Swift async/await concurrency. There is a video here: https://developer.apple.com/videos/play/wwdc2021/10132 In the mean time try replacing `.task` with `.onAppear` and get rid of the `async` at the end of `getAnnotations()` and the `await` as well. See also: https://developer.apple.com/documentation/swift/updating_an_app_to_use_swift_concurrency – workingdog support Ukraine Aug 15 '22 at 09:03
  • I updated the code snippet, and I attempted to remove the asynchronous part of the function, but still got the 2 errors. –  Aug 15 '22 at 14:05
  • don't remove the completion handlers, put them back. You need to read the basics again at: https://docs.swift.org/swift-book/LanguageGuide/TheBasics.html and do the tutorial at: https://developer.apple.com/tutorials/swiftui/ you cannot remove the asynchronous part of the function by removing the completion handler. Use your original code for `getAnnotations`, not the one where you just added `async` at the end of the function. – workingdog support Ukraine Aug 15 '22 at 23:06
  • The function itself is not an asynchronous function. I updated the code snippet to reflect this. I'm really not sure what to do at this point. When I take a step back, the logic makes sense, but I'm not exactally sure what to put inside of the `.onAppear { }` –  Aug 16 '22 at 02:39
  • The question is receiving a number of downvotes (not by me) - I believe it's because the question, code and objective is not clear. So, let me ask; is the objective to load data from Firestore and display it in the UI? If so, may I suggest creating a new project and work with something simple - say a list of users with names. Craft an app to load the users and display their name in a list. Once that's working, expand on it to display Map data. It really just seems like a focus on the basics of SwiftUI and asynchronous functions would be a good starting point. – Jay Aug 16 '22 at 20:14
  • i've decided to start from scratch, and clear up my basics so I could later take another approach at this problem. –  Aug 16 '22 at 20:36

1 Answers1

0

func getAnnotations(...) is asynchronous, because inside db.collection("annotations").addSnapshotListener... is asynchronous.

Use the func getAnnotations(completion: @escaping (_ annotations: [MKPointAnnotation]?) -> Void) {...} that you have now, and use this:

.onAppear {
    getAnnotations { annotations in
         annotations?.forEach { print("----> coordinate: \($0.coordinate)")  }
    }
}
             

Pay attention to the details, every bracket count, and they should be placed as shown.

Read the basics again at: https://docs.swift.org/swift-book/LanguageGuide/TheBasics.html

and do the tutorial at: https://developer.apple.com/tutorials/swiftui/

  • I got that part, still not sure on how to make the `db.collection("annotations").addSnapshotListener...` synchronous, but the function works without any errors. The `Map` itself inside of the function still has the errors that I had defined during my original post. –  Aug 16 '22 at 03:43
  • you cannot "make" `asynchronous` function `synchronous`. It is not possible. You have to understand the concept of `asynchronous` functions, and in this case using a completion handler, before you keep trying things you do not understand, hoping it will work by luck. You also cannot put `...The Map itself inside of the function still has the errors..`, as was mentioned before in your previous questions. Without some basic understanding you will keep struggling. Read and learn the basics first. – workingdog support Ukraine Aug 16 '22 at 03:56