From c919cda38cc30d788a4cb0dc8249c064e3b4f272 Mon Sep 17 00:00:00 2001 From: Daniel Eden Date: Sat, 6 Jul 2024 23:32:45 +0100 Subject: [PATCH] Lots of housekeeping (but now location is broken lol) --- Localizable.xcstrings | 12 +- Solstice.xcodeproj/project.pbxproj | 54 ++---- .../Solstice watchOS Watch App.xcscheme | 3 +- .../xcshareddata/xcschemes/Solstice.xcscheme | 6 +- Solstice/Charts/AnnualDaylightChart.swift | 3 +- Solstice/ContentView.swift | 8 - Solstice/Detail View/DetailView.swift | 1 - .../Extensions/CLLocationCoordinate2D++.swift | 15 -- .../LocationPermissionScreenerView.swift | 43 +++++ Solstice/Helpers/AnyLocation.swift | 17 +- Solstice/Helpers/CurrentLocation.swift | 171 ++++++------------ Solstice/Helpers/LocationSearchService.swift | 1 - Solstice/Helpers/Tips.swift | 21 --- Solstice/List View/LocationListRow.swift | 3 +- Solstice/List View/SidebarListView.swift | 8 +- Solstice/LocationPermissionScreenerView.swift | 74 -------- Solstice/SolsticeApp.swift | 19 +- watchOS/ContentView.swift | 6 - 18 files changed, 142 insertions(+), 323 deletions(-) create mode 100644 Solstice/Helper Views/LocationPermissionScreenerView.swift delete mode 100644 Solstice/Helpers/Tips.swift delete mode 100644 Solstice/LocationPermissionScreenerView.swift diff --git a/Localizable.xcstrings b/Localizable.xcstrings index 493cf344..cfeddd8a 100644 --- a/Localizable.xcstrings +++ b/Localizable.xcstrings @@ -1552,6 +1552,9 @@ } } } + }, + "Set up" : { + }, "Set Up Location Access" : { "extractionState" : "stale", @@ -1574,9 +1577,6 @@ } } } - }, - "Set up location services" : { - }, "Set Up Location Services" : { "extractionState" : "stale", @@ -2004,9 +2004,6 @@ } } } - }, - "Welcome to Solstice" : { - }, "What location do you want to see the daylight for?" : { "localizations" : { @@ -2037,9 +2034,6 @@ } } } - }, - "You can use Solstice with or without your real location. Tap this button to allow or deny access to your location." : { - }, "You will need to enable location services for Solstice or choose a different location in order to receive notifications." : { "localizations" : { diff --git a/Solstice.xcodeproj/project.pbxproj b/Solstice.xcodeproj/project.pbxproj index c4b96f74..7b642066 100644 --- a/Solstice.xcodeproj/project.pbxproj +++ b/Solstice.xcodeproj/project.pbxproj @@ -12,7 +12,7 @@ 7106C86F29A276B40007A7EC /* WidgetBundle.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7106C86E29A276B40007A7EC /* WidgetBundle.swift */; }; 7106C87329A276B40007A7EC /* Widget.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7106C87229A276B40007A7EC /* Widget.swift */; }; 7106C87629A276B40007A7EC /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 7106C87529A276B40007A7EC /* Assets.xcassets */; }; - 7106C87C29A276B40007A7EC /* Widget Extension.appex in Embed Foundation Extensions */ = {isa = PBXBuildFile; fileRef = 7106C86829A276B30007A7EC /* Widget Extension.appex */; platformFilters = (ios, macos, ); settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; }; + 7106C87C29A276B40007A7EC /* Widget Extension.appex in Embed Foundation Extensions */ = {isa = PBXBuildFile; fileRef = 7106C86829A276B30007A7EC /* Widget Extension.appex */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; }; 7106C88129A277460007A7EC /* CurrentLocation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 719846AF28EEE48900E866CE /* CurrentLocation.swift */; }; 7106C88229A2774A0007A7EC /* TimeMachine.swift in Sources */ = {isa = PBXBuildFile; fileRef = 71F641C0299FCCFF00FE5AB5 /* TimeMachine.swift */; }; 7106C88329A277550007A7EC /* Solstice.xcdatamodeld in Sources */ = {isa = PBXBuildFile; fileRef = 7198468F28E5895F00E866CE /* Solstice.xcdatamodeld */; }; @@ -49,7 +49,7 @@ 713F7FFF29BE88E800BEA156 /* DaylightSummaryTitle.swift in Sources */ = {isa = PBXBuildFile; fileRef = 713F7FFE29BE88E800BEA156 /* DaylightSummaryTitle.swift */; }; 713F800029BE88E800BEA156 /* DaylightSummaryTitle.swift in Sources */ = {isa = PBXBuildFile; fileRef = 713F7FFE29BE88E800BEA156 /* DaylightSummaryTitle.swift */; }; 713F800129BE88E800BEA156 /* DaylightSummaryTitle.swift in Sources */ = {isa = PBXBuildFile; fileRef = 713F7FFE29BE88E800BEA156 /* DaylightSummaryTitle.swift */; }; - 71475FE729ACF87E0031D576 /* Solstice watchOS Watch App.app in Embed Watch Content */ = {isa = PBXBuildFile; fileRef = 719F924329ACD1CC00C06921 /* Solstice watchOS Watch App.app */; platformFilter = ios; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; }; + 71475FE729ACF87E0031D576 /* Solstice watchOS Watch App.app in Embed Watch Content */ = {isa = PBXBuildFile; fileRef = 719F924329ACD1CC00C06921 /* Solstice watchOS Watch App.app */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; }; 714A944629C7318D00C67A4A /* SidebarListView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 714A944529C7318D00C67A4A /* SidebarListView.swift */; }; 7150CC5A29AB7B3000E6B90C /* NotificationManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 71BD5E3F29A785FE00E40C01 /* NotificationManager.swift */; }; 7150CC5B29AB7B3C00E6B90C /* AppStorage++.swift in Sources */ = {isa = PBXBuildFile; fileRef = 717F8BB829A8CAFC0015ECCB /* AppStorage++.swift */; }; @@ -207,8 +207,6 @@ 71A029BA2C36928400E19819 /* defaultData.json in Resources */ = {isa = PBXBuildFile; fileRef = 71A029B82C36928400E19819 /* defaultData.json */; }; 71A029BB2C36928400E19819 /* defaultData.json in Resources */ = {isa = PBXBuildFile; fileRef = 71A029B82C36928400E19819 /* defaultData.json */; }; 71A029BC2C36928400E19819 /* defaultData.json in Resources */ = {isa = PBXBuildFile; fileRef = 71A029B82C36928400E19819 /* defaultData.json */; }; - 71A029C12C38A5F900E19819 /* Tips.swift in Sources */ = {isa = PBXBuildFile; fileRef = 71A029C02C38A5F900E19819 /* Tips.swift */; }; - 71A029C22C38A5F900E19819 /* Tips.swift in Sources */ = {isa = PBXBuildFile; fileRef = 71A029C02C38A5F900E19819 /* Tips.swift */; }; 71A2BDE629B747940071ACE9 /* Globals.swift in Sources */ = {isa = PBXBuildFile; fileRef = 71A2BDE529B747940071ACE9 /* Globals.swift */; }; 71A2BDE729B747940071ACE9 /* Globals.swift in Sources */ = {isa = PBXBuildFile; fileRef = 71A2BDE529B747940071ACE9 /* Globals.swift */; }; 71A2BDE829B747940071ACE9 /* Globals.swift in Sources */ = {isa = PBXBuildFile; fileRef = 71A2BDE529B747940071ACE9 /* Globals.swift */; }; @@ -381,7 +379,6 @@ 71A029B12C3052BC00E19819 /* EquinoxAndSolsticeDescriptions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EquinoxAndSolsticeDescriptions.swift; sourceTree = ""; }; 71A029B32C368E1900E19819 /* SavedLocation++.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "SavedLocation++.swift"; sourceTree = ""; }; 71A029B82C36928400E19819 /* defaultData.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = defaultData.json; sourceTree = ""; }; - 71A029C02C38A5F900E19819 /* Tips.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Tips.swift; sourceTree = ""; }; 71A2BDE529B747940071ACE9 /* Globals.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Globals.swift; sourceTree = ""; }; 71A9A54F2AB82D6200C3A38C /* Localizable.xcstrings */ = {isa = PBXFileReference; lastKnownFileType = text.json.xcstrings; path = Localizable.xcstrings; sourceTree = ""; }; 71AEB7B029AE0A9B00A7952D /* AnyLocation.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AnyLocation.swift; sourceTree = ""; }; @@ -551,10 +548,11 @@ 7195127F29B713D6009D282F /* Helper Views */ = { isa = PBXGroup; children = ( - 7117008A29A52B04001BE478 /* SkyGradient.swift */, 7195128029B713EC009D282F /* AdaptiveLabeledContent.swift */, - 71EAB72429C3479F00FA043F /* TimeMachineDeactivatorView.swift */, 710D9B702AB4752D0093A9A6 /* ContentToggle.swift */, + 71AEB7BA29AE3F0D00A7952D /* LocationPermissionScreenerView.swift */, + 7117008A29A52B04001BE478 /* SkyGradient.swift */, + 71EAB72429C3479F00FA043F /* TimeMachineDeactivatorView.swift */, ); path = "Helper Views"; sourceTree = ""; @@ -593,7 +591,6 @@ 7198469228E5895F00E866CE /* Info.plist */, 7198469828E5898500E866CE /* StoreKitConfiguration.storekit */, 7198468628E5895E00E866CE /* ContentView.swift */, - 71AEB7BA29AE3F0D00A7952D /* LocationPermissionScreenerView.swift */, 719846A928E992A200E866CE /* LocationSearchResultRow.swift */, 7198468428E5895E00E866CE /* SolsticeApp.swift */, 71F641C629A0F4A900FE5AB5 /* TimeMachineView.swift */, @@ -634,7 +631,6 @@ 71908ACC2AC95A5500C7B610 /* StringBuilder.swift */, 71F641C0299FCCFF00FE5AB5 /* TimeMachine.swift */, 716BE04F2C2E752F004DDCBC /* DeepLinkResolver.swift */, - 71A029C02C38A5F900E19819 /* Tips.swift */, ); path = Helpers; sourceTree = ""; @@ -1045,7 +1041,6 @@ 71DAB3D429CD865800E30148 /* EarthScene.swift in Sources */, 7198468E28E5895F00E866CE /* Persistence.swift in Sources */, 710D9B712AB4752D0093A9A6 /* ContentToggle.swift in Sources */, - 71A029C12C38A5F900E19819 /* Tips.swift in Sources */, 7195128129B713EC009D282F /* AdaptiveLabeledContent.swift in Sources */, 71DAB3D229CD862A00E30148 /* EarthNode.swift in Sources */, 71EAB72529C3479F00FA043F /* TimeMachineDeactivatorView.swift in Sources */, @@ -1129,7 +1124,6 @@ 716BE0522C2E7E53004DDCBC /* LocationSearchService.swift in Sources */, 719F927429ACD21300C06921 /* SolsticeApp.swift in Sources */, 713F7FF629BE091D00BEA156 /* TimeMachineView.swift in Sources */, - 71A029C22C38A5F900E19819 /* Tips.swift in Sources */, 719F927029ACD21300C06921 /* DetailView.swift in Sources */, 719F928729ACD28500C06921 /* Solstice.xcdatamodeld in Sources */, 71A029B52C368E1900E19819 /* SavedLocation++.swift in Sources */, @@ -1162,10 +1156,6 @@ /* Begin PBXTargetDependency section */ 7106C87B29A276B40007A7EC /* PBXTargetDependency */ = { isa = PBXTargetDependency; - platformFilters = ( - ios, - macos, - ); target = 7106C86729A276B30007A7EC /* Widget Extension */; targetProxy = 7106C87A29A276B40007A7EC /* PBXContainerItemProxy */; }; @@ -1176,7 +1166,6 @@ }; 71AEB7AF29AD0F2100A7952D /* PBXTargetDependency */ = { isa = PBXTargetDependency; - platformFilter = ios; target = 719F924229ACD1CC00C06921 /* Solstice watchOS Watch App */; targetProxy = 71AEB7AE29AD0F2100A7952D /* PBXContainerItemProxy */; }; @@ -1208,7 +1197,6 @@ INFOPLIST_FILE = Widget/Info.plist; INFOPLIST_KEY_CFBundleDisplayName = Widget; INFOPLIST_KEY_NSHumanReadableCopyright = ""; - INFOPLIST_KEY_NSLocationWhenInUseUsageDescription = "Solstice uses your location to calculate local sunrise and sunset times"; IPHONEOS_DEPLOYMENT_TARGET = 16.1; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", @@ -1243,7 +1231,6 @@ INFOPLIST_FILE = Widget/Info.plist; INFOPLIST_KEY_CFBundleDisplayName = Widget; INFOPLIST_KEY_NSHumanReadableCopyright = ""; - INFOPLIST_KEY_NSLocationWhenInUseUsageDescription = "Solstice uses your location to calculate local sunrise and sunset times"; IPHONEOS_DEPLOYMENT_TARGET = 16.1; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", @@ -1278,7 +1265,6 @@ INFOPLIST_FILE = Widget/Info.plist; INFOPLIST_KEY_CFBundleDisplayName = Widget; INFOPLIST_KEY_NSHumanReadableCopyright = ""; - INFOPLIST_KEY_NSLocationWhenInUseUsageDescription = "Solstice uses your location to calculate local sunrise and sunset times"; IPHONEOS_DEPLOYMENT_TARGET = 16.1; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", @@ -1314,7 +1300,6 @@ INFOPLIST_FILE = Widget/Info.plist; INFOPLIST_KEY_CFBundleDisplayName = Widget; INFOPLIST_KEY_NSHumanReadableCopyright = ""; - INFOPLIST_KEY_NSLocationWhenInUseUsageDescription = "Solstice uses your location to calculate local sunrise and sunset times"; IPHONEOS_DEPLOYMENT_TARGET = 16.1; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", @@ -1376,7 +1361,7 @@ ENABLE_STRICT_OBJC_MSGSEND = YES; ENABLE_TESTABILITY = YES; ENABLE_USER_SCRIPT_SANDBOXING = YES; - GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_C_LANGUAGE_STANDARD = gnu17; GCC_DYNAMIC_NO_PIC = NO; GCC_NO_COMMON_BLOCKS = YES; GCC_OPTIMIZATION_LEVEL = 0; @@ -1390,11 +1375,14 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; + INFOPLIST_KEY_NSLocationWhenInUseUsageDescription = "Solstice uses your location to calculate local sunrise and sunset times"; + LOCALIZATION_PREFERS_STRING_CATALOGS = YES; MARKETING_VERSION = 2.2; MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; MTL_FAST_MATH = YES; ONLY_ACTIVE_ARCH = YES; SDKROOT = xros; + SUPPORTED_PLATFORMS = ""; SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; SWIFT_OPTIMIZATION_LEVEL = "-Onone"; }; @@ -1439,7 +1427,7 @@ ENABLE_NS_ASSERTIONS = NO; ENABLE_STRICT_OBJC_MSGSEND = YES; ENABLE_USER_SCRIPT_SANDBOXING = YES; - GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_C_LANGUAGE_STANDARD = gnu17; GCC_NO_COMMON_BLOCKS = YES; GCC_WARN_64_TO_32_BIT_CONVERSION = YES; GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; @@ -1447,34 +1435,34 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; + INFOPLIST_KEY_NSLocationWhenInUseUsageDescription = "Solstice uses your location to calculate local sunrise and sunset times"; + LOCALIZATION_PREFERS_STRING_CATALOGS = YES; MARKETING_VERSION = 2.2; MTL_ENABLE_DEBUG_INFO = NO; MTL_FAST_MATH = YES; SDKROOT = xros; + SUPPORTED_PLATFORMS = ""; SWIFT_COMPILATION_MODE = wholemodule; SWIFT_OPTIMIZATION_LEVEL = "-O"; - VALIDATE_PRODUCT = YES; }; name = Release; }; 7198469628E5895F00E866CE /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { - ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; - ASSETCATALOG_COMPILER_GENERATE_ASSET_SYMBOLS = NO; ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; CODE_SIGN_ENTITLEMENTS = Solstice/Solstice.entitlements; CODE_SIGN_STYLE = Automatic; CURRENT_PROJECT_VERSION = 1; DEVELOPMENT_ASSET_PATHS = "\"Solstice/Preview Content\""; DEVELOPMENT_TEAM = YC249PY26F; + ENABLE_HARDENED_RUNTIME = YES; ENABLE_PREVIEWS = YES; GENERATE_INFOPLIST_FILE = YES; INFOPLIST_FILE = Solstice/Info.plist; INFOPLIST_KEY_CFBundleDisplayName = Solstice; INFOPLIST_KEY_LSApplicationCategoryType = "public.app-category.weather"; - INFOPLIST_KEY_NSLocationWhenInUseUsageDescription = "Solstice uses your location to calculate local sunrise and sunset times"; INFOPLIST_KEY_UIApplicationSceneManifest_Generation = YES; INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES; INFOPLIST_KEY_UILaunchScreen_Generation = YES; @@ -1495,28 +1483,26 @@ SUPPORTS_XR_DESIGNED_FOR_IPHONE_IPAD = NO; SWIFT_EMIT_LOC_STRINGS = YES; SWIFT_VERSION = 5.0; - TARGETED_DEVICE_FAMILY = "1,2"; + TARGETED_DEVICE_FAMILY = "1,2,7"; }; name = Debug; }; 7198469728E5895F00E866CE /* Release */ = { isa = XCBuildConfiguration; buildSettings = { - ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; - ASSETCATALOG_COMPILER_GENERATE_ASSET_SYMBOLS = NO; ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; CODE_SIGN_ENTITLEMENTS = Solstice/Solstice.entitlements; CODE_SIGN_STYLE = Automatic; CURRENT_PROJECT_VERSION = 1; DEVELOPMENT_ASSET_PATHS = "\"Solstice/Preview Content\""; DEVELOPMENT_TEAM = YC249PY26F; + ENABLE_HARDENED_RUNTIME = YES; ENABLE_PREVIEWS = YES; GENERATE_INFOPLIST_FILE = YES; INFOPLIST_FILE = Solstice/Info.plist; INFOPLIST_KEY_CFBundleDisplayName = Solstice; INFOPLIST_KEY_LSApplicationCategoryType = "public.app-category.weather"; - INFOPLIST_KEY_NSLocationWhenInUseUsageDescription = "Solstice uses your location to calculate local sunrise and sunset times"; INFOPLIST_KEY_UIApplicationSceneManifest_Generation = YES; INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES; INFOPLIST_KEY_UILaunchScreen_Generation = YES; @@ -1537,7 +1523,7 @@ SUPPORTS_XR_DESIGNED_FOR_IPHONE_IPAD = NO; SWIFT_EMIT_LOC_STRINGS = YES; SWIFT_VERSION = 5.0; - TARGETED_DEVICE_FAMILY = "1,2"; + TARGETED_DEVICE_FAMILY = "1,2,7"; }; name = Release; }; @@ -1555,8 +1541,6 @@ GENERATE_INFOPLIST_FILE = YES; INFOPLIST_FILE = watchOS/Info.plist; INFOPLIST_KEY_CFBundleDisplayName = Solstice; - INFOPLIST_KEY_NSLocationAlwaysAndWhenInUseUsageDescription = "Solstice uses your location to calculate sunrise and sunset times"; - INFOPLIST_KEY_NSLocationWhenInUseUsageDescription = "Solstice uses your location to calculate sunrise and sunset times"; INFOPLIST_KEY_UISupportedInterfaceOrientations = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown"; INFOPLIST_KEY_WKCompanionAppBundleIdentifier = me.daneden.Solstice; INFOPLIST_KEY_WKRunsIndependentlyOfCompanionApp = YES; @@ -1568,6 +1552,7 @@ PRODUCT_NAME = "$(TARGET_NAME)"; SDKROOT = watchos; SKIP_INSTALL = YES; + SUPPORTED_PLATFORMS = "watchsimulator watchos"; SWIFT_EMIT_LOC_STRINGS = YES; SWIFT_VERSION = 5.0; TARGETED_DEVICE_FAMILY = 4; @@ -1589,8 +1574,6 @@ GENERATE_INFOPLIST_FILE = YES; INFOPLIST_FILE = watchOS/Info.plist; INFOPLIST_KEY_CFBundleDisplayName = Solstice; - INFOPLIST_KEY_NSLocationAlwaysAndWhenInUseUsageDescription = "Solstice uses your location to calculate sunrise and sunset times"; - INFOPLIST_KEY_NSLocationWhenInUseUsageDescription = "Solstice uses your location to calculate sunrise and sunset times"; INFOPLIST_KEY_UISupportedInterfaceOrientations = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown"; INFOPLIST_KEY_WKCompanionAppBundleIdentifier = me.daneden.Solstice; INFOPLIST_KEY_WKRunsIndependentlyOfCompanionApp = YES; @@ -1602,6 +1585,7 @@ PRODUCT_NAME = "$(TARGET_NAME)"; SDKROOT = watchos; SKIP_INSTALL = YES; + SUPPORTED_PLATFORMS = "watchsimulator watchos"; SWIFT_EMIT_LOC_STRINGS = YES; SWIFT_VERSION = 5.0; TARGETED_DEVICE_FAMILY = 4; diff --git a/Solstice.xcodeproj/xcshareddata/xcschemes/Solstice watchOS Watch App.xcscheme b/Solstice.xcodeproj/xcshareddata/xcschemes/Solstice watchOS Watch App.xcscheme index 8e312568..74b354ed 100644 --- a/Solstice.xcodeproj/xcshareddata/xcschemes/Solstice watchOS Watch App.xcscheme +++ b/Solstice.xcodeproj/xcshareddata/xcschemes/Solstice watchOS Watch App.xcscheme @@ -4,7 +4,8 @@ version = "1.7"> + buildImplicitDependencies = "YES" + buildArchitectures = "Automatic"> + buildImplicitDependencies = "YES" + buildArchitectures = "Automatic"> + + : View { @@ -107,7 +106,7 @@ extension AnnualDaylightChart { } return dates.map { date in - return Solar(for: date, coordinate: CLLocationCoordinate2D(latitude: location.latitude, longitude: location.longitude)) + return Solar(for: date, coordinate: location.coordinate) }.compactMap { $0 } } } diff --git a/Solstice/ContentView.swift b/Solstice/ContentView.swift index c7515dbf..988cbcb0 100644 --- a/Solstice/ContentView.swift +++ b/Solstice/ContentView.swift @@ -8,7 +8,6 @@ import SwiftUI import CoreData import Solar -import CoreLocation struct ContentView: View { @AppStorage(Preferences.listViewSortDimension) private var itemSortDimension @@ -77,13 +76,6 @@ struct ContentView: View { } } .resolveDeepLink(Array(items)) - .task(id: scenePhase) { - if currentLocation.isAuthorized, - selectedLocation == currentLocation.id, - scenePhase == .active { - currentLocation.requestLocation() - } - } .overlay { TimelineView(.everyMinute) { timelineContext in Color.clear.task(id: timelineContext.date) { diff --git a/Solstice/Detail View/DetailView.swift b/Solstice/Detail View/DetailView.swift index c30c0f92..3797ef91 100644 --- a/Solstice/Detail View/DetailView.swift +++ b/Solstice/Detail View/DetailView.swift @@ -7,7 +7,6 @@ import SwiftUI import Solar -import CoreLocation struct DetailView: View { static var userActivity: String { diff --git a/Solstice/Extensions/CLLocationCoordinate2D++.swift b/Solstice/Extensions/CLLocationCoordinate2D++.swift index 3e3ca10d..62e7f8ed 100644 --- a/Solstice/Extensions/CLLocationCoordinate2D++.swift +++ b/Solstice/Extensions/CLLocationCoordinate2D++.swift @@ -8,21 +8,6 @@ import Foundation import CoreLocation -extension CLLocationCoordinate2D: RawRepresentable { - public init?(rawValue: String) { - let parts = rawValue.split(separator: ",", maxSplits: 2) - guard let lat = Double(parts.first ?? .init()), let long = Double(parts.last ?? .init()) else { - return nil - } - - self = CLLocationCoordinate2D(latitude: lat, longitude: long) - } - - public var rawValue: String { - "\(latitude),\(longitude)" - } -} - extension CLLocationCoordinate2D { static var proxiedToTimeZone: CLLocationCoordinate2D { let offset = Double(TimeZone.current.secondsFromGMT()) / (60 * 60) diff --git a/Solstice/Helper Views/LocationPermissionScreenerView.swift b/Solstice/Helper Views/LocationPermissionScreenerView.swift new file mode 100644 index 00000000..0242fa44 --- /dev/null +++ b/Solstice/Helper Views/LocationPermissionScreenerView.swift @@ -0,0 +1,43 @@ +// +// LocationPermissionScreenerView.swift +// Solstice +// +// Created by Daniel Eden on 28/02/2023. +// + +import SwiftUI + +struct LocationPermissionScreenerView: View { + @Environment(\.openURL) var openURL + @EnvironmentObject var currentLocation: CurrentLocation + + var body: some View { + AdaptiveLabeledContent { + Button { + currentLocation.requestAccess() + } label: { + Text("Set up") + } + .buttonStyle(.bordered) + + } label: { + HStack { + Image(systemName: "location") + .imageScale(.small) + .foregroundStyle(.secondary) + .symbolVariant(.fill) + + Text("Current location") + } + } + .padding(.vertical, 6) + } +} + +struct LocationPermissionScreenerView_Previews: PreviewProvider { + static var previews: some View { + Form { + LocationPermissionScreenerView() + } + } +} diff --git a/Solstice/Helpers/AnyLocation.swift b/Solstice/Helpers/AnyLocation.swift index 9e934b7e..098e6ecb 100644 --- a/Solstice/Helpers/AnyLocation.swift +++ b/Solstice/Helpers/AnyLocation.swift @@ -10,9 +10,9 @@ import CoreLocation import CoreData protocol AnyLocation: Hashable { - var title: String? { get set } - var subtitle: String? { get set } - var timeZoneIdentifier: String? { get set } + var title: String? { get } + var subtitle: String? { get } + var timeZoneIdentifier: String? { get } var latitude: Double { get } var longitude: Double { get } } @@ -32,17 +32,6 @@ extension AnyLocation { var coordinate: CLLocationCoordinate2D { CLLocationCoordinate2D(latitude: latitude, longitude: longitude) } - - mutating func reverseGeocodeLocation() async { - guard let placemarks = try? await CLGeocoder().reverseGeocodeLocation(CLLocation(latitude: coordinate.latitude, longitude: coordinate.longitude)), - let placemark = placemarks.first else { - return - } - - title = placemark.name ?? title - subtitle = placemark.country ?? subtitle - timeZoneIdentifier = placemark.timeZone?.identifier ?? timeZoneIdentifier - } } extension SavedLocation: ObservableLocation { } diff --git a/Solstice/Helpers/CurrentLocation.swift b/Solstice/Helpers/CurrentLocation.swift index 1576d534..261e0f18 100644 --- a/Solstice/Helpers/CurrentLocation.swift +++ b/Solstice/Helpers/CurrentLocation.swift @@ -9,106 +9,68 @@ import CoreLocation import SwiftUI import Combine -#if canImport(WidgetKit) -import WidgetKit -#endif - -class CurrentLocation: NSObject, ObservableObject, ObservableLocation, Identifiable { - static let identifier = "currentLocation" - @AppStorage(Preferences.cachedLatitude) var latitude - @AppStorage(Preferences.cachedLongitude) var longitude - - @Published var title: String? - @Published var subtitle: String? - let id = CurrentLocation.identifier - +class CurrentLocation: NSObject, ObservableObject, CLLocationManagerDelegate { @Published private(set) var placemark: CLPlacemark? - @Published var timeZoneIdentifier: String? - private(set) var location: CLLocation? { didSet { - Task { await processLocation(location) } + Task { + await processLocation(location) + await NotificationManager.scheduleNotifications(currentLocation: self) + } } } - var coordinate: CLLocationCoordinate2D { - CLLocationCoordinate2D(latitude: latitude, longitude: longitude) - } - private let locationManager = CLLocationManager() private let geocoder = CLGeocoder() - private var sink: AnyCancellable? = nil - override init() { super.init() - + locationManager.delegate = self locationManager.desiredAccuracy = kCLLocationAccuracyReduced - - sink = locationManager.publisher(for: \.location).sink { location in - self.location = location - } } func requestAccess() { -#if os(macOS) - self.locationManager.requestAlwaysAuthorization() -#else self.locationManager.requestWhenInUseAuthorization() -#endif + } + + func requestLocation() { + guard isAuthorized else { return } + + if #available(iOS 17, watchOS 10, macOS 14, visionOS 1, *) { + Task { + do { + try await requestLocation() + } catch { + print("Error requesting location: \(error.localizedDescription)") + } + } + } else { + legacyRequestLocation() + } + } + + @available(iOS 17, watchOS 10, macOS 14, visionOS 1, *) + func requestLocation() async throws { + let updates = CLLocationUpdate.liveUpdates() + for try await update in updates { + guard let location = update.location else { return } + + self.location = location + break + } } } // MARK: Location update request methods and handlers extension CurrentLocation { - @MainActor func processLocation(_ location: CLLocation?) async -> Void { + @MainActor func processLocation(_ location: CLLocation?) async { guard let location else { return } - latitude = location.coordinate.latitude - longitude = location.coordinate.longitude - let reverseGeocoded = try? await geocoder.reverseGeocodeLocation(location) if let firstResult = reverseGeocoded?.first { placemark = firstResult - title = firstResult.locality - subtitle = firstResult.country - timeZoneIdentifier = firstResult.timeZone?.identifier - } - } - - @available(iOS 17, watchOS 10, macOS 14, visionOS 2, *) - func requestLocation() async { - do { - for try await update in CLLocationUpdate.liveUpdates() { - guard let location = update.location else { return } - - guard let storedLocation = self.location else { - return self.location = location - } - - if storedLocation.distance(from: location) > 10000 { - return self.location = location - } - } - } catch { - print(error.localizedDescription) - } - } - - func requestLocation() { - if #available(iOS 17, watchOS 10, macOS 14, visionOS 2, *) { - Task { - await requestLocation() - } - } else { - locationManager.requestLocation() - locationManager.startUpdatingLocation() - -#if !os(watchOS) && !os(visionOS) - locationManager.startMonitoringSignificantLocationChanges() -#endif } } @@ -123,52 +85,39 @@ extension CurrentLocation { } } - var isAuthorizedForWidgetUpdates: Bool { -#if !os(watchOS) - locationManager.isAuthorizedForWidgetUpdates -#else - isAuthorized -#endif + func locationManagerDidChangeAuthorization(_ manager: CLLocationManager) { + if location == nil && isAuthorized { + requestLocation() + } } } -extension CurrentLocation: CLLocationManagerDelegate { - func locationManagerDidChangeAuthorization(_ manager: CLLocationManager) { -#if canImport(WidgetKit) - WidgetCenter.shared.reloadAllTimelines() -#endif - - if isAuthorized { requestLocation() } +extension CurrentLocation { + // MARK: Fallback location request code + func legacyRequestLocation() { + locationManager.startUpdatingLocation() } func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) { - print("Received location update") - - let newLocation = locations.last - - if location == nil { - updateLocation(newLocation) - } else if let newLocation, let location, - newLocation.distance(from: location) > CLLocationDistance(10_000) { - updateLocation(newLocation) - } else { - print("Location is within 10km of last update") - } + guard let location = locations.last else { return } + self.location = location + manager.stopUpdatingLocation() } - private func updateLocation(_ newLocation: CLLocation?) { - self.location = newLocation - - if newLocation != nil { - Task { await NotificationManager.scheduleNotifications(currentLocation: self) } - -#if canImport(WidgetKit) - WidgetCenter.shared.reloadAllTimelines() -#endif - } - } - - func locationManager(_ manager: CLLocationManager, didFailWithError error: Error) { - print(error) + func locationManager(_ manager: CLLocationManager, didFailWithError error: any Error) { + print("Error with location manager delegate: \(error.localizedDescription)") } } + +extension CurrentLocation: ObservableLocation { + var title: String? { placemark?.locality } + var subtitle: String? { placemark?.country } + var timeZoneIdentifier: String? { placemark?.timeZone?.identifier } + var latitude: Double { location?.coordinate.latitude ?? 0 } + var longitude: Double { location?.coordinate.longitude ?? 0 } +} + +extension CurrentLocation: Identifiable { + static let identifier = "currentLocation" + var id: String { CurrentLocation.identifier } +} diff --git a/Solstice/Helpers/LocationSearchService.swift b/Solstice/Helpers/LocationSearchService.swift index 4443bf97..fbd6e1fa 100644 --- a/Solstice/Helpers/LocationSearchService.swift +++ b/Solstice/Helpers/LocationSearchService.swift @@ -33,7 +33,6 @@ struct LocationSearchResult: Identifiable, Hashable { findCoordinates() } - @available(iOS 16, macOS 13, visionOS 1, *) mutating func findCoordinates() { let geocoder = CLGeocoder() geocoder.geocodeAddressString(name) { [self] (response, error) in diff --git a/Solstice/Helpers/Tips.swift b/Solstice/Helpers/Tips.swift deleted file mode 100644 index d4b84da4..00000000 --- a/Solstice/Helpers/Tips.swift +++ /dev/null @@ -1,21 +0,0 @@ -// -// Tips.swift -// Solstice -// -// Created by Daniel Eden on 05/07/2024. -// - -import Foundation -import TipKit - -struct WelcomeTip: Tip { - var title: Text { - Text("Welcome to Solstice") - } - - var message: Text? { - Text("You can use Solstice with or without your real location. Tap this button to allow or deny access to your location.") - } - - let id = "WelcomeTip" -} diff --git a/Solstice/List View/LocationListRow.swift b/Solstice/List View/LocationListRow.swift index 7ad6901f..9f093773 100644 --- a/Solstice/List View/LocationListRow.swift +++ b/Solstice/List View/LocationListRow.swift @@ -7,7 +7,6 @@ import SwiftUI import Solar -import CoreLocation struct LocationListRow: View { @EnvironmentObject var timeMachine: TimeMachine @@ -18,7 +17,7 @@ struct LocationListRow: View { @State private var showRemainingDaylight = false var solar: Solar? { - Solar(for: timeMachine.date, coordinate: CLLocationCoordinate2D(latitude: location.latitude, longitude: location.longitude)) + Solar(for: timeMachine.date, coordinate: location.coordinate) } var isCurrentLocation: Bool { diff --git a/Solstice/List View/SidebarListView.swift b/Solstice/List View/SidebarListView.swift index 871ebdfb..313460bb 100644 --- a/Solstice/List View/SidebarListView.swift +++ b/Solstice/List View/SidebarListView.swift @@ -32,11 +32,11 @@ struct SidebarListView: View { TimeMachineDeactivatorView() } - if currentLocation.authorizationStatus == .notDetermined { - LocationPermissionScreenerView() - } - Section { + if currentLocation.authorizationStatus == .notDetermined { + LocationPermissionScreenerView() + } + if !currentLocation.isAuthorized && items.isEmpty { VStack { Text("No locations") diff --git a/Solstice/LocationPermissionScreenerView.swift b/Solstice/LocationPermissionScreenerView.swift deleted file mode 100644 index f338b014..00000000 --- a/Solstice/LocationPermissionScreenerView.swift +++ /dev/null @@ -1,74 +0,0 @@ -// -// LocationPermissionScreenerView.swift -// Solstice -// -// Created by Daniel Eden on 28/02/2023. -// - -import SwiftUI -import TipKit - -struct LocationPermissionScreenerView: View { - @Environment(\.openURL) var openURL - @EnvironmentObject var currentLocation: CurrentLocation - - let tip = WelcomeTip() - - var fallbackTipView: some View { - #if !os(watchOS) - GroupBox { - tip.message - } label: { - tip.title - } - #else - VStack(alignment: .leading) { - tip.title.font(.headline) - tip.message - } - #endif - } - - var body: some View { - Section { - #if !os(watchOS) - if #unavailable(iOS 17, macOS 13, visionOS 1) { - fallbackTipView - } - #else - fallbackTipView - #endif - - Button { - currentLocation.requestAccess() - #if os(macOS) - openURL.callAsFunction(URL(string: "x-apple.systempreferences:com.apple.preference.security?Privacy_LocationServices")!) - #endif - } label: { - Text("Set up location services") - } - #if !os(watchOS) - .modify { content in - if #available(iOS 17, macOS 14, *) { - content.popoverTip(WelcomeTip()) - } else { - content - } - } - #endif - #if os(iOS) - .buttonStyle(.borderless) - #elseif os(macOS) - .buttonStyle(.bordered) - #endif - } - } -} - -struct LocationPermissionScreenerView_Previews: PreviewProvider { - static var previews: some View { - Form { - LocationPermissionScreenerView() - } - } -} diff --git a/Solstice/SolsticeApp.swift b/Solstice/SolsticeApp.swift index c8004339..c23369a9 100644 --- a/Solstice/SolsticeApp.swift +++ b/Solstice/SolsticeApp.swift @@ -37,9 +37,7 @@ struct SolsticeApp: App { switch phase { #if !os(watchOS) case .background: - Task { - await NotificationManager.scheduleNotifications(currentLocation: currentLocation) - } + await NotificationManager.scheduleNotifications(currentLocation: currentLocation) #endif case .active: currentLocation.requestLocation() @@ -47,21 +45,6 @@ struct SolsticeApp: App { return } } - .task { - if #available(iOS 17, watchOS 10, macOS 13, *) { - // Configure and load your tips at app launch. - do { - try Tips.configure([ - .displayFrequency(.immediate), - .datastoreLocation(.applicationDefault) - ]) - } - catch { - // Handle TipKit errors - print("Error initializing TipKit \(error.localizedDescription)") - } - } - } .migrateAppFeatures() .environment(\.managedObjectContext, persistenceController.container.viewContext) } diff --git a/watchOS/ContentView.swift b/watchOS/ContentView.swift index 417140f3..5a485d3b 100644 --- a/watchOS/ContentView.swift +++ b/watchOS/ContentView.swift @@ -112,12 +112,6 @@ struct ContentView: View { } } } - .task(id: scenePhase) { - if currentLocation.isAuthorized, - scenePhase != .background { - currentLocation.requestLocation() - } - } } var fallbackBody: some View {