diff --git a/Meshtastic Client.xcodeproj/project.pbxproj b/Meshtastic Client.xcodeproj/project.pbxproj index 980e4498..1f46a7c2 100644 --- a/Meshtastic Client.xcodeproj/project.pbxproj +++ b/Meshtastic Client.xcodeproj/project.pbxproj @@ -10,7 +10,6 @@ C9483F6D2773017500998F6B /* MapView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C9483F6C2773017500998F6B /* MapView.swift */; }; C9697F9D279336B700250207 /* LocalMBTileOverlay.swift in Sources */ = {isa = PBXBuildFile; fileRef = C9697F9C279336B700250207 /* LocalMBTileOverlay.swift */; }; C9697FA527933B8C00250207 /* SQLite in Frameworks */ = {isa = PBXBuildFile; productRef = C9697FA427933B8C00250207 /* SQLite */; }; - C9697FA72793F9FB00250207 /* MTAppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = C9697FA62793F9FB00250207 /* MTAppDelegate.swift */; }; C9A7BC1027759A9600760B50 /* PositionAnnotationView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C9A7BC0F27759A9600760B50 /* PositionAnnotationView.swift */; }; C9A88B55278B503C00BD810A /* MapViewModule.swift in Sources */ = {isa = PBXBuildFile; fileRef = C9A88B54278B503C00BD810A /* MapViewModule.swift */; }; C9A88B57278B559900BD810A /* apponly.pb.swift in Sources */ = {isa = PBXBuildFile; fileRef = C9A88B56278B559900BD810A /* apponly.pb.swift */; }; @@ -78,7 +77,6 @@ /* Begin PBXFileReference section */ C9483F6C2773017500998F6B /* MapView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MapView.swift; sourceTree = ""; }; C9697F9C279336B700250207 /* LocalMBTileOverlay.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LocalMBTileOverlay.swift; sourceTree = ""; }; - C9697FA62793F9FB00250207 /* MTAppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MTAppDelegate.swift; sourceTree = ""; }; C9A7BC0F27759A9600760B50 /* PositionAnnotationView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PositionAnnotationView.swift; sourceTree = ""; }; C9A88B54278B503C00BD810A /* MapViewModule.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MapViewModule.swift; sourceTree = ""; }; C9A88B56278B559900BD810A /* apponly.pb.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = apponly.pb.swift; sourceTree = ""; }; @@ -265,7 +263,6 @@ DDC2E18826CE24EE0042C5E4 /* Model */, DDC2E18926CE24F70042C5E4 /* Resources */, DDC2E15726CE248E0042C5E4 /* MeshtasticClientApp.swift */, - C9697FA62793F9FB00250207 /* MTAppDelegate.swift */, DDC2E16526CE248F0042C5E4 /* Info.plist */, DDC2E15D26CE248F0042C5E4 /* Preview Content */, ); @@ -562,7 +559,6 @@ DDAF8C6326ED0A230058C060 /* admin.pb.swift in Sources */, C9483F6D2773017500998F6B /* MapView.swift in Sources */, DDAF8C5826ED07FD0058C060 /* mesh.pb.swift in Sources */, - C9697FA72793F9FB00250207 /* MTAppDelegate.swift in Sources */, DD8169FB271F1F3A00F4AB02 /* MeshLog.swift in Sources */, DD2E65262767A01F00E45FC5 /* NodeDetail.swift in Sources */, C9A88B57278B559900BD810A /* apponly.pb.swift in Sources */, @@ -738,7 +734,7 @@ CODE_SIGN_STYLE = Automatic; CURRENT_PROJECT_VERSION = 18; DEVELOPMENT_ASSET_PATHS = "\"MeshtasticClient/Preview Content\""; - DEVELOPMENT_TEAM = GCH7VS5Y9R; + DEVELOPMENT_TEAM = 37C534H572; ENABLE_PREVIEWS = YES; INFOPLIST_FILE = MeshtasticClient/Info.plist; IPHONEOS_DEPLOYMENT_TARGET = 15.0; diff --git a/Meshtastic Client.xcodeproj/project.xcworkspace/xcuserdata/joshuapirihi.xcuserdatad/UserInterfaceState.xcuserstate b/Meshtastic Client.xcodeproj/project.xcworkspace/xcuserdata/joshuapirihi.xcuserdatad/UserInterfaceState.xcuserstate index c932fa44..8bcb5868 100644 Binary files a/Meshtastic Client.xcodeproj/project.xcworkspace/xcuserdata/joshuapirihi.xcuserdatad/UserInterfaceState.xcuserstate and b/Meshtastic Client.xcodeproj/project.xcworkspace/xcuserdata/joshuapirihi.xcuserdatad/UserInterfaceState.xcuserstate differ diff --git a/MeshtasticClient/MTAppDelegate.swift b/MeshtasticClient/MTAppDelegate.swift deleted file mode 100644 index a59f9b27..00000000 --- a/MeshtasticClient/MTAppDelegate.swift +++ /dev/null @@ -1,28 +0,0 @@ -// -// AppDelegate.swift -// MeshtasticClient -// -// Created by Joshua Pirihi on 16/01/22. -// - -import Foundation -import UIKit - -class MTAppDelegate: NSObject, UIApplicationDelegate { - - func applicationDidFinishLaunching(_ application: UIApplication) { - } - - func application( - _ application: UIApplication, - didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]? = nil - ) -> Bool { - // ... - return true - } - - func application(_ app: UIApplication, open url: URL, options: [UIApplication.OpenURLOptionsKey: Any] = [:]) -> Bool { - print("We received a file") - return true - } -} diff --git a/MeshtasticClient/MeshtasticClientApp.swift b/MeshtasticClient/MeshtasticClientApp.swift index 5f99066d..10879c8b 100644 --- a/MeshtasticClient/MeshtasticClientApp.swift +++ b/MeshtasticClient/MeshtasticClientApp.swift @@ -3,8 +3,6 @@ import CoreData @main struct MeshtasticClientApp: App { - - @UIApplicationDelegateAdaptor var delegate: MTAppDelegate let persistenceController = PersistenceController.shared @@ -19,6 +17,33 @@ struct MeshtasticClientApp: App { .environment(\.managedObjectContext, persistenceController.container.viewContext) .environmentObject(bleManager) .environmentObject(userSettings) + .onOpenURL(perform: { (url) in + //we are expecting a .mbtiles map file that contains raster data + //save it to the documents directory, and name it offline_map.mbtiles + let fileManager = FileManager.default + let documentsDirectory = fileManager.urls(for: .documentDirectory, in: .userDomainMask).first! + let destination = documentsDirectory.appendingPathComponent("offline_map.mbtiles", isDirectory: false) + + //do we need to delete an old one? + if (fileManager.fileExists(atPath: destination.path)) { + print("ℹ️ Found an old map file. Deleting it") + try? fileManager.removeItem(atPath: destination.path) + } + + try? fileManager.copyItem(at: url, to: destination) + + if (fileManager.fileExists(atPath: destination.path)) { + print("ℹ️ Saved the map file") + + //need to tell the map view that it needs to update and try loading the new overlay + UserDefaults.standard.set(Date().timeIntervalSince1970, forKey: "lastUpdatedLocalMapFile") + + } else { + print("💥 Didn't save the map file") + } + + } + ) } .onChange(of: scenePhase) { (newScenePhase) in switch newScenePhase { diff --git a/MeshtasticClient/Views/Map/LocalMBTileOverlay.swift b/MeshtasticClient/Views/Map/LocalMBTileOverlay.swift index 088f6365..ed3f4022 100644 --- a/MeshtasticClient/Views/Map/LocalMBTileOverlay.swift +++ b/MeshtasticClient/Views/Map/LocalMBTileOverlay.swift @@ -33,6 +33,11 @@ extension MKMapRect { } } +enum MapTileError: Error { + case invalidFormat + case other +} + class LocalMBTileOverlay: MKTileOverlay { var path: String! @@ -46,7 +51,7 @@ class LocalMBTileOverlay: MKTileOverlay { } } - init(mbTilePath path: String) { + init?(mbTilePath path: String) { super.init(urlTemplate: nil) self.path = path @@ -58,6 +63,12 @@ class LocalMBTileOverlay: MKTileOverlay { let name = Expression("name") let value = Expression("value") + //make sure it's raster + let formatQuery = try mb.pluck(metadata.select(value).filter(name == "format")) + if formatQuery?[value] == nil || (formatQuery![value] != "jpg" && formatQuery![value] != "png") { + throw MapTileError.invalidFormat + } + let minZQuery = try mb.pluck(metadata.select(value).filter(name == "minzoom")) self.minimumZ = Int(minZQuery![value])! @@ -78,7 +89,8 @@ class LocalMBTileOverlay: MKTileOverlay { } catch { - //print("MAP ERROR \(error)") + print("💥 Map tile error: \(error)") + return nil } diff --git a/MeshtasticClient/Views/Map/MapViewModule.swift b/MeshtasticClient/Views/Map/MapViewModule.swift index f7468edd..0bebbc20 100644 --- a/MeshtasticClient/Views/Map/MapViewModule.swift +++ b/MeshtasticClient/Views/Map/MapViewModule.swift @@ -17,6 +17,10 @@ public struct MapView: UIViewRepresentable { //@Binding private var region: MKCoordinateRegion + //make this view dependent on the UserDefault that is updated when importing a new map file + @AppStorage("lastUpdatedLocalMapFile") private var lastUpdatedLocalMapFile = 0 + @State private var loadedLastUpdatedLocalMapFile = 0 + private var customMapOverlay: CustomMapOverlay? @State private var presentCustomMapOverlayHash: CustomMapOverlay? @@ -138,20 +142,31 @@ public struct MapView: UIViewRepresentable { //mapView.region = self.region //} - if self.customMapOverlay != self.presentCustomMapOverlayHash { + if self.customMapOverlay != self.presentCustomMapOverlayHash || self.loadedLastUpdatedLocalMapFile != self.lastUpdatedLocalMapFile { mapView.removeOverlays(mapView.overlays) if let customMapOverlay = self.customMapOverlay { - if let tilePath = Bundle.main.path(forResource: "offline_map", ofType: "mbtiles") { - let overlay = LocalMBTileOverlay(mbTilePath: tilePath) + let fileManager = FileManager.default + let documentsDirectory = fileManager.urls(for: .documentDirectory, in: .userDomainMask).first! + let tilePath = documentsDirectory.appendingPathComponent("offline_map.mbtiles", isDirectory: false).path + if fileManager.fileExists(atPath: tilePath) { + //if let tilePath = Bundle.main.path(forResource: "offline_map", ofType: "mbtiles") { - overlay.canReplaceMapContent = false//customMapOverlay.canReplaceMapContent + print("Loading local map file") - mapView.addOverlay(overlay) + if let overlay = LocalMBTileOverlay(mbTilePath: tilePath) { + + overlay.canReplaceMapContent = false//customMapOverlay.canReplaceMapContent + + mapView.addOverlay(overlay) + } + } else { + print("Couldn't find a local map file to load") } } DispatchQueue.main.async { self.presentCustomMapOverlayHash = self.customMapOverlay + self.loadedLastUpdatedLocalMapFile = self.lastUpdatedLocalMapFile } }