My Locations - Exercise pg 210 (was 220): more efficient processing of changes

Just thinking about this, and having looked at some of the other solutions I think there might be a simpler solution?

I’m working on the premise that the notification.userInfo will provide the arrays of Locations that we need to process. Therefore there is no need to go back to CoreData and query for these - proving the first efficiency saving.

Therefore within the notification closure we should be able to extract two arrays of Location - one for deleted, one for added + updated, and then, via the selector, pass these on to a function to update the display selectively, rather than just calling updatedLocations.

There is a bit of extra fun, as despite the note in the book, the dictionary item is a set not an array.

A bit like:

   var managedObjectContext: NSManagedObjectContext! {
         didSet {
         NotificationCenter.default.addObserver(forName: Notification.Name.NSManagedObjectContextObjectsDidChange,
                                      object: managedObjectContext,
                                      queue: OperationQueue.main) {notification in
                                         if self.isViewLoaded {
                                             var updated = [Location]()
                                             var removed = [Location]()
                                             if let dictionary = notification.userInfo {
                                                if let set  = dictionary["inserted"] as? Set<Location> {
                                                  updated = Array(set)
                                                }
                                               if let set = dictionary["deleted"] as? Set<Location>{
                                                   removed = Array(set)
                                               }
                                               if let set = dictionary["updated"] as? Set<Location>{
                                                   updated.append(contentsOf: Array(set))
                                               }
                                               self.refreshLocations(deleted: removed, udopated: updated)
                                            }
                                       }
         }
      }
   }

   }

   func refreshLocations(deleted deletedLocations:[Location], udpated updatedLocations: [Location]) {
      let allLocations = deletedLocations + updatedLocations
      mapView.removeAnnotations(allLocations)
      mapView.addAnnotations(updatedlocations)
   }

If this works, and base on some quick testing it seems to, it would probably be worth refactoring upateLocations() to call refreshLocations(deleted: update: ) passing in its locations array for both parameters. The would remove duplication of code.

1 Like

Looks good to me. :smiley:

  1. You will also need to update the locations list, as well as the map, for inserts and deletions. That lets you use the index of a location in the locations list as the tag on the info button in the annotation view.

  2. You don’t need to update the annotations for an update, since the location actually is the annotation. Took me a while to grok that.

  3. It would be nice to update the selected annotation, if the user edited that location. It turns out you also need to update the selected annotation after changes that affect indexes, since that can affect the index stored in the info button. Took a while to discover that one, too. So might as well do it every time. You just need to deselect and reselect the currently selected annotation:

       for location in self.mapView.selectedAnnotations {
         if let view = self.mapView.view(for: location), view.isSelected {
           self.mapView.deselectAnnotation(location, animated: false)
           self.mapView.selectAnnotation(location, animated: false)
         }
    
1 Like

This topic was automatically closed after 166 days. New replies are no longer allowed.