From 1354427bfe2d681b8267a87a705157626746b101 Mon Sep 17 00:00:00 2001 From: ChDel Date: Tue, 26 Mar 2024 16:45:32 -0700 Subject: [PATCH 1/2] Fixing UserDefault wrapper crashing with enums --- Meshtastic/Enums/AppSettingsEnums.swift | 6 +- Meshtastic/Extensions/UserDefaults.swift | 447 +++++------------- .../Config/Module/DetectionSensorConfig.swift | 2 +- 3 files changed, 115 insertions(+), 340 deletions(-) diff --git a/Meshtastic/Enums/AppSettingsEnums.swift b/Meshtastic/Enums/AppSettingsEnums.swift index 0250119a..c53c3826 100644 --- a/Meshtastic/Enums/AppSettingsEnums.swift +++ b/Meshtastic/Enums/AppSettingsEnums.swift @@ -130,7 +130,7 @@ enum LocationUpdateInterval: Int, CaseIterable, Identifiable { } } -enum MapLayer: String, CaseIterable, Equatable { +enum MapLayer: String, CaseIterable, Equatable, Decodable { case standard case hybrid case satellite @@ -138,7 +138,7 @@ enum MapLayer: String, CaseIterable, Equatable { var localized: String { self.rawValue.localized } } -enum MapTileServer: String, CaseIterable, Identifiable { +enum MapTileServer: String, CaseIterable, Identifiable, Decodable { case openStreetMap case openStreetMapDE case openStreetMapFR @@ -273,7 +273,7 @@ enum OverlayType: String, CaseIterable, Equatable { var localized: String { self.rawValue.localized } } -enum MapOverlayServer: String, CaseIterable, Identifiable { +enum MapOverlayServer: String, CaseIterable, Identifiable, Decodable { case baseReReflectivityCurrent case baseReReflectivityOneHourAgo case echoTopsEetCurrent diff --git a/Meshtastic/Extensions/UserDefaults.swift b/Meshtastic/Extensions/UserDefaults.swift index ccb8a7a3..c714efbf 100644 --- a/Meshtastic/Extensions/UserDefaults.swift +++ b/Meshtastic/Extensions/UserDefaults.swift @@ -13,6 +13,36 @@ import Foundation +@propertyWrapper +struct UserDefault { + let key: UserDefaults.Keys + let defaultValue: T + + init(_ key: UserDefaults.Keys, defaultValue: T) { + self.key = key + self.defaultValue = defaultValue + } + + var wrappedValue: T { + get { + if defaultValue as? any RawRepresentable != nil { + let storedValue = UserDefaults.standard.object(forKey: key.rawValue) + + guard let storedValue, + let data = ("\"\(storedValue)\"".data(using: .utf8)), + let value = (try? JSONDecoder().decode(T.self, from: data)) else { return defaultValue } + + return value + } + + return UserDefaults.standard.object(forKey: key.rawValue) as? T ?? defaultValue + } + set { + UserDefaults.standard.set((newValue as? any RawRepresentable)?.rawValue ?? newValue, forKey: key.rawValue) + } + } +} + extension UserDefaults { enum Keys: String, CaseIterable { case preferredPeripheralId @@ -26,10 +56,16 @@ extension UserDefaults { case meshMapShowNodeHistory case meshMapShowRouteLines case enableMapConvexHull + case enableMapRecentering + case enableMapNodeHistoryPins + case enableMapRouteLines case enableMapTraffic case enableMapPointsOfInterest case enableOfflineMaps + case enableOfflineMapsMBTiles case mapTileServer + case enableOverlayServer + case mapOverlayServer case mapTilesAboveLabels case mapUseLegacy case enableDetectionNotifications @@ -42,340 +78,79 @@ extension UserDefaults { func reset() { Keys.allCases.forEach { removeObject(forKey: $0.rawValue) } } - static var preferredPeripheralId: String { - get { - UserDefaults.standard.string(forKey: "preferredPeripheralId") ?? "" - } - set { - UserDefaults.standard.set(newValue, forKey: "preferredPeripheralId") - } - } - static var preferredPeripheralNum: Int { - get { - UserDefaults.standard.integer(forKey: "preferredPeripheralNum") - } - set { - UserDefaults.standard.set(newValue, forKey: "preferredPeripheralNum") - } - } - static var provideLocation: Bool { - get { - UserDefaults.standard.bool(forKey: "provideLocation") - } set { - UserDefaults.standard.set(newValue, forKey: "provideLocation") - } - } - static var provideLocationInterval: Int { - get { - UserDefaults.standard.integer(forKey: "provideLocationInterval") - } - set { - UserDefaults.standard.set(newValue, forKey: "provideLocationInterval") - } - } - static var mapLayer: MapLayer { - get { - MapLayer(rawValue: UserDefaults.standard.string(forKey: "mapLayer") ?? MapLayer.standard.rawValue) ?? MapLayer.standard - } - set { - UserDefaults.standard.set(newValue.rawValue, forKey: "mapLayer") - } - } - static var meshMapDistance: Double { - get { - UserDefaults.standard.double(forKey: "meshMapDistance") - } - set { - UserDefaults.standard.set(newValue, forKey: "meshMapDistance") - } - } - static var enableMapWaypoints: Bool { - get { - UserDefaults.standard.bool(forKey: "enableMapWaypoints") - } - set { - UserDefaults.standard.set(newValue, forKey: "enableMapWaypoints") - } - } - static var enableMapRecentering: Bool { - get { - UserDefaults.standard.bool(forKey: "meshMapRecentering") - } - set { - UserDefaults.standard.set(newValue, forKey: "meshMapRecentering") - } - } - static var enableMapNodeHistoryPins: Bool { - get { - UserDefaults.standard.bool(forKey: "meshMapShowNodeHistory") - } - set { - UserDefaults.standard.set(newValue, forKey: "meshMapShowNodeHistory") - } - } - static var enableMapRouteLines: Bool { - get { - UserDefaults.standard.bool(forKey: "meshMapShowRouteLines") - } - set { - UserDefaults.standard.set(newValue, forKey: "meshMapShowRouteLines") - } - } - static var enableMapConvexHull: Bool { - get { - UserDefaults.standard.bool(forKey: "enableMapConvexHull") - } - set { - UserDefaults.standard.set(newValue, forKey: "enableMapConvexHull") - } - } - static var enableMapTraffic: Bool { - get { - UserDefaults.standard.bool(forKey: "enableMapTraffic") - } - set { - UserDefaults.standard.set(newValue, forKey: "enableMapTraffic") - } - } - static var enableMapPointsOfInterest: Bool { - get { - UserDefaults.standard.bool(forKey: "enableMapPointsOfInterest") - } - set { - UserDefaults.standard.set(newValue, forKey: "enableMapPointsOfInterest") - } - } - static var enableOfflineMaps: Bool { - get { - UserDefaults.standard.bool(forKey: "enableOfflineMaps") - } - set { - UserDefaults.standard.set(newValue, forKey: "enableOfflineMaps") - } - } - static var enableOfflineMapsMBTiles: Bool { - get { - UserDefaults.standard.bool(forKey: "enableOfflineMapsMBTiles") - } - set { - UserDefaults.standard.set(newValue, forKey: "enableOfflineMapsMBTiles") - } - } - static var mapTileServer: MapTileServer { - get { - MapTileServer(rawValue: UserDefaults.standard.string(forKey: "mapTileServer") ?? MapTileServer.openStreetMap.rawValue) ?? MapTileServer.openStreetMap - } - set { - UserDefaults.standard.set(newValue.rawValue, forKey: "mapTileServer") - } - } - static var enableOverlayServer: Bool { - get { - UserDefaults.standard.bool(forKey: "enableOverlayServer") - } - set { - UserDefaults.standard.set(newValue, forKey: "enableOverlayServer") - } - } - static var mapOverlayServer: MapOverlayServer { - get { - MapOverlayServer(rawValue: UserDefaults.standard.string(forKey: "mapOverlayServer") ?? MapOverlayServer.baseReReflectivityCurrent.rawValue) ?? MapOverlayServer.baseReReflectivityCurrent - } - set { - UserDefaults.standard.set(newValue.rawValue, forKey: "mapOverlayServer") - } - } - static var mapTilesAboveLabels: Bool { - get { - UserDefaults.standard.bool(forKey: "mapTilesAboveLabels") - } - set { - UserDefaults.standard.set(newValue, forKey: "mapTilesAboveLabels") - } - } - - static var mapUseLegacy: Bool { - get { - UserDefaults.standard.bool(forKey: "mapUseLegacy") - } - set { - UserDefaults.standard.set(newValue, forKey: "mapUseLegacy") - } - } - - static var enableDetectionNotifications: Bool { - get { - UserDefaults.standard.bool(forKey: "enableDetectionNotifications") - } - set { - UserDefaults.standard.set(newValue, forKey: "enableDetectionNotifications") - } - } - - static var detectionSensorRole: DetectionSensorRole { - get { - DetectionSensorRole(rawValue: UserDefaults.standard.string(forKey: "detectionSensorRole") ?? DetectionSensorRole.sensor.rawValue) ?? DetectionSensorRole.sensor - } - set { - UserDefaults.standard.set(newValue.rawValue, forKey: "detectionSensorRole") - } - } - static var enableSmartPosition: Bool { - get { - UserDefaults.standard.bool(forKey: "enableSmartPosition") - } - set { - UserDefaults.standard.set(newValue, forKey: "enableSmartPosition") - } - } - static var modemPreset: Int { - get { - UserDefaults.standard.integer(forKey: "modemPreset") - } - set { - UserDefaults.standard.set(newValue, forKey: "modemPreset") - } - } - static var firmwareVersion: String { - get { - UserDefaults.standard.string(forKey: "firmwareVersion") ?? "0.0.0" - } - set { - UserDefaults.standard.set(newValue, forKey: "firmwareVersion") - } - } -} -//import Foundation -// -//@propertyWrapper -//struct UserDefault { -// let key: UserDefaults.Keys -// let defaultValue: T -// -// init(_ key: UserDefaults.Keys, defaultValue: T) { -// self.key = key -// self.defaultValue = defaultValue -// } -// -// var wrappedValue: T { -// get { -// UserDefaults.standard.object(forKey: key.rawValue) as? T ?? defaultValue -// } -// set { -// UserDefaults.standard.set(newValue, forKey: key.rawValue) -// } -// } -//} -// -//extension UserDefaults { -// enum Keys: String, CaseIterable { -// case preferredPeripheralId -// case preferredPeripheralNum -// case provideLocation -// case provideLocationInterval -// case mapLayer -// case meshMapDistance -// case enableMapWaypoints -// case meshMapRecentering -// case meshMapShowNodeHistory -// case meshMapShowRouteLines -// case enableMapConvexHull -// case enableMapRecentering -// case enableMapNodeHistoryPins -// case enableMapRouteLines -// case enableMapTraffic -// case enableMapPointsOfInterest -// case enableOfflineMaps -// case enableOfflineMapsMBTiles -// case mapTileServer -// case enableOverlayServer -// case mapOverlayServer -// case mapTilesAboveLabels -// case mapUseLegacy -// case enableDetectionNotifications -// case detectionSensorRole -// case enableSmartPosition -// case modemPreset -// case firmwareVersion -// } -// -// func reset() { -// Keys.allCases.forEach { removeObject(forKey: $0.rawValue) } -// } -// -// @UserDefault(.preferredPeripheralId, defaultValue: "") -// static var preferredPeripheralId: String -// -// @UserDefault(.preferredPeripheralNum, defaultValue: 0) -// static var preferredPeripheralNum: Int -// -// @UserDefault(.provideLocation, defaultValue: false) -// static var provideLocation: Bool -// -// @UserDefault(.provideLocationInterval, defaultValue: 0) -// static var provideLocationInterval: Int -// -// @UserDefault(.mapLayer, defaultValue: .standard) -// static var mapLayer: MapLayer -// -// @UserDefault(.meshMapDistance, defaultValue: 800000) -// static var meshMapDistance: Double -// -// @UserDefault(.enableMapWaypoints, defaultValue: false) -// static var enableMapWaypoints: Bool -// -// @UserDefault(.enableMapRecentering, defaultValue: false) -// static var enableMapRecentering: Bool -// -// @UserDefault(.enableMapNodeHistoryPins, defaultValue: false) -// static var enableMapNodeHistoryPins: Bool -// -// @UserDefault(.enableMapRouteLines, defaultValue: false) -// static var enableMapRouteLines: Bool -// -// @UserDefault(.enableMapConvexHull, defaultValue: false) -// static var enableMapConvexHull: Bool -// -// @UserDefault(.enableMapTraffic, defaultValue: false) -// static var enableMapTraffic: Bool -// -// @UserDefault(.enableMapPointsOfInterest, defaultValue: false) -// static var enableMapPointsOfInterest: Bool -// -// @UserDefault(.enableOfflineMaps, defaultValue: false) -// static var enableOfflineMaps: Bool -// -// @UserDefault(.enableOfflineMapsMBTiles, defaultValue: false) -// static var enableOfflineMapsMBTiles: Bool -// -// @UserDefault(.mapTileServer, defaultValue: .openStreetMap) -// static var mapTileServer: MapTileServer -// -// @UserDefault(.enableOverlayServer, defaultValue: false) -// static var enableOverlayServer: Bool -// -// @UserDefault(.mapOverlayServer, defaultValue: .baseReReflectivityCurrent) -// static var mapOverlayServer: MapOverlayServer -// -// @UserDefault(.mapTilesAboveLabels, defaultValue: false) -// static var mapTilesAboveLabels: Bool -// -// @UserDefault(.mapUseLegacy, defaultValue: false) -// static var mapUseLegacy: Bool -// -// @UserDefault(.enableDetectionNotifications, defaultValue: false) -// static var enableDetectionNotifications: Bool -// -// @UserDefault(.detectionSensorRole, defaultValue: .sensor) -// static var detectionSensorRole: DetectionSensorRole -// -// @UserDefault(.enableSmartPosition, defaultValue: false) -// static var enableSmartPosition: Bool -// -// @UserDefault(.modemPreset, defaultValue: 0) -// static var modemPreset: Int -// -// @UserDefault(.firmwareVersion, defaultValue: "0.0.0") -// static var firmwareVersion: String -//} + @UserDefault(.preferredPeripheralId, defaultValue: "") + static var preferredPeripheralId: String + + @UserDefault(.preferredPeripheralNum, defaultValue: 0) + static var preferredPeripheralNum: Int + + @UserDefault(.provideLocation, defaultValue: false) + static var provideLocation: Bool + + @UserDefault(.provideLocationInterval, defaultValue: 0) + static var provideLocationInterval: Int + + @UserDefault(.mapLayer, defaultValue: .standard) + static var mapLayer: MapLayer + + @UserDefault(.meshMapDistance, defaultValue: 800000) + static var meshMapDistance: Double + + @UserDefault(.enableMapWaypoints, defaultValue: false) + static var enableMapWaypoints: Bool + + @UserDefault(.enableMapRecentering, defaultValue: false) + static var enableMapRecentering: Bool + + @UserDefault(.enableMapNodeHistoryPins, defaultValue: false) + static var enableMapNodeHistoryPins: Bool + + @UserDefault(.enableMapRouteLines, defaultValue: false) + static var enableMapRouteLines: Bool + + @UserDefault(.enableMapConvexHull, defaultValue: false) + static var enableMapConvexHull: Bool + + @UserDefault(.enableMapTraffic, defaultValue: false) + static var enableMapTraffic: Bool + + @UserDefault(.enableMapPointsOfInterest, defaultValue: false) + static var enableMapPointsOfInterest: Bool + + @UserDefault(.enableOfflineMaps, defaultValue: false) + static var enableOfflineMaps: Bool + + @UserDefault(.enableOfflineMapsMBTiles, defaultValue: false) + static var enableOfflineMapsMBTiles: Bool + + @UserDefault(.mapTileServer, defaultValue: .openStreetMap) + static var mapTileServer: MapTileServer + + @UserDefault(.enableOverlayServer, defaultValue: false) + static var enableOverlayServer: Bool + + @UserDefault(.mapOverlayServer, defaultValue: .baseReReflectivityCurrent) + static var mapOverlayServer: MapOverlayServer + + @UserDefault(.mapTilesAboveLabels, defaultValue: false) + static var mapTilesAboveLabels: Bool + + @UserDefault(.mapUseLegacy, defaultValue: false) + static var mapUseLegacy: Bool + + @UserDefault(.enableDetectionNotifications, defaultValue: false) + static var enableDetectionNotifications: Bool + + @UserDefault(.detectionSensorRole, defaultValue: .sensor) + static var detectionSensorRole: DetectionSensorRole + + @UserDefault(.enableSmartPosition, defaultValue: false) + static var enableSmartPosition: Bool + + @UserDefault(.modemPreset, defaultValue: 0) + static var modemPreset: Int + + @UserDefault(.firmwareVersion, defaultValue: "0.0.0") + static var firmwareVersion: String +} diff --git a/Meshtastic/Views/Settings/Config/Module/DetectionSensorConfig.swift b/Meshtastic/Views/Settings/Config/Module/DetectionSensorConfig.swift index 54570be2..bd00c76e 100644 --- a/Meshtastic/Views/Settings/Config/Module/DetectionSensorConfig.swift +++ b/Meshtastic/Views/Settings/Config/Module/DetectionSensorConfig.swift @@ -6,7 +6,7 @@ // import SwiftUI -enum DetectionSensorRole: String, CaseIterable, Equatable { +enum DetectionSensorRole: String, CaseIterable, Equatable, Decodable { case sensor case client var description: String { From 8149863621e7302b11876190525f839add91c407 Mon Sep 17 00:00:00 2001 From: ChDel Date: Tue, 26 Mar 2024 19:40:48 -0700 Subject: [PATCH 2/2] Allow for numerical enum to decode --- Meshtastic/Extensions/UserDefaults.swift | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/Meshtastic/Extensions/UserDefaults.swift b/Meshtastic/Extensions/UserDefaults.swift index c714efbf..a93a4114 100644 --- a/Meshtastic/Extensions/UserDefaults.swift +++ b/Meshtastic/Extensions/UserDefaults.swift @@ -29,7 +29,8 @@ struct UserDefault { let storedValue = UserDefaults.standard.object(forKey: key.rawValue) guard let storedValue, - let data = ("\"\(storedValue)\"".data(using: .utf8)), + let jsonString = (storedValue as? String != nil) ? "\"\(storedValue)\"" : "\(storedValue)", + let data = jsonString.data(using: .utf8), let value = (try? JSONDecoder().decode(T.self, from: data)) else { return defaultValue } return value @@ -73,6 +74,7 @@ extension UserDefaults { case enableSmartPosition case modemPreset case firmwareVersion + case testIntEnum } func reset() { @@ -153,4 +155,13 @@ extension UserDefaults { @UserDefault(.firmwareVersion, defaultValue: "0.0.0") static var firmwareVersion: String + + @UserDefault(.testIntEnum, defaultValue: .one) + static var testIntEnum: TestIntEnum +} + +enum TestIntEnum: Int, Decodable { + case one = 1 + case two + case three }