Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to best load Locations for MapView from Webserver

I have like 2000 locations in a web database which a user should be able to select on a Map. I can ask the web database to give me only a certain number of locations originating from a current location.

To make everything smooth and elegant I would first instantiate MKMapView, start CLLocationManager and wait until I get a didUpdateLocations. Then I would try to get my data from a Database with a completion handler.

Should I

a) get all data at once

b) get the data in little pieces or chunks ?

What it the best way?

func locationManager(manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
    self.gotoCurrentLocation()
    if let userLocation = manager.location {
        GroundHelper.getAllGroundLocations(userLocation) { self.handleWaypoints($0!) }
    }
}

private func handleWaypoints(grounds: [Ground]) {
    mapView.addAnnotations(grounds)
}

// MARK: - Helper Methods

typealias GPXCompletionHandler = ([Ground]?) -> Void

class func getAllGroundLocations(userlocation: CLLocation, completionHandler: GPXCompletionHandler)  {

    let priority = DISPATCH_QUEUE_PRIORITY_DEFAULT
    dispatch_async(dispatch_get_global_queue(priority, 0), { ()->() in
        var results = RestApiManager.sharedInstance.getGPS(userlocation, limit: 50)

        // return first 50 results
        dispatch_async(dispatch_get_main_queue(), {

            var grounds = [Ground]()
            for result in results {
                let (_,jsonGround) = result
                let ground = Ground(json:jsonGround)
                grounds.append(ground)
            }
            completionHandler(grounds)
        })

        // get the rest
        results = RestApiManager.sharedInstance.getGPS(userlocation)

        // return them
        dispatch_async(dispatch_get_main_queue(), {

            var grounds = [Ground]()
            for result in results {
                let (_,jsonGround) = result
                let ground = Ground(json:jsonGround)
                grounds.append(ground)
            }
            completionHandler(grounds)

        })
    })
}
like image 518
Timm Kent Avatar asked Oct 03 '15 13:10

Timm Kent


1 Answers

Getting all data at once is not scalable. You might make it work on a timely manner with 2000 entries, but what if the data set grows? Can you handle 3000? 5000? 10000 annotations?

Getting your data in chunks, returning only entries near the location the map is centered in, and displaying those entries as the user moves around the map makes more sense. However, this approach is very slow, as there is usually a long delay between the user dragging the map and the annotations appearing on screen (network requests are slow in nature).

Thus, the recommended approach for a great user experience is to cache the results locally. You can do this if you have a local database (Core Data, for example) or you could do this with NSCache.

With this approach, you will be hitting the server with new requests as the user moves around the map, but the amount of results returned can be limited to 20, 50 or 100 (something that gets you the highest amounts of data while being very responsive).

Next, you would be rendering all annotations from your cached results on the map, so the number of annotations will grow as the user moves around the map.

The guys from http://realm.io have a pretty nice video that explains this approach: https://www.youtube.com/watch?v=hNDNXECD84c While you do not need to use their mobile database (Realm), you can get the idea of the application architecture and design.

like image 172
Eneko Alonso Avatar answered Oct 05 '22 02:10

Eneko Alonso