improvement: avoid duplicate map tile loading

Previously a map tile cache miss would cause 2x loading of the tile: once from
the remote tile server (which is then written to disk) and once from disk
during the default MKTileOverlay.loadTile function. Instead we now directly
implement loadTile so that we can avoid the duplicate loading and simply
return the fetched remote tile after it is cached, which leads to a
noticeable improvement in offline map performance.
This commit is contained in:
Austin Payne 2024-02-14 23:10:30 -07:00
parent 231a160c1e
commit 3c0e56aeaf
2 changed files with 21 additions and 13 deletions

View file

@ -103,20 +103,26 @@ class OfflineTileManager: ObservableObject {
}
}
}
func getTileOverlay(for path: MKTileOverlayPath) -> URL {
let file = "\(UserDefaults.mapTileServer.id)-z\(path.z)x\(path.x)y\(path.y).png"
// Check is tile is already available
let tilesUrl = documentsDirectory.appendingPathComponent("tiles").appendingPathComponent(file)
if fileManager.fileExists(atPath: tilesUrl.path) {
return tilesUrl
} else {
if UserDefaults.enableOfflineMaps, UserDefaults.mapTileServer.zoomRange.contains(path.z) { // Get and persist newTile
return persistLocally(path: path)
} else { // Else display empty tile (transparent over Maps tiles)
return Bundle.main.url(forResource: "alpha", withExtension: "png")!
}
func loadAndCacheTileOverlay(for path: MKTileOverlayPath) throws -> Data {
guard UserDefaults.enableOfflineMaps, UserDefaults.mapTileServer.zoomRange.contains(path.z) else {
return try Data(contentsOf: Bundle.main.url(forResource: "alpha", withExtension: "png")!)
}
let tilesUrl = documentsDirectory
.appendingPathComponent("tiles")
.appendingPathComponent("\(UserDefaults.mapTileServer.id)-z\(path.z)x\(path.x)y\(path.y)")
.appendingPathExtension("png")
do {
return try Data(contentsOf: tilesUrl)
} catch let error as NSError where error.code == NSFileReadNoSuchFileError {
let data = try Data(contentsOf: overlay.url(forTilePath: path))
try data.write(to: tilesUrl)
return data
}
}
// MARK: Private methods
private func computeTileOverlayPaths(boundingBox box: MKMapRect, maxZ: Int = 17) -> [MKTileOverlayPath] {
var paths = [MKTileOverlayPath]()

View file

@ -11,5 +11,7 @@ import MapKit
typealias TileCoordinates = (x: Int, y: Int, z: Int)
class TileOverlay: MKTileOverlay {
override func url(forTilePath path: MKTileOverlayPath) -> URL { OfflineTileManager.shared.getTileOverlay(for: path) }
override func loadTile(at path: MKTileOverlayPath) async throws -> Data {
return try OfflineTileManager.shared.loadAndCacheTileOverlay(for: path)
}
}