diff --git a/Localizable.xcstrings b/Localizable.xcstrings index 1020c403..493cf344 100644 --- a/Localizable.xcstrings +++ b/Localizable.xcstrings @@ -1068,6 +1068,7 @@ } }, "Location Services" : { + "extractionState" : "stale", "localizations" : { "it" : { "stringUnit" : { @@ -1573,8 +1574,12 @@ } } } + }, + "Set up location services" : { + }, "Set Up Location Services" : { + "extractionState" : "stale", "localizations" : { "it" : { "stringUnit" : { @@ -1585,6 +1590,7 @@ } }, "Set up location services to see sunrise and sunset times for your current location in the app, widgets, and notifications" : { + "extractionState" : "stale", "localizations" : { "it" : { "stringUnit" : { @@ -1998,6 +2004,9 @@ } } } + }, + "Welcome to Solstice" : { + }, "What location do you want to see the daylight for?" : { "localizations" : { @@ -2028,6 +2037,9 @@ } } } + }, + "You can use Solstice with or without your real location. Tap this button to allow or deny access to your location." : { + }, "You will need to enable location services for Solstice or choose a different location in order to receive notifications." : { "localizations" : { diff --git a/Solstice.xcodeproj/project.pbxproj b/Solstice.xcodeproj/project.pbxproj index 7e55d7ac..c4b96f74 100644 --- a/Solstice.xcodeproj/project.pbxproj +++ b/Solstice.xcodeproj/project.pbxproj @@ -207,6 +207,8 @@ 71A029BA2C36928400E19819 /* defaultData.json in Resources */ = {isa = PBXBuildFile; fileRef = 71A029B82C36928400E19819 /* defaultData.json */; }; 71A029BB2C36928400E19819 /* defaultData.json in Resources */ = {isa = PBXBuildFile; fileRef = 71A029B82C36928400E19819 /* defaultData.json */; }; 71A029BC2C36928400E19819 /* defaultData.json in Resources */ = {isa = PBXBuildFile; fileRef = 71A029B82C36928400E19819 /* defaultData.json */; }; + 71A029C12C38A5F900E19819 /* Tips.swift in Sources */ = {isa = PBXBuildFile; fileRef = 71A029C02C38A5F900E19819 /* Tips.swift */; }; + 71A029C22C38A5F900E19819 /* Tips.swift in Sources */ = {isa = PBXBuildFile; fileRef = 71A029C02C38A5F900E19819 /* Tips.swift */; }; 71A2BDE629B747940071ACE9 /* Globals.swift in Sources */ = {isa = PBXBuildFile; fileRef = 71A2BDE529B747940071ACE9 /* Globals.swift */; }; 71A2BDE729B747940071ACE9 /* Globals.swift in Sources */ = {isa = PBXBuildFile; fileRef = 71A2BDE529B747940071ACE9 /* Globals.swift */; }; 71A2BDE829B747940071ACE9 /* Globals.swift in Sources */ = {isa = PBXBuildFile; fileRef = 71A2BDE529B747940071ACE9 /* Globals.swift */; }; @@ -379,6 +381,7 @@ 71A029B12C3052BC00E19819 /* EquinoxAndSolsticeDescriptions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EquinoxAndSolsticeDescriptions.swift; sourceTree = ""; }; 71A029B32C368E1900E19819 /* SavedLocation++.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "SavedLocation++.swift"; sourceTree = ""; }; 71A029B82C36928400E19819 /* defaultData.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = defaultData.json; sourceTree = ""; }; + 71A029C02C38A5F900E19819 /* Tips.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Tips.swift; sourceTree = ""; }; 71A2BDE529B747940071ACE9 /* Globals.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Globals.swift; sourceTree = ""; }; 71A9A54F2AB82D6200C3A38C /* Localizable.xcstrings */ = {isa = PBXFileReference; lastKnownFileType = text.json.xcstrings; path = Localizable.xcstrings; sourceTree = ""; }; 71AEB7B029AE0A9B00A7952D /* AnyLocation.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AnyLocation.swift; sourceTree = ""; }; @@ -631,6 +634,7 @@ 71908ACC2AC95A5500C7B610 /* StringBuilder.swift */, 71F641C0299FCCFF00FE5AB5 /* TimeMachine.swift */, 716BE04F2C2E752F004DDCBC /* DeepLinkResolver.swift */, + 71A029C02C38A5F900E19819 /* Tips.swift */, ); path = Helpers; sourceTree = ""; @@ -1041,6 +1045,7 @@ 71DAB3D429CD865800E30148 /* EarthScene.swift in Sources */, 7198468E28E5895F00E866CE /* Persistence.swift in Sources */, 710D9B712AB4752D0093A9A6 /* ContentToggle.swift in Sources */, + 71A029C12C38A5F900E19819 /* Tips.swift in Sources */, 7195128129B713EC009D282F /* AdaptiveLabeledContent.swift in Sources */, 71DAB3D229CD862A00E30148 /* EarthNode.swift in Sources */, 71EAB72529C3479F00FA043F /* TimeMachineDeactivatorView.swift in Sources */, @@ -1124,6 +1129,7 @@ 716BE0522C2E7E53004DDCBC /* LocationSearchService.swift in Sources */, 719F927429ACD21300C06921 /* SolsticeApp.swift in Sources */, 713F7FF629BE091D00BEA156 /* TimeMachineView.swift in Sources */, + 71A029C22C38A5F900E19819 /* Tips.swift in Sources */, 719F927029ACD21300C06921 /* DetailView.swift in Sources */, 719F928729ACD28500C06921 /* Solstice.xcdatamodeld in Sources */, 71A029B52C368E1900E19819 /* SavedLocation++.swift in Sources */, diff --git a/Solstice/Helpers/Tips.swift b/Solstice/Helpers/Tips.swift new file mode 100644 index 00000000..d4b84da4 --- /dev/null +++ b/Solstice/Helpers/Tips.swift @@ -0,0 +1,21 @@ +// +// Tips.swift +// Solstice +// +// Created by Daniel Eden on 05/07/2024. +// + +import Foundation +import TipKit + +struct WelcomeTip: Tip { + var title: Text { + Text("Welcome to Solstice") + } + + var message: Text? { + Text("You can use Solstice with or without your real location. Tap this button to allow or deny access to your location.") + } + + let id = "WelcomeTip" +} diff --git a/Solstice/LocationPermissionScreenerView.swift b/Solstice/LocationPermissionScreenerView.swift index 1bbe6661..f338b014 100644 --- a/Solstice/LocationPermissionScreenerView.swift +++ b/Solstice/LocationPermissionScreenerView.swift @@ -6,32 +6,62 @@ // import SwiftUI +import TipKit struct LocationPermissionScreenerView: View { @Environment(\.openURL) var openURL @EnvironmentObject var currentLocation: CurrentLocation + let tip = WelcomeTip() + + var fallbackTipView: some View { + #if !os(watchOS) + GroupBox { + tip.message + } label: { + tip.title + } + #else + VStack(alignment: .leading) { + tip.title.font(.headline) + tip.message + } + #endif + } + var body: some View { Section { + #if !os(watchOS) + if #unavailable(iOS 17, macOS 13, visionOS 1) { + fallbackTipView + } + #else + fallbackTipView + #endif + Button { currentLocation.requestAccess() #if os(macOS) openURL.callAsFunction(URL(string: "x-apple.systempreferences:com.apple.preference.security?Privacy_LocationServices")!) #endif } label: { - Text("Set Up Location Services") + Text("Set up location services") } + #if !os(watchOS) + .modify { content in + if #available(iOS 17, macOS 14, *) { + content.popoverTip(WelcomeTip()) + } else { + content + } + } + #endif #if os(iOS) .buttonStyle(.borderless) #elseif os(macOS) .buttonStyle(.bordered) #endif - } header: { - Label("Location Services", systemImage: "location") - } footer: { - Text("Set up location services to see sunrise and sunset times for your current location in the app, widgets, and notifications") } - .navigationTitle(Text(verbatim: "Solstice")) } } diff --git a/Solstice/SolsticeApp.swift b/Solstice/SolsticeApp.swift index 22817144..c8004339 100644 --- a/Solstice/SolsticeApp.swift +++ b/Solstice/SolsticeApp.swift @@ -7,6 +7,7 @@ import SwiftUI import StoreKit +import TipKit @main struct SolsticeApp: App { @@ -46,6 +47,21 @@ struct SolsticeApp: App { return } } + .task { + if #available(iOS 17, watchOS 10, macOS 13, *) { + // Configure and load your tips at app launch. + do { + try Tips.configure([ + .displayFrequency(.immediate), + .datastoreLocation(.applicationDefault) + ]) + } + catch { + // Handle TipKit errors + print("Error initializing TipKit \(error.localizedDescription)") + } + } + } .migrateAppFeatures() .environment(\.managedObjectContext, persistenceController.container.viewContext) }