Meshtastic-Apple/Meshtastic/MeshtasticAppDelegate.swift
copilot-swe-agent[bot] 7718132c6f
Merge origin/2.7.10 into firmware-updates - resolve conflicts (CarPlay, onboarding, map cache, DM crash fix)
Co-authored-by: garthvh <1795163+garthvh@users.noreply.github.com>
2026-04-18 18:02:41 +00:00

150 lines
5.1 KiB
Swift

//
// MeshtasticAppDelegate.swift
// Meshtastic
//
// Created by Ben on 8/20/23.
//
import Intents
import SwiftUI
import OSLog
class MeshtasticAppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterDelegate, ObservableObject {
var router: Router?
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]? = nil) -> Bool {
Logger.services.info("🚀 [App] Meshtstic Apple App launched!")
// Default User Default Values
UserDefaults.standard.register(defaults: ["meshMapRecentering": true])
UserDefaults.standard.register(defaults: ["meshMapShowNodeHistory": true])
UserDefaults.standard.register(defaults: ["meshMapShowRouteLines": true])
UNUserNotificationCenter.current().delegate = self
let locationsHandler = LocationsHandler.shared
locationsHandler.startLocationUpdates()
// If a background activity session was previously active, reinstantiate it after the background launch.
if locationsHandler.backgroundActivity {
locationsHandler.backgroundActivity = true
}
if Calendar.current.date(byAdding: .day, value: 1, to: UserDefaults.lastDeviceAPIUpdate)! < Date() {
// lastUpdate is older than 1 day
Task {
Logger.services.info("📋 Device list API data is older than one day, updating...")
try await MeshtasticAPI.shared.refreshDevicesAPIData()
UserDefaults.lastDeviceAPIUpdate = Date()
}
} else {
Logger.services.info("📋 Device list API data update is not needed...")
}
// Initialize TAK Server if enabled
Task { @MainActor in
TAKServerManager.shared.initializeOnStartup()
}
// Request Siri authorization so intent donations work and CarPlay messaging is available.
#if !targetEnvironment(macCatalyst)
INPreferences.requestSiriAuthorization { status in
Logger.services.info("Siri authorization status: \(String(describing: status))")
}
#endif
return true
}
// MARK: - SiriKit Intent Handling
/// Routes incoming SiriKit intents to the appropriate handler for CarPlay and Siri messaging support.
func application(_ application: UIApplication, handlerFor intent: INIntent) -> Any? {
IntentHandler().handler(for: intent)
}
// Lets us show the notification in the app in the foreground
func userNotificationCenter(
_ center: UNUserNotificationCenter,
willPresent notification: UNNotification,
withCompletionHandler completionHandler: @escaping (UNNotificationPresentationOptions) -> Void
) {
completionHandler([.list, .banner, .sound])
}
// This method is called when a user clicks on the notification
func userNotificationCenter(
_ center: UNUserNotificationCenter,
didReceive response: UNNotificationResponse,
withCompletionHandler completionHandler: @escaping () -> Void
) {
let userInfo = response.notification.request.content.userInfo
switch response.actionIdentifier {
case UNNotificationDefaultActionIdentifier:
break
case "messageNotification.thumbsUpAction":
if let channel = userInfo["channel"] as? Int32,
let replyID = userInfo["messageId"] as? Int64 {
Task {
do {
try await AccessoryManager.shared.sendMessage(
message: Tapbacks.thumbsUp.emojiString,
toUserNum: userInfo["userNum"] as? Int64 ?? 0,
channel: channel,
isEmoji: true,
replyID: replyID
)
Logger.services.info("Tapback response sent")
} catch {
Logger.services.error("Failed to retrieve channel or messageId from userInfo")
}
}
}
case "messageNotification.thumbsDownAction":
if let channel = userInfo["channel"] as? Int32,
let replyID = userInfo["messageId"] as? Int64 {
Task {
do {
try await AccessoryManager.shared.sendMessage(
message: Tapbacks.thumbsDown.emojiString,
toUserNum: userInfo["userNum"] as? Int64 ?? 0,
channel: channel,
isEmoji: true,
replyID: replyID
)
Logger.services.info("Tapback response sent")
} catch {
Logger.services.error("Failed to retrieve channel or messageId from userInfo")
}
}
}
case "messageNotification.replyInputAction":
if let userInput = (response as? UNTextInputNotificationResponse)?.userText,
let channel = userInfo["channel"] as? Int32,
let replyID = userInfo["messageId"] as? Int64 {
Task {
do {
try await AccessoryManager.shared.sendMessage(
message: userInput,
toUserNum: userInfo["userNum"] as? Int64 ?? 0,
channel: channel,
isEmoji: false,
replyID: replyID
)
Logger.services.info("Actionable notification reply sent")
} catch {
Logger.services.error("Failed to retrieve user input, channel, or messageId from userInfo")
}
}
}
default:
break
}
if let targetValue = userInfo["target"] as? String,
let deepLink = userInfo["path"] as? String,
let url = URL(string: deepLink) {
Logger.services.info("userNotificationCenter didReceiveResponse handling deeplink: \(targetValue, privacy: .public) \(deepLink, privacy: .public)")
router?.route(url: url)
} else {
Logger.services.error("Failed to handle notification response: \(userInfo, privacy: .public)")
}
completionHandler()
}
}