diff --git a/Intents/GetSunriseTime.swift b/Intents/GetSunriseTime.swift index 4b290ecf..f404c299 100644 --- a/Intents/GetSunriseTime.swift +++ b/Intents/GetSunriseTime.swift @@ -8,7 +8,6 @@ import Foundation import AppIntents import CoreLocation -import Solar struct GetSunriseTime: AppIntent { static var title: LocalizedStringResource = "Get Sunrise Time" @@ -29,7 +28,7 @@ struct GetSunriseTime: AppIntent { throw $location.needsValueError("What location do you want to see the sunrise for?") } - let solar = Solar(for: date, coordinate: coordinate) + let solar = NTSolar(for: date, coordinate: coordinate, timeZone: location.timeZone ?? .autoupdatingCurrent) return .result( value: solar?.sunrise, diff --git a/Intents/GetSunsetTime.swift b/Intents/GetSunsetTime.swift index 069fb2a1..5d27a793 100644 --- a/Intents/GetSunsetTime.swift +++ b/Intents/GetSunsetTime.swift @@ -8,7 +8,6 @@ import Foundation import AppIntents import CoreLocation -import Solar struct GetSunsetTime: AppIntent { static var title: LocalizedStringResource = "Get Sunset Time" @@ -29,7 +28,7 @@ struct GetSunsetTime: AppIntent { throw $location.needsValueError("What location do you want to see the sunset for?") } - let solar = Solar(for: date, coordinate: coordinate) + let solar = NTSolar(for: date, coordinate: coordinate, timeZone: location.timeZone ?? .autoupdatingCurrent) return .result( value: solar?.sunset, diff --git a/Intents/ViewDaylight.swift b/Intents/ViewDaylight.swift index 43c9854d..746ef861 100644 --- a/Intents/ViewDaylight.swift +++ b/Intents/ViewDaylight.swift @@ -8,7 +8,6 @@ import Foundation import AppIntents import CoreLocation -import Solar struct ViewDaylight: AppIntent { static var title: LocalizedStringResource = "View Daylight" @@ -33,7 +32,7 @@ struct ViewDaylight: AppIntent { formatter.unitsStyle = .full formatter.allowedUnits = [.hour, .minute, .second] - let solar = Solar(for: date, coordinate: coordinate) + let solar = NTSolar(for: date, coordinate: coordinate, timeZone: location.timeZone ?? .autoupdatingCurrent) let duration = (solar?.sunrise ?? .now).distance(to: solar?.sunset ?? .now) diff --git a/Intents/ViewRemainingDaylight.swift b/Intents/ViewRemainingDaylight.swift index f01c0d14..a116e403 100644 --- a/Intents/ViewRemainingDaylight.swift +++ b/Intents/ViewRemainingDaylight.swift @@ -7,7 +7,6 @@ import Foundation import AppIntents -import Solar import CoreLocation struct ViewRemainingDaylight: AppIntent { @@ -26,7 +25,7 @@ struct ViewRemainingDaylight: AppIntent { throw $location.needsValueError("What location do you want to see the daylight for?") } - let solar = Solar(coordinate: coordinate)! + let solar = NTSolar(for: .now, coordinate: coordinate, timeZone: location.timeZone ?? .autoupdatingCurrent)! var resultValue: TimeInterval diff --git a/Localizable.xcstrings b/Localizable.xcstrings index 81adafcf..fd900f83 100644 --- a/Localizable.xcstrings +++ b/Localizable.xcstrings @@ -392,16 +392,6 @@ } } }, - "ceeK/Solar" : { - "localizations" : { - "it" : { - "stringUnit" : { - "state" : "translated", - "value" : "ceeK/Solar" - } - } - } - }, "Change how notifications behave when daily daylight begins to decrease. This can help with Seasonal Affective Disorder." : { "localizations" : { "it" : { @@ -1556,16 +1546,6 @@ } } }, - "Open Source Acknowledgements" : { - "localizations" : { - "it" : { - "stringUnit" : { - "state" : "translated", - "value" : "Ringraziamenti Open Source" - } - } - } - }, "Order" : { "localizations" : { "it" : { diff --git a/Solstice.xcodeproj/project.pbxproj b/Solstice.xcodeproj/project.pbxproj index 57a988d1..146f8a03 100644 --- a/Solstice.xcodeproj/project.pbxproj +++ b/Solstice.xcodeproj/project.pbxproj @@ -17,11 +17,9 @@ 7106C88129A277460007A7EC /* CurrentLocation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 719846AF28EEE48900E866CE /* CurrentLocation.swift */; }; 7106C88329A277550007A7EC /* Solstice.xcdatamodeld in Sources */ = {isa = PBXBuildFile; fileRef = 7198468F28E5895F00E866CE /* Solstice.xcdatamodeld */; }; 7106C88429A2775C0007A7EC /* TimeInterval++.swift in Sources */ = {isa = PBXBuildFile; fileRef = 71F641BC299968BF00FE5AB5 /* TimeInterval++.swift */; }; - 7106C88529A2775C0007A7EC /* Solar++.swift in Sources */ = {isa = PBXBuildFile; fileRef = 719846AC28E9AA0E00E866CE /* Solar++.swift */; }; 7106C88629A2775C0007A7EC /* Date++.swift in Sources */ = {isa = PBXBuildFile; fileRef = 71F641BA2999631D00FE5AB5 /* Date++.swift */; }; 7106C88729A2775C0007A7EC /* View+CapsuleAppearance.swift in Sources */ = {isa = PBXBuildFile; fileRef = 71F641C2299FD39E00FE5AB5 /* View+CapsuleAppearance.swift */; }; 7106C88929A277680007A7EC /* Persistence.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7198468D28E5895F00E866CE /* Persistence.swift */; }; - 7106C88B29A277740007A7EC /* Solar in Frameworks */ = {isa = PBXBuildFile; productRef = 7106C88A29A277740007A7EC /* Solar */; }; 7106C88D29A27D0A0007A7EC /* OverviewWidgetView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7106C88C29A27D0A0007A7EC /* OverviewWidgetView.swift */; }; 710D9B712AB4752D0093A9A6 /* ContentToggle.swift in Sources */ = {isa = PBXBuildFile; fileRef = 710D9B702AB4752D0093A9A6 /* ContentToggle.swift */; }; 710D9B722AB4752D0093A9A6 /* ContentToggle.swift in Sources */ = {isa = PBXBuildFile; fileRef = 710D9B702AB4752D0093A9A6 /* ContentToggle.swift */; }; @@ -160,7 +158,6 @@ 7194653A2C2C1E09008408C0 /* WidgetLocationHeadingView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 71BD5E3A29A7778900E40C01 /* WidgetLocationHeadingView.swift */; }; 7194653B2C2C1E09008408C0 /* CompactLabelStyle.swift in Sources */ = {isa = PBXBuildFile; fileRef = 71F2C3A02ABB282D00E69426 /* CompactLabelStyle.swift */; }; 7194653C2C2C1E09008408C0 /* OverviewWidgetView+AccessoryWidgetViews.swift in Sources */ = {isa = PBXBuildFile; fileRef = 719F558029DB1ABB00D7AE8E /* OverviewWidgetView+AccessoryWidgetViews.swift */; }; - 7194653D2C2C1E09008408C0 /* Solar++.swift in Sources */ = {isa = PBXBuildFile; fileRef = 719846AC28E9AA0E00E866CE /* Solar++.swift */; }; 7194653E2C2C1E09008408C0 /* Globals.swift in Sources */ = {isa = PBXBuildFile; fileRef = 71A2BDE529B747940071ACE9 /* Globals.swift */; }; 7194653F2C2C1E09008408C0 /* SolarChartWidget.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7132AC0B29E6917F00523215 /* SolarChartWidget.swift */; }; 719465402C2C1E09008408C0 /* Date++.swift in Sources */ = {isa = PBXBuildFile; fileRef = 71F641BA2999631D00FE5AB5 /* Date++.swift */; }; @@ -172,7 +169,6 @@ 7194654A2C2C1E09008408C0 /* View+CapsuleAppearance.swift in Sources */ = {isa = PBXBuildFile; fileRef = 71F641C2299FD39E00FE5AB5 /* View+CapsuleAppearance.swift */; }; 7194654C2C2C1E09008408C0 /* DaylightChart.swift in Sources */ = {isa = PBXBuildFile; fileRef = 71F641B829995ED000FE5AB5 /* DaylightChart.swift */; }; 7194654D2C2C1E09008408C0 /* Solstice.xcdatamodeld in Sources */ = {isa = PBXBuildFile; fileRef = 7198468F28E5895F00E866CE /* Solstice.xcdatamodeld */; }; - 7194654F2C2C1E09008408C0 /* Solar in Frameworks */ = {isa = PBXBuildFile; productRef = 7194651E2C2C1E09008408C0 /* Solar */; }; 719465502C2C1E09008408C0 /* SwiftUI.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 7106C86B29A276B40007A7EC /* SwiftUI.framework */; }; 719465512C2C1E09008408C0 /* WidgetKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 7106C86929A276B30007A7EC /* WidgetKit.framework */; }; 719465552C2C1E09008408C0 /* Localizable.xcstrings in Resources */ = {isa = PBXBuildFile; fileRef = 71A9A54F2AB82D6200C3A38C /* Localizable.xcstrings */; }; @@ -194,11 +190,9 @@ 7198468E28E5895F00E866CE /* Persistence.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7198468D28E5895F00E866CE /* Persistence.swift */; }; 7198469128E5895F00E866CE /* Solstice.xcdatamodeld in Sources */ = {isa = PBXBuildFile; fileRef = 7198468F28E5895F00E866CE /* Solstice.xcdatamodeld */; }; 7198469928E5898500E866CE /* StoreKitConfiguration.storekit in Resources */ = {isa = PBXBuildFile; fileRef = 7198469828E5898500E866CE /* StoreKitConfiguration.storekit */; }; - 7198469C28E58CAA00E866CE /* Solar in Frameworks */ = {isa = PBXBuildFile; productRef = 7198469B28E58CAA00E866CE /* Solar */; }; 7198469E28E58CCD00E866CE /* DetailView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7198469D28E58CCD00E866CE /* DetailView.swift */; }; 719846A828E991FA00E866CE /* LocationSearchService.swift in Sources */ = {isa = PBXBuildFile; fileRef = 719846A728E991FA00E866CE /* LocationSearchService.swift */; }; 719846AA28E992A200E866CE /* LocationSearchResultRow.swift in Sources */ = {isa = PBXBuildFile; fileRef = 719846A928E992A200E866CE /* LocationSearchResultRow.swift */; }; - 719846AD28E9AA0E00E866CE /* Solar++.swift in Sources */ = {isa = PBXBuildFile; fileRef = 719846AC28E9AA0E00E866CE /* Solar++.swift */; }; 719846B028EEE48900E866CE /* CurrentLocation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 719846AF28EEE48900E866CE /* CurrentLocation.swift */; }; 7198C40A29DB06A8009A457E /* CountdownWidget.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7198C40929DB06A8009A457E /* CountdownWidget.swift */; }; 7198C40E29DB06C1009A457E /* OverviewWidget.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7198C40D29DB06C1009A457E /* OverviewWidget.swift */; }; @@ -211,7 +205,6 @@ 719F927429ACD21300C06921 /* SolsticeApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7198468428E5895E00E866CE /* SolsticeApp.swift */; }; 719F927529ACD21300C06921 /* Persistence.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7198468D28E5895F00E866CE /* Persistence.swift */; }; 719F927729ACD21A00C06921 /* View+CapsuleAppearance.swift in Sources */ = {isa = PBXBuildFile; fileRef = 71F641C2299FD39E00FE5AB5 /* View+CapsuleAppearance.swift */; }; - 719F927829ACD21A00C06921 /* Solar++.swift in Sources */ = {isa = PBXBuildFile; fileRef = 719846AC28E9AA0E00E866CE /* Solar++.swift */; }; 719F927929ACD21A00C06921 /* Date++.swift in Sources */ = {isa = PBXBuildFile; fileRef = 71F641BA2999631D00FE5AB5 /* Date++.swift */; }; 719F927A29ACD21A00C06921 /* AppStorage++.swift in Sources */ = {isa = PBXBuildFile; fileRef = 717F8BB829A8CAFC0015ECCB /* AppStorage++.swift */; }; 719F927B29ACD21A00C06921 /* TimeInterval++.swift in Sources */ = {isa = PBXBuildFile; fileRef = 71F641BC299968BF00FE5AB5 /* TimeInterval++.swift */; }; @@ -219,7 +212,6 @@ 719F927D29ACD22100C06921 /* CurrentLocation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 719846AF28EEE48900E866CE /* CurrentLocation.swift */; }; 719F927F29ACD22100C06921 /* NotificationManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 71BD5E3F29A785FE00E40C01 /* NotificationManager.swift */; }; 719F928129ACD22100C06921 /* SkyGradient.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7117008A29A52B04001BE478 /* SkyGradient.swift */; }; - 719F928329ACD24700C06921 /* Solar in Frameworks */ = {isa = PBXBuildFile; productRef = 719F928229ACD24700C06921 /* Solar */; }; 719F928729ACD28500C06921 /* Solstice.xcdatamodeld in Sources */ = {isa = PBXBuildFile; fileRef = 7198468F28E5895F00E866CE /* Solstice.xcdatamodeld */; }; 719F928829ACD29100C06921 /* ContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 71DB52AD29ABD19B00D62BB7 /* ContentView.swift */; }; 71A029A92C2E838100E19819 /* EarthSceneKitView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 71A029A82C2E838100E19819 /* EarthSceneKitView.swift */; }; @@ -282,6 +274,16 @@ 71E3C41A2EA12B030005C884 /* View+backportGlassEffect.swift in Sources */ = {isa = PBXBuildFile; fileRef = 71E3C4172EA12B030005C884 /* View+backportGlassEffect.swift */; }; 71E3C41B2EA12B030005C884 /* View+backportGlassEffect.swift in Sources */ = {isa = PBXBuildFile; fileRef = 71E3C4172EA12B030005C884 /* View+backportGlassEffect.swift */; }; 71E8DC2D29CB601800F3E1B4 /* EquinoxAndSolsticeInfoSheet.swift in Sources */ = {isa = PBXBuildFile; fileRef = 71E8DC2C29CB601800F3E1B4 /* EquinoxAndSolsticeInfoSheet.swift */; }; + 71E945802F31FDA600B26D02 /* NTSolar.swift in Sources */ = {isa = PBXBuildFile; fileRef = 71E9457F2F31FDA600B26D02 /* NTSolar.swift */; }; + 71E945812F31FDA600B26D02 /* NTSolar.swift in Sources */ = {isa = PBXBuildFile; fileRef = 71E9457F2F31FDA600B26D02 /* NTSolar.swift */; }; + 71E945822F31FDA600B26D02 /* NTSolar.swift in Sources */ = {isa = PBXBuildFile; fileRef = 71E9457F2F31FDA600B26D02 /* NTSolar.swift */; }; + 71E945832F31FDA600B26D02 /* NTSolar.swift in Sources */ = {isa = PBXBuildFile; fileRef = 71E9457F2F31FDA600B26D02 /* NTSolar.swift */; }; + 71E9459E2F32095300B26D02 /* NTSolar++.swift in Sources */ = {isa = PBXBuildFile; fileRef = 71E9459D2F32095300B26D02 /* NTSolar++.swift */; }; + 71E9459F2F32095300B26D02 /* NTSolar++.swift in Sources */ = {isa = PBXBuildFile; fileRef = 71E9459D2F32095300B26D02 /* NTSolar++.swift */; }; + 71E945A02F32095300B26D02 /* NTSolar++.swift in Sources */ = {isa = PBXBuildFile; fileRef = 71E9459D2F32095300B26D02 /* NTSolar++.swift */; }; + 71E945A12F32095300B26D02 /* NTSolar++.swift in Sources */ = {isa = PBXBuildFile; fileRef = 71E9459D2F32095300B26D02 /* NTSolar++.swift */; }; + 71E945A22F32095300B26D02 /* NTSolar++.swift in Sources */ = {isa = PBXBuildFile; fileRef = 71E9459D2F32095300B26D02 /* NTSolar++.swift */; }; + 71E945A32F32095300B26D02 /* NTSolar++.swift in Sources */ = {isa = PBXBuildFile; fileRef = 71E9459D2F32095300B26D02 /* NTSolar++.swift */; }; 71F2C3A12ABB282D00E69426 /* CompactLabelStyle.swift in Sources */ = {isa = PBXBuildFile; fileRef = 71F2C3A02ABB282D00E69426 /* CompactLabelStyle.swift */; }; 71F58A412E617E08007A5E45 /* ShareSolarChartView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7124339C2D77B06900709C20 /* ShareSolarChartView.swift */; }; 71F641B729994DD900FE5AB5 /* LocationListRow.swift in Sources */ = {isa = PBXBuildFile; fileRef = 71F641B629994DD900FE5AB5 /* LocationListRow.swift */; }; @@ -454,7 +456,6 @@ 7198469D28E58CCD00E866CE /* DetailView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DetailView.swift; sourceTree = ""; }; 719846A728E991FA00E866CE /* LocationSearchService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LocationSearchService.swift; sourceTree = ""; }; 719846A928E992A200E866CE /* LocationSearchResultRow.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LocationSearchResultRow.swift; sourceTree = ""; }; - 719846AC28E9AA0E00E866CE /* Solar++.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Solar++.swift"; sourceTree = ""; }; 719846AF28EEE48900E866CE /* CurrentLocation.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CurrentLocation.swift; sourceTree = ""; }; 7198C40929DB06A8009A457E /* CountdownWidget.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CountdownWidget.swift; sourceTree = ""; }; 7198C40D29DB06C1009A457E /* OverviewWidget.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OverviewWidget.swift; sourceTree = ""; }; @@ -492,6 +493,8 @@ 71E3C4122EA12ABE0005C884 /* CircleWithSlice.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CircleWithSlice.swift; sourceTree = ""; }; 71E3C4172EA12B030005C884 /* View+backportGlassEffect.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "View+backportGlassEffect.swift"; sourceTree = ""; }; 71E8DC2C29CB601800F3E1B4 /* EquinoxAndSolsticeInfoSheet.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EquinoxAndSolsticeInfoSheet.swift; sourceTree = ""; }; + 71E9457F2F31FDA600B26D02 /* NTSolar.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NTSolar.swift; sourceTree = ""; }; + 71E9459D2F32095300B26D02 /* NTSolar++.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "NTSolar++.swift"; sourceTree = ""; }; 71F2C3A02ABB282D00E69426 /* CompactLabelStyle.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CompactLabelStyle.swift; sourceTree = ""; }; 71F641B629994DD900FE5AB5 /* LocationListRow.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LocationListRow.swift; sourceTree = ""; }; 71F641B829995ED000FE5AB5 /* DaylightChart.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DaylightChart.swift; sourceTree = ""; }; @@ -515,7 +518,6 @@ buildActionMask = 2147483647; files = ( 71C105AB2E7D313D00A76EBB /* TimeMachine in Frameworks */, - 7106C88B29A277740007A7EC /* Solar in Frameworks */, 7106C86C29A276B40007A7EC /* SwiftUI.framework in Frameworks */, 7106C86A29A276B40007A7EC /* WidgetKit.framework in Frameworks */, 71CB96DC2DE9AC4300660AA9 /* Suite in Frameworks */, @@ -541,7 +543,6 @@ buildActionMask = 2147483647; files = ( 71C105A92E7D313900A76EBB /* TimeMachine in Frameworks */, - 7194654F2C2C1E09008408C0 /* Solar in Frameworks */, 719465502C2C1E09008408C0 /* SwiftUI.framework in Frameworks */, 719465512C2C1E09008408C0 /* WidgetKit.framework in Frameworks */, 71CB96DE2DE9AC4700660AA9 /* Suite in Frameworks */, @@ -555,7 +556,6 @@ 718177752F301426007A2E9E /* YapKit in Frameworks */, 71CB96D82DE9AC3600660AA9 /* Suite in Frameworks */, 71691B892C2E816A00E4ED96 /* RealityKitContent in Frameworks */, - 7198469C28E58CAA00E866CE /* Solar in Frameworks */, 71C105962E7D2F0800A76EBB /* TimeMachine in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; @@ -564,7 +564,6 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( - 719F928329ACD24700C06921 /* Solar in Frameworks */, 71C105982E7D2F1200A76EBB /* TimeMachine in Frameworks */, 71CB96DA2DE9AC3E00660AA9 /* Suite in Frameworks */, ); @@ -783,9 +782,10 @@ 719846A728E991FA00E866CE /* LocationSearchService.swift */, 71BD5E3F29A785FE00E40C01 /* NotificationManager.swift */, 48E63C6609375907FE3E8C3C /* Secrets.swift */, + BE27E44FFAA0D1493AA14281 /* Secrets.swift.template */, + 71E9457F2F31FDA600B26D02 /* NTSolar.swift */, 71C8F4CD29A38752009A86B4 /* SolsticeCalculator.swift */, 71908ACC2AC95A5500C7B610 /* StringBuilder.swift */, - BE27E44FFAA0D1493AA14281 /* Secrets.swift.template */, ); path = Helpers; sourceTree = ""; @@ -800,7 +800,7 @@ 71F641BA2999631D00FE5AB5 /* Date++.swift */, 712433922D77A4CE00709C20 /* EdgeInsets++.swift */, 712433AA2D79090100709C20 /* NSImage+pngData.swift */, - 719846AC28E9AA0E00E866CE /* Solar++.swift */, + 71E9459D2F32095300B26D02 /* NTSolar++.swift */, 71F641BC299968BF00FE5AB5 /* TimeInterval++.swift */, 71C105A32E7D311700A76EBB /* TimeMachine++.swift */, 7195125729B48ECD009D282F /* TimeZone++.swift */, @@ -921,7 +921,6 @@ ); name = "Widget Extension"; packageProductDependencies = ( - 7106C88A29A277740007A7EC /* Solar */, 71CB96DB2DE9AC4300660AA9 /* Suite */, 71C105AA2E7D313D00A76EBB /* TimeMachine */, ); @@ -985,7 +984,6 @@ ); name = "watchOS Widget Extension"; packageProductDependencies = ( - 7194651E2C2C1E09008408C0 /* Solar */, 71CB96DD2DE9AC4700660AA9 /* Suite */, 71C105A82E7D313900A76EBB /* TimeMachine */, ); @@ -1011,7 +1009,6 @@ ); name = Solstice; packageProductDependencies = ( - 7198469B28E58CAA00E866CE /* Solar */, 71691B882C2E816A00E4ED96 /* RealityKitContent */, 71CB96D72DE9AC3600660AA9 /* Suite */, 71C105952E7D2F0800A76EBB /* TimeMachine */, @@ -1037,7 +1034,6 @@ ); name = "Solstice watchOS Watch App"; packageProductDependencies = ( - 719F928229ACD24700C06921 /* Solar */, 71CB96D92DE9AC3E00660AA9 /* Suite */, 71C105972E7D2F1200A76EBB /* TimeMachine */, ); @@ -1088,21 +1084,19 @@ ); mainGroup = 7198467828E5895E00E866CE; packageReferences = ( - 7198469A28E58CAA00E866CE /* XCRemoteSwiftPackageReference "Solar" */, - 71CB96D62DE9AC3600660AA9 /* XCRemoteSwiftPackageReference "Suite" */, 71C105942E7D2F0800A76EBB /* XCRemoteSwiftPackageReference "TimeMachine" */, 718177732F301426007A2E9E /* XCRemoteSwiftPackageReference "YapKit" */, + 71CB96D62DE9AC3600660AA9 /* XCRemoteSwiftPackageReference "Suite" */, ); - productRefGroup = 7198468228E5895E00E866CE /* Products */; projectDirPath = ""; projectRoot = ""; targets = ( - 7198468028E5895E00E866CE /* Solstice */, - 719F924229ACD1CC00C06921 /* Solstice watchOS Watch App */, 7106C86729A276B30007A7EC /* Widget Extension */, - 7194651D2C2C1E09008408C0 /* watchOS Widget Extension */, 71615FDB2F29F0610082944D /* SolsticeTests */, 71615FEA2F29F06F0082944D /* SolsticeUITests */, + 7194651D2C2C1E09008408C0 /* watchOS Widget Extension */, + 7198468028E5895E00E866CE /* Solstice */, + 719F924229ACD1CC00C06921 /* Solstice watchOS Watch App */, ); }; /* End PBXProject section */ @@ -1191,12 +1185,14 @@ 7198C40E29DB06C1009A457E /* OverviewWidget.swift in Sources */, 718177692F2EA2EC007A2E9E /* LocationAppEntity.swift in Sources */, 719465172C294B42008408C0 /* Color+Mix.swift in Sources */, + 71E9459F2F32095300B26D02 /* NTSolar++.swift in Sources */, 715489B52F201D8400FD5CCE /* SolsticeWidgetLocationManager.swift in Sources */, 719F557D29DB1A4300D7AE8E /* CountdownWidgetView+AccessoryWidgetViews.swift in Sources */, 7106C88429A2775C0007A7EC /* TimeInterval++.swift in Sources */, 7132AC1529E6960400523215 /* ConditionalGlobals+WidgetTargets.swift in Sources */, 7117008929A52ABE001BE478 /* CountdownWidgetView.swift in Sources */, 711782D92E4DFDFE0006CE5B /* DeduplicateLocationRecordsModifier.swift in Sources */, + 71E945802F31FDA600B26D02 /* NTSolar.swift in Sources */, 7198C40A29DB06A8009A457E /* CountdownWidget.swift in Sources */, 7132AC0F29E6922500523215 /* SolarChartWidgetView.swift in Sources */, 71908ACF2AC95A5500C7B610 /* StringBuilder.swift in Sources */, @@ -1213,7 +1209,6 @@ 71F2C3A12ABB282D00E69426 /* CompactLabelStyle.swift in Sources */, 712433962D77A4CE00709C20 /* EdgeInsets++.swift in Sources */, 719F558129DB1ABC00D7AE8E /* OverviewWidgetView+AccessoryWidgetViews.swift in Sources */, - 7106C88529A2775C0007A7EC /* Solar++.swift in Sources */, 71A2BDE829B747940071ACE9 /* Globals.swift in Sources */, 7132AC0C29E6917F00523215 /* SolarChartWidget.swift in Sources */, 71E3C4182EA12B030005C884 /* View+backportGlassEffect.swift in Sources */, @@ -1239,6 +1234,7 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( + 71E9459E2F32095300B26D02 /* NTSolar++.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -1246,6 +1242,7 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( + 71E945A22F32095300B26D02 /* NTSolar++.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -1271,6 +1268,7 @@ 719465302C2C1E09008408C0 /* CountdownWidget.swift in Sources */, 719465312C2C1E09008408C0 /* SolarChartWidgetView.swift in Sources */, 719465322C2C1E09008408C0 /* StringBuilder.swift in Sources */, + 71E945812F31FDA600B26D02 /* NTSolar.swift in Sources */, 719465332C2C1E09008408C0 /* WidgetMissingLocationView.swift in Sources */, 719465342C2C1E09008408C0 /* OverviewWidgetView.swift in Sources */, 719465352C2C1E09008408C0 /* WidgetBundle.swift in Sources */, @@ -1287,10 +1285,10 @@ 71E3C4152EA12ABE0005C884 /* CircleWithSlice.swift in Sources */, 712433942D77A4CE00709C20 /* EdgeInsets++.swift in Sources */, 7194653C2C2C1E09008408C0 /* OverviewWidgetView+AccessoryWidgetViews.swift in Sources */, - 7194653D2C2C1E09008408C0 /* Solar++.swift in Sources */, 7194653E2C2C1E09008408C0 /* Globals.swift in Sources */, 7194653F2C2C1E09008408C0 /* SolarChartWidget.swift in Sources */, 719465402C2C1E09008408C0 /* Date++.swift in Sources */, + 71E945A32F32095300B26D02 /* NTSolar++.swift in Sources */, 71C105A52E7D311B00A76EBB /* TimeMachine++.swift in Sources */, 719465422C2C1E09008408C0 /* View+RectangularEdgeMask.swift in Sources */, 715489B42F201D8400FD5CCE /* SolsticeWidgetLocationManager.swift in Sources */, @@ -1346,6 +1344,7 @@ 71AEB7B129AE0A9B00A7952D /* AnyLocation.swift in Sources */, 71C105912E7C08DA00A76EBB /* AlignedIconLabelStyle.swift in Sources */, 7116B1EA2DDD07F70018A294 /* View+timeMachineOverlayModifier.swift in Sources */, + 71E945A02F32095300B26D02 /* NTSolar++.swift in Sources */, 71F641C3299FD39E00FE5AB5 /* View+CapsuleAppearance.swift in Sources */, 7132AC1929E6962D00523215 /* ConditionalGlobals.swift in Sources */, 711782D82E4DFDFE0006CE5B /* DeduplicateLocationRecordsModifier.swift in Sources */, @@ -1366,7 +1365,6 @@ 719846AA28E992A200E866CE /* LocationSearchResultRow.swift in Sources */, 71A029B42C368E1900E19819 /* SavedLocation++.swift in Sources */, 718B136C29A918680001D4DC /* SupporterSettings.swift in Sources */, - 719846AD28E9AA0E00E866CE /* Solar++.swift in Sources */, 71908ACD2AC95A5500C7B610 /* StringBuilder.swift in Sources */, 712433932D77A4CE00709C20 /* EdgeInsets++.swift in Sources */, 712433A62D78867300709C20 /* StackedLabelStyle.swift in Sources */, @@ -1374,6 +1372,7 @@ 71FBEE6A2E4B345700EC1EF0 /* Bundle++.swift in Sources */, 71691B8B2C2E81DD00E4ED96 /* SolarSystemMiniMap.swift in Sources */, 71A029AD2C30501900E19819 /* EarthRealityView.swift in Sources */, + 71E945822F31FDA600B26D02 /* NTSolar.swift in Sources */, 71F641C529A0290700FE5AB5 /* AnnualDaylightChart.swift in Sources */, 7116B1F22DDD13A00018A294 /* DeviceRotationViewModifier.swift in Sources */, 7195128A29B8855F009D282F /* View+EllipticalEdgeMask.swift in Sources */, @@ -1401,7 +1400,6 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( - 719F927829ACD21A00C06921 /* Solar++.swift in Sources */, 71C105902E7C08DA00A76EBB /* AlignedIconLabelStyle.swift in Sources */, 719F928129ACD22100C06921 /* SkyGradient.swift in Sources */, 71789AA62C32DCC7002B8A33 /* View+MaterialListRowBackground.swift in Sources */, @@ -1424,6 +1422,7 @@ 719F927429ACD21300C06921 /* SolsticeApp.swift in Sources */, 712433A52D78867300709C20 /* StackedLabelStyle.swift in Sources */, 719F927029ACD21300C06921 /* DetailView.swift in Sources */, + 71E945832F31FDA600B26D02 /* NTSolar.swift in Sources */, 716260C92E6E245A00C42756 /* TimeTravelToolbar.swift in Sources */, 719F928729ACD28500C06921 /* Solstice.xcdatamodeld in Sources */, 71A029B52C368E1900E19819 /* SavedLocation++.swift in Sources */, @@ -1454,6 +1453,7 @@ 719F926E29ACD21300C06921 /* AnnualDaylightChart.swift in Sources */, 712433952D77A4CE00709C20 /* EdgeInsets++.swift in Sources */, 712433AB2D79090500709C20 /* NSImage+pngData.swift in Sources */, + 71E945A12F32095300B26D02 /* NTSolar++.swift in Sources */, 7116B1F62DDDE2130018A294 /* VariableBlurView.swift in Sources */, 713F7FFD29BE867E00BEA156 /* DailyOverview.swift in Sources */, 718177672F2EA2EC007A2E9E /* LocationAppEntity.swift in Sources */, @@ -2125,22 +2125,6 @@ minimumVersion = 1.0.0; }; }; - 7194651F2C2C1E09008408C0 /* XCRemoteSwiftPackageReference "Solar" */ = { - isa = XCRemoteSwiftPackageReference; - repositoryURL = "https://github.com/ceeK/Solar"; - requirement = { - kind = upToNextMajorVersion; - minimumVersion = 3.0.0; - }; - }; - 7198469A28E58CAA00E866CE /* XCRemoteSwiftPackageReference "Solar" */ = { - isa = XCRemoteSwiftPackageReference; - repositoryURL = "https://github.com/ceeK/Solar"; - requirement = { - kind = upToNextMajorVersion; - minimumVersion = 3.0.0; - }; - }; 71C105942E7D2F0800A76EBB /* XCRemoteSwiftPackageReference "TimeMachine" */ = { isa = XCRemoteSwiftPackageReference; repositoryURL = "https://github.com/daneden/TimeMachine"; @@ -2160,11 +2144,6 @@ /* End XCRemoteSwiftPackageReference section */ /* Begin XCSwiftPackageProductDependency section */ - 7106C88A29A277740007A7EC /* Solar */ = { - isa = XCSwiftPackageProductDependency; - package = 7198469A28E58CAA00E866CE /* XCRemoteSwiftPackageReference "Solar" */; - productName = Solar; - }; 71691B882C2E816A00E4ED96 /* RealityKitContent */ = { isa = XCSwiftPackageProductDependency; productName = RealityKitContent; @@ -2174,21 +2153,6 @@ package = 718177732F301426007A2E9E /* XCRemoteSwiftPackageReference "YapKit" */; productName = YapKit; }; - 7194651E2C2C1E09008408C0 /* Solar */ = { - isa = XCSwiftPackageProductDependency; - package = 7194651F2C2C1E09008408C0 /* XCRemoteSwiftPackageReference "Solar" */; - productName = Solar; - }; - 7198469B28E58CAA00E866CE /* Solar */ = { - isa = XCSwiftPackageProductDependency; - package = 7198469A28E58CAA00E866CE /* XCRemoteSwiftPackageReference "Solar" */; - productName = Solar; - }; - 719F928229ACD24700C06921 /* Solar */ = { - isa = XCSwiftPackageProductDependency; - package = 7198469A28E58CAA00E866CE /* XCRemoteSwiftPackageReference "Solar" */; - productName = Solar; - }; 71C105952E7D2F0800A76EBB /* TimeMachine */ = { isa = XCSwiftPackageProductDependency; package = 71C105942E7D2F0800A76EBB /* XCRemoteSwiftPackageReference "TimeMachine" */; diff --git a/Solstice.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved b/Solstice.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved index d126de83..0006a119 100644 --- a/Solstice.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved +++ b/Solstice.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved @@ -1,14 +1,5 @@ { "pins" : [ - { - "identity" : "solar", - "kind" : "remoteSourceControl", - "location" : "https://github.com/ceeK/Solar", - "state" : { - "revision" : "c2b96f2d5fb7f835b91cefac5e83101f54643901", - "version" : "3.0.1" - } - }, { "identity" : "suite", "kind" : "remoteSourceControl", diff --git a/Solstice/Charts/AnnualDaylightChart.swift b/Solstice/Charts/AnnualDaylightChart.swift index 99098646..4c8de34e 100644 --- a/Solstice/Charts/AnnualDaylightChart.swift +++ b/Solstice/Charts/AnnualDaylightChart.swift @@ -6,7 +6,6 @@ // import SwiftUI -import Solar import Charts import TimeMachine @@ -14,7 +13,7 @@ struct AnnualDaylightChart: View { @Environment(\.timeMachine) var timeMachine: TimeMachine var location: Location - var kvPairs: KeyValuePairs = [ + var kvPairs: KeyValuePairs = [ .astronomical: .indigo, .nautical: .blue, .civil: .teal, @@ -45,7 +44,7 @@ struct AnnualDaylightChart: View { } @ChartContentBuilder - private func solarBarMarks(for solar: Solar) -> some ChartContent { + private func solarBarMarks(for solar: NTSolar) -> some ChartContent { astronomicalBarMark(for: solar) nauticalBarMark(for: solar) civilBarMark(for: solar) @@ -53,7 +52,7 @@ struct AnnualDaylightChart: View { } @ChartContentBuilder - private func astronomicalBarMark(for solar: Solar) -> some ChartContent { + private func astronomicalBarMark(for solar: NTSolar) -> some ChartContent { if let astronomicalSunrise = solar.astronomicalSunrise?.withTimeZoneAdjustment(for: location.timeZone), let astronomicalSunset = solar.astronomicalSunset?.withTimeZoneAdjustment(for: location.timeZone) { let yStart: Double = max(0, solar.startOfDay.distance(to: astronomicalSunrise)) @@ -63,12 +62,12 @@ struct AnnualDaylightChart: View { yStart: .value("Astronomical Sunrise", yStart), yEnd: .value("Astronomical Sunset", yEnd) ) - .foregroundStyle(by: .value("Phase", Solar.Phase.astronomical)) + .foregroundStyle(by: .value("Phase", NTSolar.Phase.astronomical)) } } @ChartContentBuilder - private func nauticalBarMark(for solar: Solar) -> some ChartContent { + private func nauticalBarMark(for solar: NTSolar) -> some ChartContent { if let nauticalSunrise = solar.nauticalSunrise?.withTimeZoneAdjustment(for: location.timeZone), let nauticalSunset = solar.nauticalSunset?.withTimeZoneAdjustment(for: location.timeZone) { let yStart: Double = max(0, solar.startOfDay.distance(to: nauticalSunrise)) @@ -78,12 +77,12 @@ struct AnnualDaylightChart: View { yStart: .value("Nautical Sunrise", yStart), yEnd: .value("Nautical Sunset", yEnd) ) - .foregroundStyle(by: .value("Phase", Solar.Phase.nautical)) + .foregroundStyle(by: .value("Phase", NTSolar.Phase.nautical)) } } @ChartContentBuilder - private func civilBarMark(for solar: Solar) -> some ChartContent { + private func civilBarMark(for solar: NTSolar) -> some ChartContent { if let civilSunrise = solar.civilSunrise?.withTimeZoneAdjustment(for: location.timeZone), let civilSunset = solar.civilSunset?.withTimeZoneAdjustment(for: location.timeZone) { let yStart: Double = max(0, solar.startOfDay.distance(to: civilSunrise)) @@ -93,11 +92,11 @@ struct AnnualDaylightChart: View { yStart: .value("Civil Sunrise", yStart), yEnd: .value("Civil Sunset", yEnd) ) - .foregroundStyle(by: .value("Phase", Solar.Phase.civil)) + .foregroundStyle(by: .value("Phase", NTSolar.Phase.civil)) } } - private func daylightBarMark(for solar: Solar) -> some ChartContent { + private func daylightBarMark(for solar: NTSolar) -> some ChartContent { let sunrise: Date = solar.safeSunrise.withTimeZoneAdjustment(for: location.timeZone) let sunset: Date = solar.safeSunset.withTimeZoneAdjustment(for: location.timeZone) let yStart: Double = max(0, solar.startOfDay.distance(to: sunrise)) @@ -107,7 +106,7 @@ struct AnnualDaylightChart: View { yStart: .value("Sunrise", yStart), yEnd: .value("Sunset", yEnd) ) - .foregroundStyle(by: .value("Phase", Solar.Phase.day)) + .foregroundStyle(by: .value("Phase", NTSolar.Phase.day)) } private var yAxisMarks: some AxisContent { @@ -138,7 +137,7 @@ struct AnnualDaylightChart: View { } extension AnnualDaylightChart { - var monthlySolars: Array { + var monthlySolars: Array { guard let year = calendar.dateInterval(of: .year, for: timeMachine.date) else { return [] } @@ -153,7 +152,7 @@ extension AnnualDaylightChart { } return dates.map { date in - return Solar(for: date, coordinate: location.coordinate) + return NTSolar(for: date, coordinate: location.coordinate, timeZone: location.timeZone) }.compactMap { $0 } } } diff --git a/Solstice/Charts/CircularSolarChart.swift b/Solstice/Charts/CircularSolarChart.swift index 2299e3f5..3498708d 100644 --- a/Solstice/Charts/CircularSolarChart.swift +++ b/Solstice/Charts/CircularSolarChart.swift @@ -6,7 +6,6 @@ // import SwiftUI -import Solar import TimeMachine import Suite import enum Accelerate.vDSP @@ -26,8 +25,8 @@ struct CircularSolarChart: View { var timeZone: TimeZone { location.timeZone } - var solar: Solar? { - Solar(for: date ?? timeMachine.date, coordinate: location.coordinate) + var solar: NTSolar? { + NTSolar(for: date ?? timeMachine.date, coordinate: location.coordinate, timeZone: location.timeZone) } var majorSunSize: Double { @@ -176,7 +175,7 @@ struct CircularSolarChart: View { private var phaseSlices: some View { Group { if let phases = solar?.phases { - let phaseKeys: [Solar.Phase] = Array(phases.keys) + let phaseKeys: [NTSolar.Phase] = Array(phases.keys) ForEach(phaseKeys, id: \.self) { key in phaseSlice(for: key, phases: phases) } @@ -186,7 +185,7 @@ struct CircularSolarChart: View { } @ViewBuilder - private func phaseSlice(for key: Solar.Phase, phases: [Solar.Phase: (sunrise: Date?, sunset: Date?)]) -> some View { + private func phaseSlice(for key: NTSolar.Phase, phases: [NTSolar.Phase: (sunrise: Date?, sunset: Date?)]) -> some View { if let (sunrise, sunset) = phases[key], let sunrise, let sunset { @@ -258,7 +257,7 @@ struct CircularSolarChart: View { @ViewBuilder private var phaseMarkers: some View { if let phases = solar?.phases { - let phaseKeys: [Solar.Phase] = Array(phases.keys) + let phaseKeys: [NTSolar.Phase] = Array(phases.keys) ForEach(phaseKeys, id: \.self) { key in phaseMarker(for: key, phases: phases) } @@ -266,7 +265,7 @@ struct CircularSolarChart: View { } @ViewBuilder - private func phaseMarker(for key: Solar.Phase, phases: [Solar.Phase: (sunrise: Date?, sunset: Date?)]) -> some View { + private func phaseMarker(for key: NTSolar.Phase, phases: [NTSolar.Phase: (sunrise: Date?, sunset: Date?)]) -> some View { if let (sunrise, sunset) = phases[key], let sunrise, let sunset { diff --git a/Solstice/Charts/DaylightChart.swift b/Solstice/Charts/DaylightChart.swift index d2308fe4..8c8c2ca3 100644 --- a/Solstice/Charts/DaylightChart.swift +++ b/Solstice/Charts/DaylightChart.swift @@ -7,17 +7,16 @@ import SwiftUI import Charts -import Solar import Suite struct DaylightChart: View { @Environment(\.isLuminanceReduced) var isLuminanceReduced @Environment(\.colorScheme) var colorScheme - @State private var selectedEvent: Solar.Event? + @State private var selectedEvent: NTSolar.Event? @State private var currentX: Date? - var solar: Solar + var solar: NTSolar var timeZone: TimeZone var showEventTypes = true @@ -109,11 +108,11 @@ struct DaylightChart: View { } } - private var filteredEvents: [Solar.Event] { + private var filteredEvents: [NTSolar.Event] { solar.events.filter { range.contains($0.date) } } - private func eventPointMark(for solarEvent: Solar.Event) -> some ChartContent { + private func eventPointMark(for solarEvent: NTSolar.Event) -> some ChartContent { PointMark( x: .value("Event Time", solarEvent.date), y: .value("Event", yValue(for: solarEvent.date)) @@ -123,9 +122,9 @@ struct DaylightChart: View { .symbolSize(markSize * .pi * 2) } - private func eventPointOpacity(for phase: Solar.Phase) -> Double { - let hiddenPhases: Set = [.night, .day, .sunrise, .sunset] - let shouldShow: Bool = showEventTypes || !Solar.Phase.plottablePhases.contains(phase) + private func eventPointOpacity(for phase: NTSolar.Phase) -> Double { + let hiddenPhases: Set = [.night, .day, .sunrise, .sunset] + let shouldShow: Bool = showEventTypes || !NTSolar.Phase.plottablePhases.contains(phase) return (shouldShow && !hiddenPhases.contains(phase)) ? 1 : 0 } @@ -307,7 +306,7 @@ extension DaylightChart { solar.daylightDuration / dayLength } - func pointMarkColor(for eventPhase: Solar.Phase) -> HierarchicalShapeStyle { + func pointMarkColor(for eventPhase: NTSolar.Phase) -> HierarchicalShapeStyle { switch eventPhase { case .astronomical: return .quaternary @@ -383,13 +382,13 @@ extension DaylightChart { Form { Group { DaylightChart( - solar: Solar(coordinate: TemporaryLocation.placeholderLondon.coordinate)!, + solar: NTSolar(for: .now, coordinate: TemporaryLocation.placeholderLondon.coordinate, timeZone: TemporaryLocation.placeholderLondon.timeZone)!, timeZone: TimeZone.autoupdatingCurrent, scrubbable: true ) DaylightChart( - solar: Solar(coordinate: TemporaryLocation.placeholderLondon.coordinate)!, + solar: NTSolar(for: .now, coordinate: TemporaryLocation.placeholderLondon.coordinate, timeZone: TemporaryLocation.placeholderLondon.timeZone)!, timeZone: TimeZone.autoupdatingCurrent, appearance: .graphical, scrubbable: true diff --git a/Solstice/ContentView.swift b/Solstice/ContentView.swift index 60b6a19b..adc9a8d5 100644 --- a/Solstice/ContentView.swift +++ b/Solstice/ContentView.swift @@ -7,7 +7,6 @@ import SwiftUI import CoreData -import Solar import Suite import TimeMachine @@ -187,4 +186,3 @@ struct ContentView: View { .environment(CurrentLocation()) .environment(LocationSearchService()) } - diff --git a/Solstice/Detail View/AnnualOverview.swift b/Solstice/Detail View/AnnualOverview.swift index 5a354278..84756309 100644 --- a/Solstice/Detail View/AnnualOverview.swift +++ b/Solstice/Detail View/AnnualOverview.swift @@ -6,7 +6,6 @@ // import SwiftUI -import Solar import Suite import TimeMachine @@ -173,19 +172,19 @@ struct AnnualOverview: View { } extension AnnualOverview { - var decemberSolsticeSolar: Solar? { +var decemberSolsticeSolar: NTSolar? { let year = calendar.component(.year, from: timeMachine.date) let decemberSolstice = SolsticeCalculator.decemberSolstice(year: year) - return Solar(for: decemberSolstice, coordinate: location.coordinate) + return NTSolar(for: decemberSolstice, coordinate: location.coordinate, timeZone: location.timeZone) } - var juneSolsticeSolar: Solar? { + var juneSolsticeSolar: NTSolar? { let year = calendar.component(.year, from: timeMachine.date) let juneSolstice = SolsticeCalculator.juneSolstice(year: year) - return Solar(for: juneSolstice, coordinate: location.coordinate) + return NTSolar(for: juneSolstice, coordinate: location.coordinate, timeZone: location.timeZone) } - var longestDay: Solar? { + var longestDay: NTSolar? { guard let decemberSolsticeSolar, let juneSolsticeSolar else { return nil @@ -194,7 +193,7 @@ extension AnnualOverview { return decemberSolsticeSolar.daylightDuration > juneSolsticeSolar.daylightDuration ? decemberSolsticeSolar : juneSolsticeSolar } - var shortestDay: Solar? { + var shortestDay: NTSolar? { guard let decemberSolsticeSolar, let juneSolsticeSolar else { return nil diff --git a/Solstice/Detail View/DailyOverview.swift b/Solstice/Detail View/DailyOverview.swift index 29b1e241..7d68a1f6 100644 --- a/Solstice/Detail View/DailyOverview.swift +++ b/Solstice/Detail View/DailyOverview.swift @@ -6,17 +6,16 @@ // import SwiftUI -import Solar import Suite import TimeMachine struct DailyOverview: View { @Environment(\.timeMachine) private var timeMachine - var solar: Solar + var solar: NTSolar var location: Location - @State private var gradientSolar: Solar? + @State private var gradientSolar: NTSolar? @AppStorage(Preferences.detailViewChartAppearance) private var chartAppearance @AppStorage(Preferences.chartType) private var chartType @@ -28,8 +27,8 @@ struct DailyOverview: View { } var differenceFromPreviousSolstice: TimeInterval? { - guard let solar = Solar(for: timeMachine.date, coordinate: location.coordinate), - let previousSolsticeSolar = Solar(for: solar.date.previousSolstice, coordinate: location.coordinate) else { + guard let solar = NTSolar(for: timeMachine.date, coordinate: location.coordinate, timeZone: location.timeZone), + let previousSolsticeSolar = NTSolar(for: solar.date.previousSolstice, coordinate: location.coordinate, timeZone: location.timeZone) else { return nil } @@ -208,12 +207,12 @@ extension DailyOverview { .if(chartAppearance == .graphical) { content in content .background { - SkyGradient(solar: gradientSolar ?? solar) + SkyGradient(ntSolar: gradientSolar ?? solar) } } .onPreferenceChange(DaylightGradientTimePreferenceKey.self) { date in - self.gradientSolar = Solar(for: date, coordinate: solar.coordinate) - } + self.gradientSolar = NTSolar(for: date, coordinate: solar.coordinate, timeZone: location.timeZone) + } #endif #if os(macOS) .padding(-12) @@ -223,7 +222,7 @@ extension DailyOverview { #Preview { Form { - DailyOverview(solar: Solar(coordinate: TemporaryLocation.placeholderLondon.coordinate)!, location: TemporaryLocation.placeholderLondon) + DailyOverview(solar: NTSolar(for: .now, coordinate: TemporaryLocation.placeholderLondon.coordinate, timeZone: TemporaryLocation.placeholderLondon.timeZone)!, location: TemporaryLocation.placeholderLondon) } .withTimeMachine(.solsticeTimeMachine) } diff --git a/Solstice/Detail View/DaylightSummaryTitle.swift b/Solstice/Detail View/DaylightSummaryTitle.swift index b9ddafe0..5841e866 100644 --- a/Solstice/Detail View/DaylightSummaryTitle.swift +++ b/Solstice/Detail View/DaylightSummaryTitle.swift @@ -6,11 +6,10 @@ // import SwiftUI -import Solar struct DaylightSummaryTitle: View { - var solar: Solar - var event: Solar.Event? + var solar: NTSolar + var event: NTSolar.Event? var date: Date? var timeZone = localTimeZone @@ -94,6 +93,6 @@ struct DaylightSummaryTitle: View { struct DaylightSummaryTitle_Previews: PreviewProvider { static var previews: some View { - DaylightSummaryTitle(solar: Solar(coordinate: TemporaryLocation.placeholderLondon.coordinate)!) + DaylightSummaryTitle(solar: NTSolar(for: .now, coordinate: TemporaryLocation.placeholderLondon.coordinate, timeZone: TemporaryLocation.placeholderLondon.timeZone)!) } } diff --git a/Solstice/Detail View/DetailView.swift b/Solstice/Detail View/DetailView.swift index 059fb1ad..26a0c023 100644 --- a/Solstice/Detail View/DetailView.swift +++ b/Solstice/Detail View/DetailView.swift @@ -6,7 +6,6 @@ // import SwiftUI -import Solar import Suite import TimeMachine @@ -29,8 +28,8 @@ struct DetailView: View { @AppStorage(Preferences.detailViewChartAppearance) private var chartAppearance @SceneStorage("selectedLocation") private var selectedLocation: String? - var solar: Solar? { - Solar(for: timeMachine.date, coordinate: location.coordinate) + var solar: NTSolar? { + NTSolar(for: timeMachine.date, coordinate: location.coordinate, timeZone: location.timeZone) } var navBarTitleText: Text { @@ -73,7 +72,7 @@ struct DetailView: View { .modify { if let solar { $0.containerBackground( - SkyGradient(solar: solar), + SkyGradient(ntSolar: solar), for: .navigation ) } else { diff --git a/Solstice/Detail View/SolarExtremetiesOverview.swift b/Solstice/Detail View/SolarExtremetiesOverview.swift index f87c953d..51b652e5 100644 --- a/Solstice/Detail View/SolarExtremetiesOverview.swift +++ b/Solstice/Detail View/SolarExtremetiesOverview.swift @@ -6,7 +6,6 @@ // import SwiftUI -import Solar import TimeMachine struct SolarExtremetiesOverview: View { @@ -48,7 +47,7 @@ fileprivate struct SolarExtremityView: View { } } - var solar: Solar +var solar: NTSolar var extremity: Extremity var body: some View { @@ -157,19 +156,19 @@ extension CompatibleDisclosureGroup { } extension SolarExtremetiesOverview { - var decemberSolsticeSolar: Solar? { +var decemberSolsticeSolar: NTSolar? { let year = calendar.component(.year, from: timeMachine.date) let decemberSolstice = SolsticeCalculator.decemberSolstice(year: year) - return Solar(for: decemberSolstice, coordinate: location.coordinate) + return NTSolar(for: decemberSolstice, coordinate: location.coordinate, timeZone: location.timeZone) } - var juneSolsticeSolar: Solar? { + var juneSolsticeSolar: NTSolar? { let year = calendar.component(.year, from: timeMachine.date) let juneSolstice = SolsticeCalculator.juneSolstice(year: year) - return Solar(for: juneSolstice, coordinate: location.coordinate) + return NTSolar(for: juneSolstice, coordinate: location.coordinate, timeZone: location.timeZone) } - var longestDay: Solar? { + var longestDay: NTSolar? { guard let decemberSolsticeSolar, let juneSolsticeSolar else { return nil @@ -178,7 +177,7 @@ extension SolarExtremetiesOverview { return decemberSolsticeSolar.daylightDuration > juneSolsticeSolar.daylightDuration ? decemberSolsticeSolar : juneSolsticeSolar } - var shortestDay: Solar? { + var shortestDay: NTSolar? { guard let decemberSolsticeSolar, let juneSolsticeSolar else { return nil diff --git a/Solstice/Extensions/AppStorage++.swift b/Solstice/Extensions/AppStorage++.swift index f6f6feb8..1eff036b 100644 --- a/Solstice/Extensions/AppStorage++.swift +++ b/Solstice/Extensions/AppStorage++.swift @@ -6,7 +6,6 @@ // import SwiftUI -import Solar fileprivate let defaultNotificationDate = calendar.date(bySettingHour: 8, minute: 0, second: 0, of: Date()) ?? .now @@ -95,7 +94,7 @@ struct Preferences { static let defaultDateComponents = DateComponents(timeZone: .autoupdatingCurrent, hour: 8, minute: 0) /// Which solar event notifications are sent relative to - static let relation: Value = ("notificationRelation", .sunrise) + static let relation: Value = ("notificationRelation", .sunrise) /// The offset in seconds between the notification and the chosen solar event static let relativeOffset: Value = ("notificationRelativeOffset", 30 * 60) diff --git a/Solstice/Extensions/Solar++.swift b/Solstice/Extensions/NTSolar++.swift similarity index 86% rename from Solstice/Extensions/Solar++.swift rename to Solstice/Extensions/NTSolar++.swift index b1c2ab21..d7d0d39f 100644 --- a/Solstice/Extensions/Solar++.swift +++ b/Solstice/Extensions/NTSolar++.swift @@ -1,16 +1,17 @@ // -// Solar++.swift +// NTSolar++.swift // Solstice // -// Created by Daniel Eden on 02/10/2022. +// Created by Daniel Eden on 03/02/2026. // +import Foundation import SwiftUI -import Solar import Charts +import CoreLocation // MARK: - Solar Event Types -extension Solar { +extension NTSolar { struct Event: Hashable, Identifiable { var id: Int { hashValue } let label: String @@ -40,7 +41,7 @@ extension Solar { } } - enum Phase: String, Plottable, CaseIterable, Codable { + enum Phase: String, Plottable, CaseIterable { case night = "Night", astronomical = "Astronomical Twilight", nautical = "Nautical Twilight", @@ -63,7 +64,7 @@ extension Solar { } // MARK: - Day Boundaries -extension Solar { +extension NTSolar { var startOfDay: Date { calendar.startOfDay(for: date) } @@ -82,7 +83,7 @@ extension Solar { } // MARK: - Safe Sunrise/Sunset -extension Solar { +extension NTSolar { var fallbackSunrise: Date? { sunrise ?? civilSunrise ?? nauticalSunrise ?? astronomicalSunrise } @@ -128,7 +129,7 @@ extension Solar { return safeSunrise.distance(to: safeSunset) } - let month = Calendar.current.component(.month, from: date) + let month = calendar.component(.month, from: date) switch month { case 1...3, 10...12: @@ -137,33 +138,28 @@ extension Solar { return coordinate.insideArcticCircle ? TimeInterval.twentyFourHours : 0 } } - - var solarNoon: Date? { - guard let fallbackSunrise, let fallbackSunset else { return nil } - return fallbackSunrise.addingTimeInterval(fallbackSunrise.distance(to: fallbackSunset) / 2) - } } // MARK: - Related Days -extension Solar { - var yesterday: Solar? { +extension NTSolar { + var yesterday: NTSolar? { let yesterdayDate = calendar.date(byAdding: .day, value: -1, to: date) ?? date.addingTimeInterval(60 * 60 * 24 - 1) - return Solar(for: yesterdayDate, coordinate: coordinate) + return NTSolar(for: yesterdayDate, coordinate: coordinate, timeZone: timeZone, calendar: calendar) } - var tomorrow: Solar? { + var tomorrow: NTSolar? { guard let tomorrow = calendar.date(byAdding: .day, value: 1, to: date) else { return nil } - return Solar(for: tomorrow, coordinate: coordinate) + return NTSolar(for: tomorrow, coordinate: coordinate, timeZone: timeZone, calendar: calendar) } } // MARK: - Difference Strings -extension Solar { +extension NTSolar { var compactDifferenceString: LocalizedStringKey { - let comparator = date.isToday ? yesterday : Solar(coordinate: self.coordinate) + let comparator = date.isToday ? yesterday : NTSolar(for: Date(), coordinate: self.coordinate, timeZone: timeZone, calendar: calendar) let difference = daylightDuration - (comparator?.daylightDuration ?? 0) let differenceString = Duration.seconds(abs(difference)).formatted(.units(maximumUnitCount: 2)) @@ -178,7 +174,7 @@ extension Solar { formatter.dateStyle = .medium formatter.formattingContext = .middleOfSentence - let comparator = date.isToday ? yesterday : Solar(coordinate: self.coordinate) + let comparator = date.isToday ? yesterday : NTSolar(for: Date(), coordinate: self.coordinate, timeZone: timeZone, calendar: calendar) let difference = daylightDuration - (comparator?.daylightDuration ?? 0) let differenceString = Duration.seconds(abs(difference)).formatted(.units(maximumUnitCount: 2)) @@ -200,7 +196,7 @@ extension Solar { } // MARK: - Events -extension Solar { +extension NTSolar { var events: Array { [ Event(label: "Astronomical Sunrise", date: astronomicalSunrise, phase: .astronomical), @@ -235,8 +231,15 @@ extension Solar { if let todayEvent { return todayEvent } - let fallbackSolar: Solar = yesterday ?? self + let fallbackSolar: NTSolar = yesterday ?? self let fallbackEvents: [Event] = fallbackSolar.events.filter { $0.phase == .sunset || $0.phase == .sunrise } return fallbackEvents.last(where: { $0.date < date }) } } + +// MARK: - View Helpers +extension NTSolar { + var view: some View { + SkyGradient(ntSolar: self) + } +} diff --git a/Solstice/Helper Views/LandingView.swift b/Solstice/Helper Views/LandingView.swift index f0aace94..60ec21b8 100644 --- a/Solstice/Helper Views/LandingView.swift +++ b/Solstice/Helper Views/LandingView.swift @@ -6,7 +6,6 @@ // import SwiftUI -import Solar import Suite fileprivate struct SizePreferenceKey: PreferenceKey { @@ -83,18 +82,19 @@ struct LandingView: View { dynamicTypeSize > .accessibility2 } - @State private var solar = Solar(coordinate: .proxiedToTimeZone) + @State private var solar = NTSolar(for: .now, coordinate: .proxiedToTimeZone, timeZone: .autoupdatingCurrent) private let renderTime = Date.now var body: some View { ZStack { TimelineView(.animation) { context in - SkyGradient(solar: solar) + SkyGradient(ntSolar: solar) .ignoresSafeArea() .task(id: context.date) { - solar = Solar( + solar = NTSolar( for: renderTime.addingTimeInterval(context.date.distance(to: renderTime) * 1000), - coordinate: .proxiedToTimeZone + coordinate: .proxiedToTimeZone, + timeZone: .autoupdatingCurrent ) ?? solar } } diff --git a/Solstice/Helper Views/ShareSolarChartView.swift b/Solstice/Helper Views/ShareSolarChartView.swift index 1c41f757..0e90c7b2 100644 --- a/Solstice/Helper Views/ShareSolarChartView.swift +++ b/Solstice/Helper Views/ShareSolarChartView.swift @@ -6,7 +6,6 @@ // import SwiftUI -import Solar import Suite import TimeMachine @@ -14,7 +13,7 @@ struct ShareSolarChartView: View { @Environment(\.dismiss) var dismiss @Environment(\.timeMachine) var timeMachine: TimeMachine - var solar: Solar + var solar: NTSolar var location: Location @State var chartAppearance: DaylightChart.Appearance = .graphical @@ -38,7 +37,7 @@ struct ShareSolarChartView: View { .if(chartAppearance == .graphical) { content in content .background { - SkyGradient(solar: solar) + SkyGradient(ntSolar: solar) } } } @@ -153,7 +152,7 @@ struct ShareSolarChartView: View { #if os(iOS) @ViewBuilder private var instagramShareButton: some View { - let solarGradient = SkyGradient(solar: solar) + let solarGradient = SkyGradient(ntSolar: solar) if let igStoriesUrl, let imageData, let topColor = solarGradient.stops.first?.toHex(), @@ -281,6 +280,6 @@ struct ShareSolarChartView: View { } #Preview { - ShareSolarChartView(solar: .init(coordinate: TemporaryLocation.placeholderLondon.coordinate)!, location: TemporaryLocation.placeholderLondon) + ShareSolarChartView(solar: .init(for: .now, coordinate: TemporaryLocation.placeholderLondon.coordinate, timeZone: TemporaryLocation.placeholderLondon.timeZone)!, location: TemporaryLocation.placeholderLondon) .withTimeMachine(.solsticeTimeMachine) } diff --git a/Solstice/Helper Views/SkyGradient.swift b/Solstice/Helper Views/SkyGradient.swift index 9f96f673..fe5c066d 100644 --- a/Solstice/Helper Views/SkyGradient.swift +++ b/Solstice/Helper Views/SkyGradient.swift @@ -7,13 +7,27 @@ import Foundation import SwiftUI -import Solar import CoreLocation -extension Solar: @unchecked @retroactive Sendable {} +/* + Date construction snippet for reference: + + let date = DateComponents(calendar: Calendar(identifier: .gregorian), + timeZone: TimeZone(identifier: "America/New_York"), + year: 2021, month: 12, day: 25, hour: 10, minute: 0).date +*/ struct SkyGradient: View, ShapeStyle { - var solar: Solar? = Solar(coordinate: .proxiedToTimeZone) + @Environment(\.timeZone) var timeZone + + var ntSolar: NTSolar? = nil + + private var effectiveSolar: NTSolar? { + if let ntSolar { + return ntSolar + } + return NTSolar(for: .now, coordinate: .proxiedToTimeZone, timeZone: timeZone) + } static let dawn = [ Color(red: 0.388, green: 0.435, blue: 0.643), @@ -51,7 +65,7 @@ struct SkyGradient: View, ShapeStyle { } var colors: [[Color]] { - let duration = solar?.daylightDuration ?? 43200 + let duration = effectiveSolar?.daylightDuration ?? 43200 let daylightHours = Int((duration / (60 * 60)) / 2) let amColors = [Self.night, Self.dawn, Self.morning] let pmColors = [Self.afternoon, Self.evening, Self.night] @@ -68,9 +82,10 @@ struct SkyGradient: View, ShapeStyle { } var stops: [Color] { - let sunrise: Date = solar?.safeSunrise ?? .now.startOfDay - let sunset: Date = solar?.safeSunset ?? .now.endOfDay - let currentDate: Date = solar?.date ?? .now + // All Date values are interpreted as absolute instants in time and should be constructed for the local solar time of the location. + let sunrise: Date = effectiveSolar?.safeSunrise ?? .now.startOfDay + let sunset: Date = effectiveSolar?.safeSunset ?? .now.endOfDay + let currentDate: Date = effectiveSolar?.date ?? .now let twilightDuration: TimeInterval = 60 * 180 let dayStart: Date = sunrise.addingTimeInterval(-twilightDuration) @@ -100,21 +115,16 @@ struct SkyGradient: View, ShapeStyle { } } -extension Solar { - var view: some View { - SkyGradient(solar: self) - } -} - fileprivate struct PreviewContainer: View { + @Environment(\.timeZone) var timeZone @State var date = Date.now - var solars: [Solar] { - var result = [Solar?]() + var solars: [NTSolar] { + var result = [NTSolar?]() for i in stride(from: 0, to: 180, by: 15) { let location = CLLocationCoordinate2D(latitude: Double(i) - 90, longitude: 0) - result.append(Solar(for: date, coordinate: location)) + result.append(NTSolar(for: date, coordinate: location, timeZone: timeZone)) } return result.compactMap { $0 } @@ -125,7 +135,7 @@ fileprivate struct PreviewContainer: View { VStack(spacing: 0) { ForEach(solars, id: \.coordinate.latitude) { solar in ZStack { - SkyGradient(solar: solar) + SkyGradient(ntSolar: solar) HStack { Text(solar.date, style: .time) diff --git a/Solstice/Helpers/NTSolar.swift b/Solstice/Helpers/NTSolar.swift new file mode 100644 index 00000000..027649e0 --- /dev/null +++ b/Solstice/Helpers/NTSolar.swift @@ -0,0 +1,708 @@ +// +// NTSolar.swift +// Solstice +// +// Created by Daniel Eden on 03/02/2026. +// + +// +// +// NTSolar.swift +// +// Created by Neil Tiffin on 5/8/19. +// Copyright © 2019 Performance Champions, Inc. +// Copyright © 2019 Neil Tiffin. +// +// Released to the public domain by Neil Tiffin, May 2019 +// Released to the public domain by Performance Champions, Inc., May 2019 +// + +import Foundation +import CoreLocation + +/// Struct to calculate sunrise, sunset, twilights, and solar noon. +/// +/// C code originally from: http://stjarnhimlen.se/comp/sunriset.c +struct NTSolar { + + // MARK: - Public Swift Interface + + let date: Date + let coordinate: CLLocationCoordinate2D + let timeZone: TimeZone + let calendar: Calendar + + let sunrise: Date? + let sunset: Date? + let civilSunrise: Date? + let civilSunset: Date? + let nauticalSunrise: Date? + let nauticalSunset: Date? + let astronomicalSunrise: Date? + let astronomicalSunset: Date? + let solarNoon: Date? + + /// Initialize solar calculations for a given date, coordinate, and time zone. + /// + /// - Parameters: + /// - date: The date for the calculation. The local calendar day in `timeZone` is used. + /// - coordinate: The latitude and longitude for the calculation. + /// - timeZone: The time zone for the resulting date and times. + /// - calendar: The calendar to use for date calculations. + init?(for date: Date, + coordinate: CLLocationCoordinate2D, + timeZone: TimeZone = .autoupdatingCurrent, + calendar: Calendar = Calendar(identifier: .gregorian)) { + var calendar = calendar + calendar.timeZone = timeZone + var components = calendar.dateComponents([.day, .year, .month], from: date) + components.calendar = calendar + + guard let year = components.year, + let month = components.month, + let day = components.day else { + return nil + } + + self.date = date + self.coordinate = coordinate + self.timeZone = timeZone + self.calendar = calendar + + let sunriseSet = NTSolar.sun_rise_set(year: year, + month: month, + day: day, + lon: coordinate.longitude, + lat: coordinate.latitude) + + let civil = NTSolar.civil_twilight(year: year, + month: month, + day: day, + lon: coordinate.longitude, + lat: coordinate.latitude) + + let nautical = NTSolar.nautical_twilight(year: year, + month: month, + day: day, + lon: coordinate.longitude, + lat: coordinate.latitude) + + let astronomical = NTSolar.astronomical_twilight(year: year, + month: month, + day: day, + lon: coordinate.longitude, + lat: coordinate.latitude) + + let offsetSeconds = NTSolar.gmtOffsetSeconds(forYear: year, month: month, day: day, timeZone: timeZone, calendar: calendar) + + self.sunrise = NTSolar.dateFromUTCHours(sunriseSet.trise, + year: year, + month: month, + day: day, + offsetSeconds: offsetSeconds, + calendar: calendar, + shouldReturn: sunriseSet.code == .RiseAndSet) + + self.sunset = NTSolar.dateFromUTCHours(sunriseSet.tset, + year: year, + month: month, + day: day, + offsetSeconds: offsetSeconds, + calendar: calendar, + shouldReturn: sunriseSet.code == .RiseAndSet) + + self.civilSunrise = NTSolar.dateFromUTCHours(civil.trise, + year: year, + month: month, + day: day, + offsetSeconds: offsetSeconds, + calendar: calendar, + shouldReturn: civil.code == .RiseAndSet) + + self.civilSunset = NTSolar.dateFromUTCHours(civil.tset, + year: year, + month: month, + day: day, + offsetSeconds: offsetSeconds, + calendar: calendar, + shouldReturn: civil.code == .RiseAndSet) + + self.nauticalSunrise = NTSolar.dateFromUTCHours(nautical.trise, + year: year, + month: month, + day: day, + offsetSeconds: offsetSeconds, + calendar: calendar, + shouldReturn: nautical.code == .RiseAndSet) + + self.nauticalSunset = NTSolar.dateFromUTCHours(nautical.tset, + year: year, + month: month, + day: day, + offsetSeconds: offsetSeconds, + calendar: calendar, + shouldReturn: nautical.code == .RiseAndSet) + + self.astronomicalSunrise = NTSolar.dateFromUTCHours(astronomical.trise, + year: year, + month: month, + day: day, + offsetSeconds: offsetSeconds, + calendar: calendar, + shouldReturn: astronomical.code == .RiseAndSet) + + self.astronomicalSunset = NTSolar.dateFromUTCHours(astronomical.tset, + year: year, + month: month, + day: day, + offsetSeconds: offsetSeconds, + calendar: calendar, + shouldReturn: astronomical.code == .RiseAndSet) + + let solarNoonUTC = (sunriseSet.trise + sunriseSet.tset) / 2 + self.solarNoon = NTSolar.dateFromUTCHours(solarNoonUTC, + year: year, + month: month, + day: day, + offsetSeconds: offsetSeconds, + calendar: calendar, + shouldReturn: true) + } + + /// Calculate the sun rise and set times for a given date/time zone. + /// + /// - Parameters: + /// - forDate: The date for the calculation. You should ensure that the date, which is stored as UTC, is the date + /// really wanted in the given time zone. It will be converted to the given time zone before being used. + /// - atLocation: The latitude and longitude for the calculation. + /// - inTimeZone: The time zone for the resulting date and times. + /// - Returns: If the sun both rises and sets on the day requested and at the + /// latitude requested then return sun rise and set times rounded down to the minute, nil otherwise. + static func sunRiseAndSet(forDate: Date, + atLocation: CLLocationCoordinate2D, + inTimeZone: TimeZone) -> (sunrise: Date, sunset: Date)? { + guard let solar = NTSolar(for: forDate, coordinate: atLocation, timeZone: inTimeZone) else { + return nil + } + + guard let sunrise = solar.sunrise, + let sunset = solar.sunset else { + return nil + } + + return (sunrise, sunset) + } + + // MARK: - Date Helpers + + private static func gmtOffsetSeconds(forYear year: Int, + month: Int, + day: Int, + timeZone: TimeZone, + calendar: Calendar) -> Int { + var components = DateComponents() + components.calendar = calendar + components.year = year + components.month = month + components.day = day + components.hour = 12 + components.minute = 0 + components.second = 0 + + let localNoon = calendar.date(from: components) ?? Date() + return timeZone.secondsFromGMT(for: localNoon) + } + + private static func dateFromUTCHours(_ utcHours: Double, + year: Int, + month: Int, + day: Int, + offsetSeconds: Int, + calendar: Calendar, + shouldReturn: Bool) -> Date? { + guard shouldReturn else { return nil } + + var localHours = utcHours + (Double(offsetSeconds) / 3600.0) + var dayOffset = 0 + while localHours < 0 { + localHours += 24.0 + dayOffset -= 1 + } + while localHours >= 24.0 { + localHours -= 24.0 + dayOffset += 1 + } + + var hour = Int(localHours) + var minute = Int(((localHours - Double(hour)) * 60.0).rounded()) + + if minute == 60 { + minute = 0 + hour += 1 + if hour == 24 { + hour = 0 + dayOffset += 1 + } + } + + var baseComponents = DateComponents() + baseComponents.calendar = calendar + baseComponents.year = year + baseComponents.month = month + baseComponents.day = day + baseComponents.hour = 0 + baseComponents.minute = 0 + baseComponents.second = 0 + + guard let baseDate = calendar.date(from: baseComponents) else { + return nil + } + + guard let adjustedDay = calendar.date(byAdding: .day, value: dayOffset, to: baseDate) else { + return nil + } + + var timeComponents = DateComponents() + timeComponents.hour = hour + timeComponents.minute = minute + timeComponents.second = 0 + + guard let finalDate = calendar.date(byAdding: timeComponents, to: adjustedDay) else { + return nil + } + + return finalDate + } + + // MARK: - SUNRISET.C + + // C code originally from: http://stjarnhimlen.se/comp/sunriset.c + // + // The conversion process removed pointers in favor of return tuples, converted macros to function calls, + // added return code enum, and converted + // comments to work with Xcode. Of course the C code had to be converted to Swift, but that was minimal. + // + // As much as possible the original code was left intact in order to not introduce bugs. + // In other words, some code was left unfashionable by today's standards. + + enum ReturnCode: Int { + /// Sun is below the specified "horizon" 24 hours + /// "Day" length = 0 hours, trise and tset are + /// both set to the time when the sun is at south. + case SunAlwaysBelow = -1 + + /// Sun rises/sets this day, times stored at rise and set. + case RiseAndSet = 0 + + /// Sun above the specified "horizon" 24 hours. + /// trise set to time when the sun is at south, + /// minus 12 hours while tset is set to the south + /// time plus 12 hours. "Day" length = 24 hours + case SunAlwaysAbove = 1 + } + + /* +++Date last modified: 05-Jul-1997 */ + /* Updated comments, 05-Aug-2013 */ + /* + SUNRISET.C - computes Sun rise/set times, start/end of twilight, and + the length of the day at any date and latitude + + Written as DAYLEN.C, 1989-08-16 + + Modified to SUNRISET.C, 1992-12-01 + + (c) Paul Schlyter, 1989, 1992 + + Released to the public domain by Paul Schlyter, December 1992 + */ + + /// A macro to compute the number of days elapsed since 2000 Jan 0.0 + /// (which is equal to 1999 Dec 31, 0h UT) + private static func days_since_2000_Jan_0(y: Int, m: Int, d: Int) -> Int { + return (367*(y)-((7*((y)+(((m)+9)/12)))/4)+((275*(m))/9)+(d)-730530) + } + + /* Some conversion factors between radians and degrees */ + private static let PI = 3.1415926535897932384 + private static let RADEG = ( 180.0 / PI ) + private static let DEGRAD = ( PI / 180.0 ) + + /* The trigonometric functions in degrees */ + private static func sind(x: Double) -> Double { return sin((x)*DEGRAD) } + private static func cosd(x: Double) -> Double { return cos((x)*DEGRAD) } + private static func tand(x: Double) -> Double { return tan((x)*DEGRAD) } + private static func atand(x: Double) -> Double { return (RADEG*atan(x)) } + private static func asind(x: Double) -> Double { return (RADEG*asin(x)) } + private static func acosd(x: Double) -> Double { return (RADEG*acos(x)) } + private static func atan2d(y: Double, x: Double) -> Double { return (RADEG*atan2(y, x)) } + + /* Following are some macros around the "workhorse" function __daylen__ */ + /* They mainly fill in the desired values for the reference altitude */ + /* below the horizon, and also selects whether this altitude should */ + /* refer to the Sun's center or its upper limb. */ + + + /** This macro computes the length of the day, from sunrise to sunset. */ + /** Sunrise/set is considered to occur when the Sun's upper limb is */ + /** 35 arc minutes below the horizon (this accounts for the refraction */ + /** of the Earth's atmosphere). */ + static func day_length(year: Int, month: Int, day: Int, lon: Double, lat: Double) -> Double { + return daylen(year: year, month: month, day: day, lon: lon, lat: lat, altit: -35.0/60.0, upper_limb: 1) + } + + /** This macro computes the length of the day, including civil twilight. */ + /** Civil twilight starts/ends when the Sun's center is 6 degrees below */ + /** the horizon. */ + static func day_civil_twilight_length(year: Int, month: Int, day: Int, lon: Double, lat: Double) -> Double { + return daylen(year: year, month: month, day: day, lon: lon, lat: lat, altit: -6.0, upper_limb: 0) + } + + /** This macro computes the length of the day, incl. nautical twilight. */ + /** Nautical twilight starts/ends when the Sun's center is 12 degrees */ + /** below the horizon. */ + static func day_nautical_twilight_length(year: Int, month: Int, day: Int, lon: Double, lat: Double) -> Double { + return daylen(year: year, month: month, day: day, lon: lon, lat: lat, altit: -12.0, upper_limb: 0) + } + + /** This macro computes the length of the day, incl. astronomical twilight. */ + /** Astronomical twilight starts/ends when the Sun's center is 18 degrees */ + /** below the horizon. */ + static func day_astronomical_twilight_length(year: Int, month: Int, day: Int, lon: Double, lat: Double) -> Double { + return daylen(year: year, month: month, day: day, lon: lon, lat: lat, altit: -18.0, upper_limb: 0) + } + + /** This macro computes times for sunrise/sunset. */ + /** Sunrise/set is considered to occur when the Sun's upper limb is */ + /** 35 arc minutes below the horizon (this accounts for the refraction */ + /** of the Earth's atmosphere). */ + static func sun_rise_set(year: Int, month: Int, day: Int, lon: Double, lat: Double) -> (trise: Double, tset: Double, code: ReturnCode) { + let (start, end, code) = sunriset(year: year, month: month, day: day, lon: lon, lat: lat, altit: -35.0/60.0, upper_limb: 1) + return (start, end, code) + } + + /** This macro computes the start and end times of civil twilight. */ + /** Civil twilight starts/ends when the Sun's center is 6 degrees below */ + /** the horizon. */ + static func civil_twilight(year: Int, month: Int, day: Int, lon: Double, lat: Double) -> (trise: Double, tset: Double, code: ReturnCode) { + let (start, end, code) = sunriset(year: year, month: month, day: day, lon: lon, lat: lat, altit: -6.0, upper_limb: 0) + return (start, end, code) + } + + /** This macro computes the start and end times of nautical twilight. */ + /** Nautical twilight starts/ends when the Sun's center is 12 degrees */ + /** below the horizon. */ + static func nautical_twilight(year: Int, month: Int, day: Int, lon: Double, lat: Double) -> (trise: Double, tset: Double, code: ReturnCode) { + let (start, end, code) = sunriset(year: year, month: month, day: day, lon: lon, lat: lat, altit: -12.0, upper_limb: 0) + return (start, end, code) + } + + /** This macro computes the start and end times of astronomical twilight. */ + /** Astronomical twilight starts/ends when the Sun's center is 18 degrees */ + /** below the horizon. */ + static func astronomical_twilight(year: Int, month: Int, day: Int, lon: Double, lat: Double) -> (trise: Double, tset: Double, code: ReturnCode) { + let (start, end, code) = sunriset(year: year, month: month, day: day, lon: lon, lat: lat, altit: -18.0, upper_limb: 0) + return (start, end, code) + } + + /// The "workhorse" function for sun rise/set times + /// + /// - Parameters: + /// - year: calendar date, 1801-2099 only. + /// - month: calendar date, 1801-2099 only. + /// - day: calendar date, 1801-2099 only. + /// - lon: Eastern longitude positive, Western longitude negative. The longitude value IS critical in this function! + /// - lat: Northern latitude positive, Southern latitude negative + /// - altit: the altitude which the Sun should cross. Set to -35/60 degrees for rise/set, -6 degrees + /// for civil, -12 degrees for nautical and -18 degrees for astronomical twilight. + /// - upper_limb: non-zero -> upper limb, zero -> center + /// Set to non-zero (e.g. 1) when computing rise/set + /// times, and to zero when computing start/end of twilight. + /// - Returns: rise, set, code. + /// + /// Both times in hours UT are relative to the specified altitude, + /// and thus this function can be used to compute + /// various twilight times, as well as rise/set times. + /// + /// Code 0 = sun rises/sets this day, times stored at rise and set. + /// + /// Code +1 = sun above the specified "horizon" 24 hours. + /// *trise set to time when the sun is at south, + /// minus 12 hours while *tset is set to the south + /// time plus 12 hours. "Day" length = 24 hours + /// + /// Code -1 = sun is below the specified "horizon" 24 hours + /// "Day" length = 0 hours, *trise and *tset are + /// both set to the time when the sun is at south. + private static func sunriset(year: Int, + month: Int, + day: Int, + lon: Double, + lat: Double, + altit: Double, + upper_limb: Int) -> (trise: Double, tset: Double, code: ReturnCode) { + + var altit = altit + var d: Double /* Days since 2000 Jan 0.0 (negative before) */ + var sr: Double /* Solar distance, astronomical units */ + var sRA: Double /* Sun's Right Ascension */ + var sdec: Double /* Sun's declination */ + var sradius: Double /* Sun's apparent radius */ + var t: Double /* Diurnal arc */ + var tsouth: Double /* Time when Sun is at south */ + var sidtime: Double /* Local sidereal time */ + + var rc: ReturnCode = ReturnCode.RiseAndSet /* Return cde from function - usually 0 */ + + /* Compute d of 12h local mean solar time */ + d = Double(days_since_2000_Jan_0(y: year, m: month, d: day)) + 0.5 - lon/360.0 + + /* Compute the local sidereal time of this moment */ + sidtime = revolution(x: GMST0(d: d) + 180.0 + lon) + + /* Compute Sun's RA, Decl and distance at this moment */ + (sRA, sdec, sr) = sun_RA_dec(d: d) + + /* Compute time when Sun is at south - in hours UT */ + tsouth = 12.0 - rev180(x: sidtime - sRA)/15.0 + + /* Compute the Sun's apparent radius in degrees */ + sradius = 0.2666 / sr + + /* Do correction to upper limb, if necessary */ + if upper_limb != 0 { + altit -= sradius + } + + /* Compute the diurnal arc that the Sun traverses to reach */ + /* the specified altitude altit: */ + do { + let cost: Double = (sind(x: altit) - sind(x: lat) * sind(x: sdec)) / (cosd(x: lat) * cosd(x: sdec)) + if cost >= 1.0 { + rc = ReturnCode.SunAlwaysBelow + t = 0.0 /* Sun always below altit */ + } else if cost <= -1.0 { + rc = ReturnCode.SunAlwaysAbove + t = 12.0 /* Sun always above altit */ + } else { + t = acosd(x: cost)/15.0 /* The diurnal arc, hours */ + } + } + + /* Store rise and set times - in hours UT */ + let trise = tsouth - t + let tset = tsouth + t + + return (trise, tset, rc) + } /* __sunriset__ */ + + /// The "workhorse" function + /// + /// - Parameters: + /// - year: year,month,date = calendar date, 1801-2099 only. + /// - month: year,month,date = calendar date, 1801-2099 only. + /// - day: year,month,date = calendar date, 1801-2099 only. + /// - lon: Eastern longitude positive, Western longitude negative + /// - lat: Northern latitude positive, Southern latitude negative + /// - altit: altit = the altitude which the Sun should cross + /// Set to -35/60 degrees for rise/set, -6 degrees + /// for civil, -12 degrees for nautical and -18 + /// degrees for astronomical twilight. + /// - upper_limb: upper_limb: non-zero -> upper limb, zero -> center + /// Set to non-zero (e.g. 1) when computing day length + /// and to zero when computing day+twilight length. + /// - Returns: Day number + private static func daylen(year: Int, month: Int, day: Int, lon: Double, lat: Double, + altit: Double, upper_limb: Int) -> Double { + /**********************************************************************/ + /* Note: year,month,date = calendar date, 1801-2099 only. */ + /* Eastern longitude positive, Western longitude negative */ + /* Northern latitude positive, Southern latitude negative */ + /* The longitude value is not critical. Set it to the correct */ + /* longitude if you're picky, otherwise set to to, say, 0.0 */ + /* The latitude however IS critical - be sure to get it correct */ + /* altit = the altitude which the Sun should cross */ + /* Set to -35/60 degrees for rise/set, -6 degrees */ + /* for civil, -12 degrees for nautical and -18 */ + /* degrees for astronomical twilight. */ + /* upper_limb: non-zero -> upper limb, zero -> center */ + /* Set to non-zero (e.g. 1) when computing day length */ + /* and to zero when computing day+twilight length. */ + /**********************************************************************/ + + var altit = altit + var d: Double /* Days since 2000 Jan 0.0 (negative before) */ + var obl_ecl: Double /* Obliquity (inclination) of Earth's axis */ + var sr: Double /* Solar distance, astronomical units */ + var slon: Double /* True solar longitude */ + var sin_sdecl: Double /* Sine of Sun's declination */ + var cos_sdecl: Double /* Cosine of Sun's declination */ + var sradius: Double /* Sun's apparent radius */ + var t: Double /* Diurnal arc */ + + /* Compute d of 12h local mean solar time */ + d = Double(days_since_2000_Jan_0(y: year, m: month, d: day)) + 0.5 - lon/360.0 + + /* Compute obliquity of ecliptic (inclination of Earth's axis) */ + obl_ecl = 23.4393 - 3.563E-7 * d + + /* Compute Sun's ecliptic longitude and distance */ + (slon, sr) = sunpos(d: d) + + /* Compute sine and cosine of Sun's declination */ + sin_sdecl = sind(x: obl_ecl) * sind(x: slon) + cos_sdecl = sqrt(1.0 - sin_sdecl * sin_sdecl) + + /* Compute the Sun's apparent radius, degrees */ + sradius = 0.2666 / sr + + /* Do correction to upper limb, if necessary */ + if upper_limb != 0 { + altit -= sradius + } + + /* Compute the diurnal arc that the Sun traverses to reach */ + /* the specified altitude altit: */ + do { + let cost: Double = (sind(x: altit) - sind(x: lat) * sin_sdecl) / (cosd(x: lat) * cos_sdecl) + if cost >= 1.0 { + t = 0.0 /* Sun always below altit */ + } else if cost <= -1.0 { + t = 24.0 /* Sun always above altit */ + } else { + t = (2.0/15.0) * acosd(x: cost) /* The diurnal arc, hours */ + } + } + + return t + } /* __daylen__ */ + + + /// This function computes the Sun's position at any instant. + private static func sunpos(d: Double) -> (lon: Double, r: Double) { + /******************************************************/ + /* Computes the Sun's ecliptic longitude and distance */ + /* at an instant given in d, number of days since */ + /* 2000 Jan 0.0. The Sun's ecliptic latitude is not */ + /* computed, since it's always very near 0. */ + /******************************************************/ + + var M: Double /* Mean anomaly of the Sun */ + var w: Double /* Mean longitude of perihelion */ + /* Note: Sun's mean longitude = M + w */ + var e: Double /* Eccentricity of Earth's orbit */ + var E: Double /* Eccentric anomaly */ + var x: Double + var y: Double /* x, y coordinates in orbit */ + var v: Double /* True anomaly */ + + /* Compute mean elements */ + M = revolution(x: 356.0470 + 0.9856002585 * d) + w = 282.9404 + 4.70935E-5 * d + e = 0.016709 - 1.151E-9 * d + + /* Compute true longitude and radius vector */ + E = M + e * RADEG * sind(x: M) * (1.0 + e * cosd(x: M)) + x = cosd(x: E) - e + y = sqrt(1.0 - e*e) * sind(x: E) + let r = sqrt(x*x + y*y) /* Solar distance */ + v = atan2d(y: y, x: x) /* True anomaly */ + var lon = v + w /* True solar longitude */ + if lon >= 360.0 { + lon -= 360.0 /* Make it 0..360 degrees */ + } + return (lon, r) + } + + private static func sun_RA_dec(d: Double) -> (RA: Double, dec: Double, r: Double) { + /******************************************************/ + /* Computes the Sun's equatorial coordinates RA, Decl */ + /* and also its distance, at an instant given in d, */ + /* the number of days since 2000 Jan 0.0. */ + /******************************************************/ + + var obl_ecl: Double + var x: Double + var y: Double + var z: Double + + /* Compute Sun's ecliptical coordinates */ + let (lon, r) = sunpos(d: d) + + /* Compute ecliptic rectangular coordinates (z=0) */ + x = r * cosd(x: lon) + y = r * sind(x: lon) + + /* Compute obliquity of ecliptic (inclination of Earth's axis) */ + obl_ecl = 23.4393 - 3.563E-7 * d + + /* Convert to equatorial rectangular coordinates - x is unchanged */ + z = y * sind(x: obl_ecl) + y = y * cosd(x: obl_ecl) + + /* Convert to spherical coordinates */ + let RA = atan2d(y: y, x: x) + let dec = atan2d(y: z, x: sqrt(x*x + y*y)) + return (RA, dec, r) + } /* sun_RA_dec */ + + private static let INV360: Double = (1.0 / 360.0) + + /*******************************************************************/ + /** This function reduces any angle to within the first revolution */ + /** by subtracting or adding even multiples of 360.0 until the */ + /** result is >= 0.0 and < 360.0 */ + /*******************************************************************/ + private static func revolution(x: Double) -> Double { + /*****************************************/ + /* Reduce angle to within 0..360 degrees */ + /*****************************************/ + + return (x - 360.0 * floor(x * INV360)) + } /* revolution */ + + private static func rev180(x: Double) -> Double { + /*********************************************/ + /* Reduce angle to within +180..+180 degrees */ + /*********************************************/ + + return (x - 360.0 * floor(x * INV360 + 0.5)) + } /* revolution */ + + /********************************************************************/ + /** This function computes GMST0, the Greenwich Mean Sidereal Time */ + /** at 0h UT (i.e. the sidereal time at the Greenwhich meridian at */ + /** 0h UT). GMST is then the sidereal time at Greenwich at any */ + /** time of the day. I've generalized GMST0 as well, and define it */ + /** as: GMST0 = GMST - UT -- this allows GMST0 to be computed at */ + /** other times than 0h UT as well. While this sounds somewhat */ + /** contradictory, it is very practical: instead of computing */ + /** GMST like: */ + /** */ + /** GMST = (GMST0) + UT * (366.2422/365.2422) */ + /** */ + /** where (GMST0) is the GMST last time UT was 0 hours, one simply */ + /** computes: */ + /** */ + /** GMST = GMST0 + UT */ + /** */ + /** where GMST0 is the GMST "at 0h UT" but at the current moment! */ + /** Defined in this way, GMST0 will increase with about 4 min a */ + /** day. It also happens that GMST0 (in degrees, 1 hr = 15 degr) */ + /** is equal to the Sun's mean longitude plus/minus 180 degrees! */ + /** (if we neglect aberration, which amounts to 20 seconds of arc */ + /** or 1.33 seconds of time) */ + /** */ + /********************************************************************/ + private static func GMST0(d: Double) -> Double { + var sidtim0: Double + /* Sidtime at 0h UT = L (Sun's mean longitude) + 180.0 degr */ + /* L = M + w, as defined in sunpos(). Since I'm too lazy to */ + /* add these numbers, I'll let the C compiler do it for me. */ + /* Any decent C compiler will add the constants at compile */ + /* time, imposing no runtime or code overhead. */ + sidtim0 = revolution(x: (180.0 + 356.0470 + 282.9404) + + (0.9856002585 + 4.70935E-5) * d) + return sidtim0 + } /* GMST0 */ + +} diff --git a/Solstice/Helpers/NotificationManager.swift b/Solstice/Helpers/NotificationManager.swift index 2f42d7f7..96b8917c 100644 --- a/Solstice/Helpers/NotificationManager.swift +++ b/Solstice/Helpers/NotificationManager.swift @@ -8,7 +8,6 @@ import Foundation import UserNotifications import CoreLocation -import Solar import SwiftUI import CoreData #if os(iOS) && !WIDGET_EXTENSION @@ -86,7 +85,7 @@ class NotificationManager: NSObject, UNUserNotificationCenterDelegate { for i in 0...63 { let date = calendar.date(byAdding: .day, value: i, to: Date()) ?? .now - guard let solar = Solar(for: date, coordinate: location.coordinate) else { + guard let solar = NTSolar(for: date, coordinate: location.coordinate, timeZone: timeZone) else { continue } @@ -150,7 +149,7 @@ class NotificationManager: NSObject, UNUserNotificationCenterDelegate { } #endif - static func getNextNotificationDate(after date: Date, with solar: Solar? = nil) -> Date { + static func getNextNotificationDate(after date: Date, with solar: NTSolar? = nil) -> Date { if scheduleType == .specificTime { let hour = notificationDateComponents.hour ?? 0 let minute = notificationDateComponents.minute ?? 0 @@ -180,7 +179,7 @@ class NotificationManager: NSObject, UNUserNotificationCenterDelegate { /// don't alter notification previews. /// - Returns: A `NotificationContent` object appropriate for the context static func buildNotificationContent(for date: Date, location: CLLocation, timeZone: TimeZone = .autoupdatingCurrent, in context: Context = .notification) -> NotificationContent? { - guard let solar = Solar(for: date, coordinate: location.coordinate) else { return nil } + guard let solar = NTSolar(for: date, coordinate: location.coordinate, timeZone: timeZone) else { return nil } let difference = solar.daylightDuration - (solar.yesterday?.daylightDuration ?? 0) @@ -259,7 +258,7 @@ class NotificationManager: NSObject, UNUserNotificationCenterDelegate { // MARK: Body Generation /// Builds the notification body with all enabled content fragments - private static func buildNotificationBody(solar: Solar, timeZone: TimeZone, difference: TimeInterval, date: Date, context: Context) -> String { + private static func buildNotificationBody(solar: NTSolar, timeZone: TimeZone, difference: TimeInterval, date: Date, context: Context) -> String { let duration = solar.daylightDuration.localizedString let differenceString = difference.localizedString @@ -293,7 +292,7 @@ class NotificationManager: NSObject, UNUserNotificationCenterDelegate { // MARK: Body Fragments - private static func sunTimesFragment(solar: Solar, timeZone: TimeZone) -> String { + private static func sunTimesFragment(solar: NTSolar, timeZone: TimeZone) -> String { let sunriseTime = solar.safeSunrise.withTimeZoneAdjustment(for: timeZone).formatted(.dateTime.hour().minute()) let sunsetTime = solar.safeSunset.withTimeZoneAdjustment(for: timeZone).formatted(.dateTime.hour().minute()) let format = NSLocalizedString( diff --git a/Solstice/List View/GraphicalLocationListRow.swift b/Solstice/List View/GraphicalLocationListRow.swift index 75879eef..10d5c066 100644 --- a/Solstice/List View/GraphicalLocationListRow.swift +++ b/Solstice/List View/GraphicalLocationListRow.swift @@ -7,14 +7,13 @@ import SwiftUI import TimeMachine -import Solar struct GraphicalLocationListRow: View { @Environment(\.timeMachine) private var timeMachine var location: Location - var solar: Solar? { - Solar(for: timeMachine.date, coordinate: location.coordinate) + var solar: NTSolar? { + NTSolar(for: timeMachine.date, coordinate: location.coordinate, timeZone: location.timeZone) } var body: some View { diff --git a/Solstice/List View/LocationListRow.swift b/Solstice/List View/LocationListRow.swift index b74c0a9b..06230865 100644 --- a/Solstice/List View/LocationListRow.swift +++ b/Solstice/List View/LocationListRow.swift @@ -6,7 +6,6 @@ // import SwiftUI -import Solar import Suite import TimeMachine @@ -20,8 +19,8 @@ struct LocationListRow: View { var headingFontWeight: Font.Weight = .medium - private var solar: Solar? { - Solar(for: timeMachine.date, coordinate: location.coordinate) + private var solar: NTSolar? { + NTSolar(for: timeMachine.date, coordinate: location.coordinate, timeZone: location.timeZone) } private var isCurrentLocation: Bool { diff --git a/Solstice/List View/SidebarListView.swift b/Solstice/List View/SidebarListView.swift index 8816c367..4b477683 100644 --- a/Solstice/List View/SidebarListView.swift +++ b/Solstice/List View/SidebarListView.swift @@ -6,7 +6,6 @@ // import SwiftUI -import Solar import TimeMachine struct SidebarListView: View { @@ -116,7 +115,7 @@ struct SidebarListView: View { private func savedLocationPreview(for item: SavedLocation) -> some View { Form { - if let solar = Solar(for: timeMachine.date, coordinate: item.coordinate) { + if let solar = NTSolar(for: timeMachine.date, coordinate: item.coordinate, timeZone: item.timeZone) { DailyOverview(solar: solar, location: item) } } @@ -167,8 +166,8 @@ extension SidebarListView { return lhs.timeZone.secondsFromGMT() > rhs.timeZone.secondsFromGMT() } case .daylightDuration: - guard let lhsSolar = Solar(for: timeMachine.date, coordinate: lhs.coordinate), - let rhsSolar = Solar(for: timeMachine.date, coordinate: rhs.coordinate) else { + guard let lhsSolar = NTSolar(for: timeMachine.date, coordinate: lhs.coordinate, timeZone: lhs.timeZone), + let rhsSolar = NTSolar(for: timeMachine.date, coordinate: rhs.coordinate, timeZone: rhs.timeZone) else { return true } diff --git a/Solstice/Settings/AboutSolsticeView.swift b/Solstice/Settings/AboutSolsticeView.swift index e44aaba6..3b6a7319 100644 --- a/Solstice/Settings/AboutSolsticeView.swift +++ b/Solstice/Settings/AboutSolsticeView.swift @@ -87,16 +87,6 @@ fileprivate struct FullStoryView: View { } } } - - if let url = URL(string: "https://github.com/ceeK/Solar") { - Section { - Link(destination: url) { - Text("ceeK/Solar") - } - } header: { - Text("Open Source Acknowledgements") - } - } } .formStyle(.grouped) .navigationTitle("About Solstice") diff --git a/Solstice/Settings/NotificationSettings.swift b/Solstice/Settings/NotificationSettings.swift index d0a7778b..f6fdefce 100644 --- a/Solstice/Settings/NotificationSettings.swift +++ b/Solstice/Settings/NotificationSettings.swift @@ -6,7 +6,6 @@ // import SwiftUI -import Solar import CoreLocation fileprivate typealias NotificationFragment = (label: String, value: Binding) diff --git a/Widget/Countdown Widget/CountdownWidget.swift b/Widget/Countdown Widget/CountdownWidget.swift index 3c9aeb9e..4f140b25 100644 --- a/Widget/Countdown Widget/CountdownWidget.swift +++ b/Widget/Countdown Widget/CountdownWidget.swift @@ -7,7 +7,6 @@ import WidgetKit import SwiftUI -import Solar struct CountdownWidget: Widget { #if os(iOS) @@ -22,7 +21,7 @@ struct CountdownWidget: Widget { private func widgetContent(for timelineEntry: SolsticeWidgetTimelineEntry) -> some View { CountdownWidgetView(entry: timelineEntry) .containerBackground(for: .widget) { - SkyGradient(solar: Solar(for: timelineEntry.date, coordinate: (timelineEntry.location ?? .defaultLocation).coordinate)!) + SkyGradient(ntSolar: NTSolar(for: timelineEntry.date, coordinate: (timelineEntry.location ?? .defaultLocation).coordinate, timeZone: (timelineEntry.location ?? .defaultLocation).timeZone)) } .widgetURL(timelineEntry.location?.url) } diff --git a/Widget/Countdown Widget/CountdownWidgetView+AccessoryWidgetViews.swift b/Widget/Countdown Widget/CountdownWidgetView+AccessoryWidgetViews.swift index 5e053dd8..090c9318 100644 --- a/Widget/Countdown Widget/CountdownWidgetView+AccessoryWidgetViews.swift +++ b/Widget/Countdown Widget/CountdownWidgetView+AccessoryWidgetViews.swift @@ -7,13 +7,12 @@ #if !os(macOS) import SwiftUI -import Solar import WidgetKit import Suite extension CountdownWidgetView { struct AccessoryInlineView: View { - var nextEvent: Solar.Event + var nextEvent: NTSolar.Event var body: some View { HStack { Image(systemName: nextEvent.imageName) @@ -27,8 +26,8 @@ extension CountdownWidgetView { var entryDate: Date - var previousEvent: Solar.Event - var nextEvent: Solar.Event + var previousEvent: NTSolar.Event + var nextEvent: NTSolar.Event @ViewBuilder var currentValueLabel: some View { @@ -65,7 +64,7 @@ extension CountdownWidgetView { } struct AccessoryRectangularView: View { - var nextEvent: Solar.Event + var nextEvent: NTSolar.Event var body: some View { HStack { @@ -89,8 +88,8 @@ extension CountdownWidgetView { } struct AccessoryCornerView: View { - var previousEvent: Solar.Event - var nextEvent: Solar.Event + var previousEvent: NTSolar.Event + var nextEvent: NTSolar.Event var body: some View { Image(systemName: nextEvent.imageName) diff --git a/Widget/Countdown Widget/CountdownWidgetView.swift b/Widget/Countdown Widget/CountdownWidgetView.swift index fec174ce..9bb128ff 100644 --- a/Widget/Countdown Widget/CountdownWidgetView.swift +++ b/Widget/Countdown Widget/CountdownWidgetView.swift @@ -6,7 +6,6 @@ // import SwiftUI -import Solar import WidgetKit import Suite @@ -87,11 +86,11 @@ struct CountdownWidgetView: SolsticeWidgetView { } extension CountdownWidgetView { - var nextSolarEvent: Solar.Event? { + var nextSolarEvent: NTSolar.Event? { solar?.nextSolarEvent } - var previousSolarEvent: Solar.Event? { + var previousSolarEvent: NTSolar.Event? { solar?.previousSolarEvent } diff --git a/Widget/Helpers/SolsticeWidgetTimelineProvider.swift b/Widget/Helpers/SolsticeWidgetTimelineProvider.swift index 5bb292d5..f197df73 100644 --- a/Widget/Helpers/SolsticeWidgetTimelineProvider.swift +++ b/Widget/Helpers/SolsticeWidgetTimelineProvider.swift @@ -8,7 +8,6 @@ import WidgetKit import CoreLocation import CoreData -import Solar import AppIntents struct SolsticeWidgetTimelineEntry: TimelineEntry { @@ -140,7 +139,7 @@ struct SolsticeTimelineProvider: AppIntentTimelineProvider { let calendar = Calendar.current guard let coordinate = widgetLocation?.coordinate, - let todaySolar = Solar(for: currentDate, coordinate: coordinate) else { + let todaySolar = NTSolar(for: currentDate, coordinate: coordinate, timeZone: widgetLocation?.timeZone ?? .autoupdatingCurrent) else { return Timeline( entries: [ SolsticeWidgetTimelineEntry(date: currentDate, location: widgetLocation, locationError: error) @@ -156,12 +155,12 @@ struct SolsticeTimelineProvider: AppIntentTimelineProvider { #endif var allKeyTimes: [Date] = [currentDate] var allHourlyTimes: [Date] = [] - var solarByDay: [Date: Solar] = [:] + var solarByDay: [Date: NTSolar] = [:] let today = calendar.startOfDay(for: currentDate) for dayOffset in 0..