Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

assertionFailure("Somehow a cluster contains an unknown annotation item.") #42

Open
IraklisElef opened this issue Apr 5, 2023 · 8 comments

Comments

@IraklisElef
Copy link

Hello!

I have a filter system in my App, which basically filters the visible annotations on the map. Based on some values, I show the appropriate annotations. I also cluster those annotations if needed. However, I noticed that if the annotation that I remove because of the new filter settings, is still in the clustering group, it throws the error assertionFailure("Somehow a cluster contains an unknown annotation item."). I guess I have to update all my clusters as I update my array of annotations. How do I do that? Thank you!

Yours faithfully,
Iraklis Eleftheriadis

@pauljohanneskraft
Copy link
Owner

Well, it apparently happens, when the cluster annotation is still in the MKMapView even though one of its member annotations has already been removed. My implementation can therefore not find the original annotation item, since it has already been removed from that dictionary.

Unfortunately, I currently do not have the time to dive deeper into this, so you might want to work around the current bug in one of the following ways:

  1. Remove the assertionFailure. Then the cluster annotation might still have that member annotation though, so not sure, how it will behave in action. It might be, that the annotation is not really removed or at least the cluster still believes that there is one more item. This is relevant, if your cluster annotation has a counter shown to the user for example.

  2. Filter the member annotations when creating the cluster annotation according to which annotations are still part of the MKMapView. This might, however, not fix the issue, since the cluster annotation could have been created when the annotation was still part of MKMapView and then later on, the cluster annotation's view is created.

  3. Do not return a MKAnnotationView in this case. I think that method allows to return nil, so we might do this (or an "invisible" one), since that cluster annotation should not be shown anyways.

In general, I believe this is actually a MapKit internal bug that we would be working around anyways - so maybe just for clarification, could you please provide a small example project or code snippet that exhibits this issue?

@IraklisElef
Copy link
Author

Thanks for the answer. If I remove the assertionFailure, the app seems to be running, so I guess that's a temporary fix. I am not really sure how I can provide you a small example project or code snippet as there is a lot of logic behind this. To cut a long story short, I have an array of annotation items that I show on the Map. Each annotation item has a variable that I use to filter the array of annotations. If the annotation that was filtered out of the array, was on a cluster annotation, it will throw the assertionFailure.

One quick note. My map seems to be lagging after applying the clustering. If I do not include a cluster and show all the annotations, the lag is not as bad. What do you think could be the issue?

@pauljohanneskraft
Copy link
Owner

Regarding the lag:

  • Are you 100% sure that you are not creating new objects (or changing the id) of your annotation items whenever the body of your view is called? Please make sure to have the annotation items in a @State property, as otherwise they will be removed and readded to the MKMapView on each rerun of the body.

  • How many annotation items are you displaying? I can still remember a couple of UIKit projects where MKMapView was just laggy when it came to trying to display a lot of annotations. I hope, you are also not doing any modifications to the annotation items based on the currently shown region, this might also create lag, since that would mean that annotation items are removed and readded all the time.

  • How complex is your annotation item and cluster content? Could it be that your view is simply a bit too complex to be displayed fast? Are you using AnyView in any of these views - this might also make things slow?

As you can see, without any context, there are many possible issues and I can only really guess what it might be unfortunately.

@IraklisElef
Copy link
Author

  1. The annotations variable is a @Published property because I have it stored in an ObservableObject class. I want to be able to access the annotations throughout my app.
  2. The number of annotations can be a lot, maybe even 1000. No, I am not doing any modifications to the annotation items based on the currently shown region. (Although, I am considering of not displaying the annotations if the user has zoomed out too much, do you think that's a good idea?)
  3. My annotation item is an image with a width and height of 40 px. Whenever a user taps the image, it displays a sheet with the annotation's information. The cluster content is a circle with a width and height of 30 px. I also overlay the number of clustered items inside the circle. No, I am not using AnyView in any of these views.

@pauljohanneskraft
Copy link
Owner

Okay, then it might simply be the number - have you done this in pure UIKit without issues? Because I would be interested whether this is an issue of MKMapView or this lib.

I'm not sure about the filtering of annotation items based on the zoom scale - it could be an improvement, but it might also cause issues instead - would need to try it out. Conceptually, clustering should normally be enough for the map to deal with it, but as I said, I have had issues with too many annotations in the past (when SwiftUI hasn't even been released).

What might work for the images is having one instance per image instead of creating them in the annotation closure (not sure, if you already do this though and whether they are backed by an asset, file or data - they might behave differently here). However, this depends on your use case and whether you have a fixed set of images that is known up front.

@IraklisElef
Copy link
Author

No I have not experienced with UIKit. The image I show is one that I have saved in the Assets. So it is basically a constant image that is the same for all annotations.

The code of the annotation item is:

struct EVChargerAnnotation: View {
    
    //MARK: - PROPERTIES
    @Binding public var selectedCharger: EVChargerModel?
    @Binding public var showingFilterSheet: Bool
    public let currentCharger: EVChargerModel?
    
    //MARK: - BODY
    var body: some View {
        
        Image(K.Images.SupplementarilyIcons.pinpoint)
            .resizable()
            .frame(width: 40, height: 40, alignment: .center)
            .onTapGesture {
                // Checks if there is no selected charger and the filter sheet is not showing, in order to prevent bug where the sheet appears and then disappears by itself
                if selectedCharger == nil && !showingFilterSheet {
                    selectedCharger = currentCharger
                }
            }
        
    } //: BODY
    
} //: VIEW

The code of the cluster annotation is:

struct EVChargerClusterAnnotation: View {
    
    //MARK: - PROPERTIES
    public let members: String
    
    //MARK: - BODY
    var body: some View {
        
        Circle()
            .foregroundColor(.primaryAppColor)
            .frame(width: 30, height: 30, alignment: .center)
            .overlay(
                Text(members)
                    .lineLimit(1)
                    .minimumScaleFactor(0.25)
                    .foregroundColor(.backgroundColor)
            )
            .overlay(
                Circle()
                    .strokeBorder(
                        Color.backgroundColor,
                        lineWidth: 1
                    )
            )
        
    } //: BODY
    
} //: STUCT

And this is the Map code:

Map(
            coordinateRegion: $region,
            type: .standard,
            pointOfInterestFilter: .excludingAll,
            informationVisibility: .userLocation,
            interactionModes: .all,
            userTrackingMode: $userTrackingMode,
            annotationItems: mapInformation.filteredEVChargers
        ) { charger in
            
            ViewMapAnnotation(
                coordinate: (charger.addressInfo?.coordinates)!,
                anchor: CGPoint(x: 0.0, y: -1.0),
                clusteringIdentifier: charger.clusteringIdentifier)
            {
                
                EVChargerAnnotation(
                    selectedCharger: $selectedCharger,
                    showingFilterSheet: $showingFilterSheet,
                    currentCharger: charger
                )
                
            }
            
        } clusterAnnotation: { charger, members in
            
            ViewMapAnnotation(annotation: charger) {
                
                EVChargerClusterAnnotation(
                    members: String(members.count)
                )
                
            }
            
        }

Maybe these code snippets will help clear things up a bit.

@pauljohanneskraft
Copy link
Owner

Yes, it does make it clearer, thank you 😊 I will try to have a deeper look in the next few days, but sadly cannot promise anything.

@IraklisElef
Copy link
Author

Thank you in advance!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants