← Back to index

SpotsApp

Spots
CodeWhat It DoesHow It Does It
▶ IMPORTS
import SwiftUI import UserNotifications import Combine import CoreLocation import MapKitFramework importsImports SwiftUI, UserNotifications, Combine, CoreLocation, MapKit.
// CLLocationCoordinate2D retroactive Equatable conformance (needed for onChange) extension CLLocationCoordinate2D: @retroactive Equatable { public static func == (lhs: CLLocationCoordinate2D, rhs: CLLocationCoordinate2D) -> Bool { lhs.latitude == rhs.latitude && lhs.longitude == rhs.longitude } }Documentation commentDescribes the following declaration.
// Simple codable venue passed from MindTheShow via URL struct PendingVenue: Codable { let name: String let lat: Double let lon: Double }Documentation commentDescribes the following declaration.
// Shared data manager for incoming shared content class SharedDataManager: ObservableObject { static let shared = SharedDataManager() @Published var pendingEntry: (name: String, content: String)? @Published var shouldImportSharedEntry = false @Published var pendingMapCenter: CLLocationCoordinate2D? @Published var pendingMapSpan: MKCoordinateSpan? @Published var pendingMapName: String? @Published var pendingVenues: [PendingVenue] = [] }Documentation commentDescribes the following declaration.
@main struct SpotsApp: App { @UIApplicationDelegateAdaptor(AppDelegate.self) var appDelegate @StateObject private var sharedDataManager = SharedDataManager.shared var body: some Scene { WindowGroup { ContentView() .environmentObject(sharedDataManager) .onOpenURL { url in handleIncomingURL(url) } } } private func handleIncomingURL(_ url: URL) { guard url.scheme == "spots" else { return } let host = url.host if host == "import" { // Triggered by share extension — signal ContentView to import the pending entry sharedDataManager.shouldImportSharedEntry = true } else if host == "map" { // Launched from MindTheShow — spots://map?lat=X&lon=Y&latSpan=A&lonSpan=B&name=N guard let components = URLComponents(url: url, resolvingAgainstBaseURL: false), let queryItems = components.queryItems else { return } var lat: Double? var lon: Double? var latSpan: Double? var lonSpan: Double? var name: String? for item in queryItems { if item.name == "lat", let v = item.value { lat = Double(v) } else if item.name == "lon", let v = item.value { lon = Double(v) } else if item.name == "latSpan", let v = item.value { latSpan = Double(v) } else if item.name == "lonSpan", let v = item.value { lonSpan = Double(v) } else if item.name == "name" { name = item.value?.removingPercentEncoding } else if item.name == "venues", let raw = item.value?.removingPercentEncoding, let data = raw.data(using: .utf8), let list = try? JSONDecoder().decode([PendingVenue].self, from: data) { sharedDataManager.pendingVenues = list } } if let lat, let lon { sharedDataManager.pendingMapCenter = CLLocationCoordinate2D(latitude: lat, longitude: lon) } if let latSpan, let lonSpan { sharedDataManager.pendingMapSpan = MKCoordinateSpan(latitudeDelta: latSpan, longitudeDelta: lonSpan) } sharedDataManager.pendingMapName = name } else if host == "add" { guard let components = URLComponents(url: url, resolvingAgainstBaseURL: false), let queryItems = components.queryItems else { return } var name = "" var content = "" for item in queryItems { if item.name == "name" { name = item.value?.removingPercentEncoding ?? "" } else if item.name == "content" { content = item.value?.removingPercentEncoding ?? "" } } if !name.isEmpty { sharedDataManager.pendingEntry = (name: name, content: content) } } } }Code blockSee source code for full implementation.
class AppDelegate: NSObject, UIApplicationDelegate, UNUserNotificationCenterDelegate { func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey : Any]? = nil) -> Bool { // Style the navigation bar title and strip the white rounded-rectangle // container that iOS automatically puts around leading bar button items. let titleColor = UIColor(red: 0/255, green: 45/255, blue: 114/255, alpha: 1) let titleFont = UIFont.systemFont(ofSize: 17, weight: .semibold) let barAppearance = UINavigationBarAppearance() barAppearance.configureWithDefaultBackground() barAppearance.titleTextAttributes = [ .foregroundColor: titleColor, .font: titleFont ] // Empty background image = no white capsule on any bar button item let btnAppearance = UIBarButtonItemAppearance(style: .plain) btnAppearance.normal.backgroundImage = UIImage() btnAppearance.highlighted.backgroundImage = UIImage() btnAppearance.disabled.backgroundImage = UIImage() barAppearance.buttonAppearance = btnAppearance UINavigationBar.appearance().standardAppearance = barAppearance UINavigationBar.appearance().compactAppearance = barAppearance UINavigationBar.appearance().scrollEdgeAppearance = barAppearance UNUserNotificationCenter.current().delegate = self // Pre-warm the WKWebView web content process so the first PDF export is instant. DispatchQueue.main.asyncAfter(deadline: .now() + 1.0) { SpotsPDFExporter.prewarm() } // Initialize WatchConnectivity on a background thread. // Two-step approach to prevent any main-thread blocking: // 1. PhoneConnectivityManager.init() is now a no-op (no WCSession call), // so the static-let lock is acquired and released on the BG thread. // 2. activateWCSession() then makes the expensive synchronous Mach IPC // call to the Watch daemon — also on the BG thread. // If ContentView.onAppear accesses .shared before step 1 completes, Swift's // static-let lock will block momentarily on the BG thread — but that lock // is released as soon as init() returns (which is now instant), so the main // thread can never block for more than a few microseconds. DispatchQueue.global(qos: .userInitiated).async { PhoneConnectivityManager.shared.activateWCSession() } // Initialize iCloud container on a background thread at launch so the // iCloud daemon has time to register the container before it's needed. DispatchQueue.global(qos: .utility).async { let url = FileManager.default.url(forUbiquityContainerIdentifier: nil) DispatchQueue.main.async { print("ℹ️ iCloud container initialized at launch: \(url?.path ?? "nil — iCloud unavailable")") } } return true } func userNotificationCenter(_ center: UNUserNotificationCenter, willPresent notification: UNNotification, withCompletionHandler completionHandler: @escaping (UNNotificationPresentationOptions) -> Void) { completionHandler([.banner, .sound]) } func userNotificationCenter(_ center: UNUserNotificationCenter, didReceive response: UNNotificationResponse, withCompletionHandler completionHandler: @escaping () -> Void) { completionHandler() } }`AppDelegate` classDefines the `AppDelegate` class. Conforms to NSObject, UIApplicationDelegate, UNUserNotificationCenterDelegate.