Widget working

release
Laurent 3 years ago
parent 87ed5a2e0d
commit 92671c60c9
  1. 24
      LaunchIntents/Info.plist
  2. 70
      LaunchIntents/IntentHandler.swift
  3. 10
      LaunchIntents/LaunchIntents.entitlements
  4. 10
      LaunchWidget/LaunchWidget.intentdefinition
  5. 38
      LaunchWidget/LaunchWidget.swift
  6. 158
      LeCountdown.xcodeproj/project.pbxproj
  7. 7
      LeCountdown/Info.plist
  8. 21
      LeCountdown/IntentDataProvider.swift
  9. 10
      LeCountdown/LeCountdown.entitlements
  10. 16
      LeCountdown/Persistence.swift

@ -0,0 +1,24 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>NSExtension</key>
<dict>
<key>NSExtensionAttributes</key>
<dict>
<key>IntentsRestrictedWhileLocked</key>
<array/>
<key>IntentsRestrictedWhileProtectedDataUnavailable</key>
<array/>
<key>IntentsSupported</key>
<array>
<string>SelectCountdownIntent</string>
</array>
</dict>
<key>NSExtensionPointIdentifier</key>
<string>com.apple.intents-service</string>
<key>NSExtensionPrincipalClass</key>
<string>$(PRODUCT_MODULE_NAME).IntentHandler</string>
</dict>
</dict>
</plist>

@ -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<CountdownProperties>?, Error?) -> Void) {
//
//
// }
func provideCountdownOptionsCollection(for intent: SelectCountdownIntent) async throws -> INObjectCollection<CountdownProperties> {
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<CountdownProperties> = 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
}
}

@ -0,0 +1,10 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>com.apple.security.application-groups</key>
<array>
<string>group.com.staxriver.countdown</string>
</array>
</dict>
</plist>

@ -18,7 +18,7 @@
<array>
<dict>
<key>INIntentCategory</key>
<string>start</string>
<string>information</string>
<key>INIntentDescriptionID</key>
<string>dIYBJB</string>
<key>INIntentEligibleForWidgets</key>
@ -45,7 +45,7 @@
<key>INIntentParameterName</key>
<string>countdown</string>
<key>INIntentParameterObjectType</key>
<string>Countdown</string>
<string>CountdownProperties</string>
<key>INIntentParameterObjectTypeNamespace</key>
<string>88xZPY</string>
<key>INIntentParameterPromptDialogs</key>
@ -116,20 +116,20 @@
<key>INIntentType</key>
<string>Custom</string>
<key>INIntentVerb</key>
<string>Start</string>
<string>View</string>
</dict>
</array>
<key>INTypes</key>
<array>
<dict>
<key>INTypeDisplayName</key>
<string>Countdown</string>
<string>CountdownProperties</string>
<key>INTypeDisplayNameID</key>
<string>ZTfW1g</string>
<key>INTypeLastPropertyTag</key>
<integer>102</integer>
<key>INTypeName</key>
<string>Countdown</string>
<string>CountdownProperties</string>
<key>INTypeProperties</key>
<array>
<dict>

@ -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<Entry>) -> ()) {
var entries: [SimpleEntry] = []
func getTimeline(for configuration: SelectCountdownIntent, in context: Context, completion: @escaping (Timeline<Entry>) -> ()) {
// 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))
}
}

@ -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 = "<group>"; };
C438C7DE2981216300BF3EF9 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
C438C7E92981260D00BF3EF9 /* CountdownView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CountdownView.swift; sourceTree = "<group>"; };
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 = "<group>"; };
C438C7F629812BB200BF3EF9 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
C438C7FE2981300500BF3EF9 /* IntentDataProvider.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = IntentDataProvider.swift; sourceTree = "<group>"; };
C438C80329813B2500BF3EF9 /* LaunchIntents.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = LaunchIntents.entitlements; sourceTree = "<group>"; };
C438C80429813B3100BF3EF9 /* LeCountdown.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = LeCountdown.entitlements; sourceTree = "<group>"; };
/* 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 = "<group>";
@ -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 = "<group>";
@ -228,6 +265,16 @@
path = LaunchWidget;
sourceTree = "<group>";
};
C438C7F329812BB200BF3EF9 /* LaunchIntents */ = {
isa = PBXGroup;
children = (
C438C7F429812BB200BF3EF9 /* IntentHandler.swift */,
C438C80329813B2500BF3EF9 /* LaunchIntents.entitlements */,
C438C7F629812BB200BF3EF9 /* Info.plist */,
);
path = LaunchIntents;
sourceTree = "<group>";
};
/* 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 */

@ -2,6 +2,13 @@
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>UIApplicationSceneManifest</key>
<dict>
<key>UIApplicationSupportsMultipleScenes</key>
<false/>
<key>UISceneConfigurations</key>
<dict/>
</dict>
<key>NSUserActivityTypes</key>
<array>
<string>SelectCountdownIntent</string>

@ -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)
}
}

@ -0,0 +1,10 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>com.apple.security.application-groups</key>
<array>
<string>group.com.staxriver.countdown</string>
</array>
</dict>
</plist>

@ -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")
}
}

Loading…
Cancel
Save