diff --git a/LaunchIntents/Info.plist b/LaunchIntents/Info.plist
new file mode 100644
index 0000000..34469a7
--- /dev/null
+++ b/LaunchIntents/Info.plist
@@ -0,0 +1,24 @@
+
+
+
+
+ NSExtension
+
+ NSExtensionAttributes
+
+ IntentsRestrictedWhileLocked
+
+ IntentsRestrictedWhileProtectedDataUnavailable
+
+ IntentsSupported
+
+ SelectCountdownIntent
+
+
+ NSExtensionPointIdentifier
+ com.apple.intents-service
+ NSExtensionPrincipalClass
+ $(PRODUCT_MODULE_NAME).IntentHandler
+
+
+
diff --git a/LaunchIntents/IntentHandler.swift b/LaunchIntents/IntentHandler.swift
new file mode 100644
index 0000000..64a4941
--- /dev/null
+++ b/LaunchIntents/IntentHandler.swift
@@ -0,0 +1,70 @@
+//
+// IntentHandler.swift
+// LaunchIntents
+//
+// Created by Laurent Morvillier on 25/01/2023.
+//
+
+import Intents
+
+class IntentHandler: INExtension, SelectCountdownIntentHandling {
+
+// func resolveCountdown(for intent: SelectCountdownIntent, with completion: @escaping (CountdownPropertiesResolutionResult) -> Void) {
+// }
+
+ func resolveCountdown(for intent: SelectCountdownIntent) async -> CountdownPropertiesResolutionResult {
+ print("***resolveCountdown")
+ if let countdown = intent.countdown {
+ return CountdownPropertiesResolutionResult.success(with: countdown)
+ } else {
+ return CountdownPropertiesResolutionResult.needsValue()
+ }
+ }
+
+// func provideCountdownOptionsCollection(for intent: SelectCountdownIntent, with completion: @escaping (INObjectCollection?, Error?) -> Void) {
+//
+//
+// }
+
+ func provideCountdownOptionsCollection(for intent: SelectCountdownIntent) async throws -> INObjectCollection {
+ print("*** provideCountdownOptionsCollection")
+
+ do {
+ let countdowns = try IntentDataProvider.main.countdowns()
+
+ let properties: [CountdownProperties] = countdowns.map { countdown in
+
+ let displayName: String
+ let formattedDuration = countdown.duration.minuteSecond
+ if let name = countdown.name, !name.isEmpty {
+ displayName = "\(name) (\(formattedDuration))"
+ } else {
+ displayName = formattedDuration
+ }
+
+ let cp = CountdownProperties(identifier: countdown.objectID.uriRepresentation().absoluteString, display: displayName)
+ cp.name = countdown.name
+ cp.duration = NSNumber(value: countdown.duration)
+ return cp
+ }
+
+ let collection: INObjectCollection = INObjectCollection(items: properties)
+ print("*** provide \(properties.count) countdowns")
+
+ return collection
+ } catch {
+ print("error = \(error)")
+ throw error
+// completion(nil, error)
+ }
+ }
+
+
+ override func handler(for intent: INIntent) -> Any {
+ // This is the default implementation. If you want different objects to handle different intents,
+ // you can override this and return the handler you want for that particular intent.
+
+ return self
+ }
+
+}
diff --git a/LaunchIntents/LaunchIntents.entitlements b/LaunchIntents/LaunchIntents.entitlements
new file mode 100644
index 0000000..a7c6d47
--- /dev/null
+++ b/LaunchIntents/LaunchIntents.entitlements
@@ -0,0 +1,10 @@
+
+
+
+
+ com.apple.security.application-groups
+
+ group.com.staxriver.countdown
+
+
+
diff --git a/LaunchWidget/LaunchWidget.intentdefinition b/LaunchWidget/LaunchWidget.intentdefinition
index 239e495..006d76f 100644
--- a/LaunchWidget/LaunchWidget.intentdefinition
+++ b/LaunchWidget/LaunchWidget.intentdefinition
@@ -18,7 +18,7 @@
INIntentCategory
- start
+ information
INIntentDescriptionID
dIYBJB
INIntentEligibleForWidgets
@@ -45,7 +45,7 @@
INIntentParameterName
countdown
INIntentParameterObjectType
- Countdown
+ CountdownProperties
INIntentParameterObjectTypeNamespace
88xZPY
INIntentParameterPromptDialogs
@@ -116,20 +116,20 @@
INIntentType
Custom
INIntentVerb
- Start
+ View
INTypes
INTypeDisplayName
- Countdown
+ CountdownProperties
INTypeDisplayNameID
ZTfW1g
INTypeLastPropertyTag
102
INTypeName
- Countdown
+ CountdownProperties
INTypeProperties
diff --git a/LaunchWidget/LaunchWidget.swift b/LaunchWidget/LaunchWidget.swift
index 3369c88..1c810c5 100644
--- a/LaunchWidget/LaunchWidget.swift
+++ b/LaunchWidget/LaunchWidget.swift
@@ -11,27 +11,31 @@ import Intents
struct Provider: IntentTimelineProvider {
func placeholder(in context: Context) -> SimpleEntry {
- SimpleEntry(id: "", name: "Tea", duration: 4 * 60.0, date: Date(), configuration: ConfigurationIntent())
+ SimpleEntry(id: "", name: "Tea", duration: 4 * 60.0, date: Date(), configuration: SelectCountdownIntent())
}
- func getSnapshot(for configuration: ConfigurationIntent, in context: Context, completion: @escaping (SimpleEntry) -> ()) {
- let entry = SimpleEntry(id: "", name: "Tea", duration: 4 * 60.0, date: Date(), configuration: configuration)
+ func getSnapshot(for configuration: SelectCountdownIntent, in context: Context, completion: @escaping (SimpleEntry) -> ()) {
+
+ guard let countdown = configuration.countdown,
+ let identifier = countdown.identifier,
+ let duration = countdown.duration else {
+ print("WARNING PLACEHOLDER!")
+ completion(placeholder(in: context))
+ return
+ }
+
+ let entry = SimpleEntry(id: identifier, name: countdown.name, duration: duration.doubleValue, date: Date(), configuration: configuration)
completion(entry)
+
}
- func getTimeline(for configuration: ConfigurationIntent, in context: Context, completion: @escaping (Timeline) -> ()) {
- var entries: [SimpleEntry] = []
+ func getTimeline(for configuration: SelectCountdownIntent, in context: Context, completion: @escaping (Timeline) -> ()) {
- // Generate a timeline consisting of five entries an hour apart, starting from the current date.
- let currentDate = Date()
- for hourOffset in 0 ..< 5 {
- let entryDate = Calendar.current.date(byAdding: .hour, value: hourOffset, to: currentDate)!
- let entry = SimpleEntry(id: "", name: "Tea", duration: 4 * 60.0, date: entryDate, configuration: configuration)
- entries.append(entry)
+ getSnapshot(for: configuration, in: context) { entry in
+ let timeline = Timeline(entries: [entry], policy: .atEnd)
+ completion(timeline)
}
-
- let timeline = Timeline(entries: entries, policy: .atEnd)
- completion(timeline)
+
}
}
@@ -41,7 +45,7 @@ struct SimpleEntry: TimelineEntry {
let duration: Double
let date: Date
- let configuration: ConfigurationIntent
+ let configuration: SelectCountdownIntent
}
struct CountdownWidgetView: View {
@@ -84,7 +88,7 @@ struct LaunchWidget: Widget {
var body: some WidgetConfiguration {
IntentConfiguration(kind: kind,
- intent: ConfigurationIntent.self,
+ intent: SelectCountdownIntent.self,
provider: Provider()) { entry in
LaunchWidgetEntryView(entry: entry)
}
@@ -96,7 +100,7 @@ struct LaunchWidget: Widget {
struct LaunchWidget_Previews: PreviewProvider {
static var previews: some View {
- LaunchWidgetEntryView(entry: SimpleEntry(id: "", name: "Tea", duration: 3 * 60.0, date: Date(), configuration: ConfigurationIntent()))
+ LaunchWidgetEntryView(entry: SimpleEntry(id: "", name: "Tea", duration: 3 * 60.0, date: Date(), configuration: SelectCountdownIntent()))
.previewContext(WidgetPreviewContext(family: .systemSmall))
}
}
diff --git a/LeCountdown.xcodeproj/project.pbxproj b/LeCountdown.xcodeproj/project.pbxproj
index 2b6f338..b028038 100644
--- a/LeCountdown.xcodeproj/project.pbxproj
+++ b/LeCountdown.xcodeproj/project.pbxproj
@@ -33,6 +33,15 @@
C438C7E82981255D00BF3EF9 /* TimeInterval+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4060DF4297AE9A7003FAB80 /* TimeInterval+Extensions.swift */; };
C438C7EA2981260D00BF3EF9 /* CountdownView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C438C7E92981260D00BF3EF9 /* CountdownView.swift */; };
C438C7EB2981266F00BF3EF9 /* CountdownView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C438C7E92981260D00BF3EF9 /* CountdownView.swift */; };
+ C438C7F229812BB200BF3EF9 /* Intents.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = C438C7F129812BB200BF3EF9 /* Intents.framework */; };
+ C438C7F529812BB200BF3EF9 /* IntentHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = C438C7F429812BB200BF3EF9 /* IntentHandler.swift */; };
+ C438C7F929812BB200BF3EF9 /* LaunchIntents.appex in Embed Foundation Extensions */ = {isa = PBXBuildFile; fileRef = C438C7F029812BB200BF3EF9 /* LaunchIntents.appex */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; };
+ C438C7FD29812BF700BF3EF9 /* LaunchWidget.intentdefinition in Sources */ = {isa = PBXBuildFile; fileRef = C438C7DB2981216200BF3EF9 /* LaunchWidget.intentdefinition */; };
+ C438C7FF2981300500BF3EF9 /* IntentDataProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = C438C7FE2981300500BF3EF9 /* IntentDataProvider.swift */; };
+ C438C800298130E900BF3EF9 /* IntentDataProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = C438C7FE2981300500BF3EF9 /* IntentDataProvider.swift */; };
+ C438C8012981327600BF3EF9 /* Persistence.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4060DC8297AE73D003FAB80 /* Persistence.swift */; };
+ C438C802298132B900BF3EF9 /* LeCountdown.xcdatamodeld in Sources */ = {isa = PBXBuildFile; fileRef = C4060DCA297AE73D003FAB80 /* LeCountdown.xcdatamodeld */; };
+ C438C80529813FB400BF3EF9 /* TimeInterval+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4060DF4297AE9A7003FAB80 /* TimeInterval+Extensions.swift */; };
/* End PBXBuildFile section */
/* Begin PBXContainerItemProxy section */
@@ -57,6 +66,13 @@
remoteGlobalIDString = C438C7CD2981216200BF3EF9;
remoteInfo = LaunchWidgetExtension;
};
+ C438C7F729812BB200BF3EF9 /* PBXContainerItemProxy */ = {
+ isa = PBXContainerItemProxy;
+ containerPortal = C4060DB4297AE73B003FAB80 /* Project object */;
+ proxyType = 1;
+ remoteGlobalIDString = C438C7EF29812BB200BF3EF9;
+ remoteInfo = LaunchIntents;
+ };
/* End PBXContainerItemProxy section */
/* Begin PBXCopyFilesBuildPhase section */
@@ -67,6 +83,7 @@
dstSubfolderSpec = 13;
files = (
C438C7E32981216300BF3EF9 /* LaunchWidgetExtension.appex in Embed Foundation Extensions */,
+ C438C7F929812BB200BF3EF9 /* LaunchIntents.appex in Embed Foundation Extensions */,
);
name = "Embed Foundation Extensions";
runOnlyForDeploymentPostprocessing = 0;
@@ -102,6 +119,13 @@
C438C7DC2981216300BF3EF9 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; };
C438C7DE2981216300BF3EF9 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; };
C438C7E92981260D00BF3EF9 /* CountdownView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CountdownView.swift; sourceTree = ""; };
+ C438C7F029812BB200BF3EF9 /* LaunchIntents.appex */ = {isa = PBXFileReference; explicitFileType = "wrapper.app-extension"; includeInIndex = 0; path = LaunchIntents.appex; sourceTree = BUILT_PRODUCTS_DIR; };
+ C438C7F129812BB200BF3EF9 /* Intents.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Intents.framework; path = System/Library/Frameworks/Intents.framework; sourceTree = SDKROOT; };
+ C438C7F429812BB200BF3EF9 /* IntentHandler.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = IntentHandler.swift; sourceTree = ""; };
+ C438C7F629812BB200BF3EF9 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; };
+ C438C7FE2981300500BF3EF9 /* IntentDataProvider.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = IntentDataProvider.swift; sourceTree = ""; };
+ C438C80329813B2500BF3EF9 /* LaunchIntents.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = LaunchIntents.entitlements; sourceTree = ""; };
+ C438C80429813B3100BF3EF9 /* LeCountdown.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = LeCountdown.entitlements; sourceTree = ""; };
/* End PBXFileReference section */
/* Begin PBXFrameworksBuildPhase section */
@@ -135,6 +159,14 @@
);
runOnlyForDeploymentPostprocessing = 0;
};
+ C438C7ED29812BB200BF3EF9 /* Frameworks */ = {
+ isa = PBXFrameworksBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ C438C7F229812BB200BF3EF9 /* Intents.framework in Frameworks */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
/* End PBXFrameworksBuildPhase section */
/* Begin PBXGroup section */
@@ -145,6 +177,7 @@
C4060DD5297AE73D003FAB80 /* LeCountdownTests */,
C4060DDF297AE73D003FAB80 /* LeCountdownUITests */,
C438C7D42981216200BF3EF9 /* LaunchWidget */,
+ C438C7F329812BB200BF3EF9 /* LaunchIntents */,
C438C7CF2981216200BF3EF9 /* Frameworks */,
C4060DBD297AE73B003FAB80 /* Products */,
);
@@ -157,6 +190,7 @@
C4060DD2297AE73D003FAB80 /* LeCountdownTests.xctest */,
C4060DDC297AE73D003FAB80 /* LeCountdownUITests.xctest */,
C438C7CE2981216200BF3EF9 /* LaunchWidgetExtension.appex */,
+ C438C7F029812BB200BF3EF9 /* LaunchIntents.appex */,
);
name = Products;
sourceTree = "";
@@ -174,6 +208,8 @@
C4060DC8297AE73D003FAB80 /* Persistence.swift */,
C438C7C4298024E900BF3EF9 /* NSManagedContext+Extensions.swift */,
C4060DF4297AE9A7003FAB80 /* TimeInterval+Extensions.swift */,
+ C438C7FE2981300500BF3EF9 /* IntentDataProvider.swift */,
+ C438C80429813B3100BF3EF9 /* LeCountdown.entitlements */,
C4060DCD297AE73D003FAB80 /* Info.plist */,
C4060DCA297AE73D003FAB80 /* LeCountdown.xcdatamodeld */,
C4060DC5297AE73D003FAB80 /* Preview Content */,
@@ -211,6 +247,7 @@
children = (
C438C7D02981216200BF3EF9 /* WidgetKit.framework */,
C438C7D22981216200BF3EF9 /* SwiftUI.framework */,
+ C438C7F129812BB200BF3EF9 /* Intents.framework */,
);
name = Frameworks;
sourceTree = "";
@@ -228,6 +265,16 @@
path = LaunchWidget;
sourceTree = "";
};
+ C438C7F329812BB200BF3EF9 /* LaunchIntents */ = {
+ isa = PBXGroup;
+ children = (
+ C438C7F429812BB200BF3EF9 /* IntentHandler.swift */,
+ C438C80329813B2500BF3EF9 /* LaunchIntents.entitlements */,
+ C438C7F629812BB200BF3EF9 /* Info.plist */,
+ );
+ path = LaunchIntents;
+ sourceTree = "";
+ };
/* End PBXGroup section */
/* Begin PBXNativeTarget section */
@@ -244,6 +291,7 @@
);
dependencies = (
C438C7E22981216300BF3EF9 /* PBXTargetDependency */,
+ C438C7F829812BB200BF3EF9 /* PBXTargetDependency */,
);
name = LeCountdown;
productName = LeCountdown;
@@ -303,6 +351,23 @@
productReference = C438C7CE2981216200BF3EF9 /* LaunchWidgetExtension.appex */;
productType = "com.apple.product-type.app-extension";
};
+ C438C7EF29812BB200BF3EF9 /* LaunchIntents */ = {
+ isa = PBXNativeTarget;
+ buildConfigurationList = C438C7FA29812BB200BF3EF9 /* Build configuration list for PBXNativeTarget "LaunchIntents" */;
+ buildPhases = (
+ C438C7EC29812BB200BF3EF9 /* Sources */,
+ C438C7ED29812BB200BF3EF9 /* Frameworks */,
+ C438C7EE29812BB200BF3EF9 /* Resources */,
+ );
+ buildRules = (
+ );
+ dependencies = (
+ );
+ name = LaunchIntents;
+ productName = LaunchIntents;
+ productReference = C438C7F029812BB200BF3EF9 /* LaunchIntents.appex */;
+ productType = "com.apple.product-type.app-extension";
+ };
/* End PBXNativeTarget section */
/* Begin PBXProject section */
@@ -327,6 +392,9 @@
C438C7CD2981216200BF3EF9 = {
CreatedOnToolsVersion = 14.2;
};
+ C438C7EF29812BB200BF3EF9 = {
+ CreatedOnToolsVersion = 14.2;
+ };
};
};
buildConfigurationList = C4060DB7297AE73B003FAB80 /* Build configuration list for PBXProject "LeCountdown" */;
@@ -346,6 +414,7 @@
C4060DD1297AE73D003FAB80 /* LeCountdownTests */,
C4060DDB297AE73D003FAB80 /* LeCountdownUITests */,
C438C7CD2981216200BF3EF9 /* LaunchWidgetExtension */,
+ C438C7EF29812BB200BF3EF9 /* LaunchIntents */,
);
};
/* End PBXProject section */
@@ -382,6 +451,13 @@
);
runOnlyForDeploymentPostprocessing = 0;
};
+ C438C7EE29812BB200BF3EF9 /* Resources */ = {
+ isa = PBXResourcesBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
/* End PBXResourcesBuildPhase section */
/* Begin PBXSourcesBuildPhase section */
@@ -391,6 +467,7 @@
files = (
C438C7EA2981260D00BF3EF9 /* CountdownView.swift in Sources */,
C4060DC9297AE73D003FAB80 /* Persistence.swift in Sources */,
+ C438C7FF2981300500BF3EF9 /* IntentDataProvider.swift in Sources */,
C4060DC2297AE73B003FAB80 /* ContentView.swift in Sources */,
C438C7C12980228B00BF3EF9 /* CountdownScheduler.swift in Sources */,
C438C7C929803CA000BF3EF9 /* AppDelegate.swift in Sources */,
@@ -433,6 +510,19 @@
);
runOnlyForDeploymentPostprocessing = 0;
};
+ C438C7EC29812BB200BF3EF9 /* Sources */ = {
+ isa = PBXSourcesBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ C438C7F529812BB200BF3EF9 /* IntentHandler.swift in Sources */,
+ C438C80529813FB400BF3EF9 /* TimeInterval+Extensions.swift in Sources */,
+ C438C802298132B900BF3EF9 /* LeCountdown.xcdatamodeld in Sources */,
+ C438C8012981327600BF3EF9 /* Persistence.swift in Sources */,
+ C438C7FD29812BF700BF3EF9 /* LaunchWidget.intentdefinition in Sources */,
+ C438C800298130E900BF3EF9 /* IntentDataProvider.swift in Sources */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
/* End PBXSourcesBuildPhase section */
/* Begin PBXTargetDependency section */
@@ -451,6 +541,11 @@
target = C438C7CD2981216200BF3EF9 /* LaunchWidgetExtension */;
targetProxy = C438C7E12981216300BF3EF9 /* PBXContainerItemProxy */;
};
+ C438C7F829812BB200BF3EF9 /* PBXTargetDependency */ = {
+ isa = PBXTargetDependency;
+ target = C438C7EF29812BB200BF3EF9 /* LaunchIntents */;
+ targetProxy = C438C7F729812BB200BF3EF9 /* PBXContainerItemProxy */;
+ };
/* End PBXTargetDependency section */
/* Begin XCBuildConfiguration section */
@@ -574,6 +669,7 @@
ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
+ CODE_SIGN_ENTITLEMENTS = LeCountdown/LeCountdown.entitlements;
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 1;
DEVELOPMENT_ASSET_PATHS = "\"LeCountdown/Preview Content\"";
@@ -605,6 +701,7 @@
ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
+ CODE_SIGN_ENTITLEMENTS = LeCountdown/LeCountdown.entitlements;
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 1;
DEVELOPMENT_ASSET_PATHS = "\"LeCountdown/Preview Content\"";
@@ -760,6 +857,58 @@
};
name = Release;
};
+ C438C7FB29812BB200BF3EF9 /* Debug */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ CODE_SIGN_ENTITLEMENTS = LaunchIntents/LaunchIntents.entitlements;
+ CODE_SIGN_STYLE = Automatic;
+ CURRENT_PROJECT_VERSION = 1;
+ DEVELOPMENT_TEAM = 526E96RFNP;
+ GENERATE_INFOPLIST_FILE = YES;
+ INFOPLIST_FILE = LaunchIntents/Info.plist;
+ INFOPLIST_KEY_CFBundleDisplayName = LaunchIntents;
+ INFOPLIST_KEY_NSHumanReadableCopyright = "";
+ LD_RUNPATH_SEARCH_PATHS = (
+ "$(inherited)",
+ "@executable_path/Frameworks",
+ "@executable_path/../../Frameworks",
+ );
+ MARKETING_VERSION = 1.0;
+ PRODUCT_BUNDLE_IDENTIFIER = com.staxriver.LeCountdown.LaunchIntents;
+ PRODUCT_NAME = "$(TARGET_NAME)";
+ SKIP_INSTALL = YES;
+ SWIFT_EMIT_LOC_STRINGS = YES;
+ SWIFT_VERSION = 5.0;
+ TARGETED_DEVICE_FAMILY = "1,2";
+ };
+ name = Debug;
+ };
+ C438C7FC29812BB200BF3EF9 /* Release */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ CODE_SIGN_ENTITLEMENTS = LaunchIntents/LaunchIntents.entitlements;
+ CODE_SIGN_STYLE = Automatic;
+ CURRENT_PROJECT_VERSION = 1;
+ DEVELOPMENT_TEAM = 526E96RFNP;
+ GENERATE_INFOPLIST_FILE = YES;
+ INFOPLIST_FILE = LaunchIntents/Info.plist;
+ INFOPLIST_KEY_CFBundleDisplayName = LaunchIntents;
+ INFOPLIST_KEY_NSHumanReadableCopyright = "";
+ LD_RUNPATH_SEARCH_PATHS = (
+ "$(inherited)",
+ "@executable_path/Frameworks",
+ "@executable_path/../../Frameworks",
+ );
+ MARKETING_VERSION = 1.0;
+ PRODUCT_BUNDLE_IDENTIFIER = com.staxriver.LeCountdown.LaunchIntents;
+ PRODUCT_NAME = "$(TARGET_NAME)";
+ SKIP_INSTALL = YES;
+ SWIFT_EMIT_LOC_STRINGS = YES;
+ SWIFT_VERSION = 5.0;
+ TARGETED_DEVICE_FAMILY = "1,2";
+ };
+ name = Release;
+ };
/* End XCBuildConfiguration section */
/* Begin XCConfigurationList section */
@@ -808,6 +957,15 @@
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
+ C438C7FA29812BB200BF3EF9 /* Build configuration list for PBXNativeTarget "LaunchIntents" */ = {
+ isa = XCConfigurationList;
+ buildConfigurations = (
+ C438C7FB29812BB200BF3EF9 /* Debug */,
+ C438C7FC29812BB200BF3EF9 /* Release */,
+ );
+ defaultConfigurationIsVisible = 0;
+ defaultConfigurationName = Release;
+ };
/* End XCConfigurationList section */
/* Begin XCVersionGroup section */
diff --git a/LeCountdown/Info.plist b/LeCountdown/Info.plist
index 0352adb..4badefc 100644
--- a/LeCountdown/Info.plist
+++ b/LeCountdown/Info.plist
@@ -2,6 +2,13 @@
+ UIApplicationSceneManifest
+
+ UIApplicationSupportsMultipleScenes
+
+ UISceneConfigurations
+
+
NSUserActivityTypes
SelectCountdownIntent
diff --git a/LeCountdown/IntentDataProvider.swift b/LeCountdown/IntentDataProvider.swift
new file mode 100644
index 0000000..8246aec
--- /dev/null
+++ b/LeCountdown/IntentDataProvider.swift
@@ -0,0 +1,21 @@
+//
+// IntentDataProvider.swift
+// LeCountdown
+//
+// Created by Laurent Morvillier on 25/01/2023.
+//
+
+import Foundation
+
+class IntentDataProvider {
+
+ static let main: IntentDataProvider = IntentDataProvider()
+
+ func countdowns() throws -> [Countdown] {
+ let context = PersistenceController.shared.container.viewContext
+ let request = Countdown.fetchRequest()
+ request.sortDescriptors = [NSSortDescriptor(keyPath: (\Countdown.order), ascending: true)]
+ return try context.fetch(request)
+ }
+
+}
diff --git a/LeCountdown/LeCountdown.entitlements b/LeCountdown/LeCountdown.entitlements
new file mode 100644
index 0000000..a7c6d47
--- /dev/null
+++ b/LeCountdown/LeCountdown.entitlements
@@ -0,0 +1,10 @@
+
+
+
+
+ com.apple.security.application-groups
+
+ group.com.staxriver.countdown
+
+
+
diff --git a/LeCountdown/Persistence.swift b/LeCountdown/Persistence.swift
index 82214d6..854fdee 100644
--- a/LeCountdown/Persistence.swift
+++ b/LeCountdown/Persistence.swift
@@ -33,7 +33,12 @@ struct PersistenceController {
let container: NSPersistentCloudKitContainer
init(inMemory: Bool = false) {
+
+ let storeURL = URL.storeURL(for: "group.com.staxriver.countdown", databaseName: "group.com.staxriver.countdown")
+ let storeDescription = NSPersistentStoreDescription(url: storeURL)
+
container = NSPersistentCloudKitContainer(name: "LeCountdown")
+ container.persistentStoreDescriptions = [storeDescription]
if inMemory {
container.persistentStoreDescriptions.first!.url = URL(fileURLWithPath: "/dev/null")
}
@@ -57,3 +62,14 @@ struct PersistenceController {
container.viewContext.automaticallyMergesChangesFromParent = true
}
}
+
+fileprivate extension URL {
+ /// Returns a URL for the given app group and database pointing to the sqlite database.
+ static func storeURL(for appGroup: String, databaseName: String) -> URL {
+ guard let fileContainer = FileManager.default.containerURL(forSecurityApplicationGroupIdentifier: appGroup) else {
+ fatalError("Shared file container could not be created.")
+ }
+
+ return fileContainer.appendingPathComponent("\(databaseName).sqlite")
+ }
+}