diff --git a/WidgetExamplesWidgetExtension.entitlements b/IntentHandler/IntentHandler.entitlements similarity index 100% rename from WidgetExamplesWidgetExtension.entitlements rename to IntentHandler/IntentHandler.entitlements diff --git a/IntentHandler/IntentHandler.swift b/IntentHandler/IntentHandler.swift index 36e9f44..6770d5c 100644 --- a/IntentHandler/IntentHandler.swift +++ b/IntentHandler/IntentHandler.swift @@ -15,8 +15,4 @@ class IntentHandler: INExtension, DynamicPersonSelectionIntentHandling { let collection = INObjectCollection(items: persons) completion(collection, nil) } - - override func handler(for intent: INIntent) -> Any { - return self - } } diff --git a/Shared/Model/Contact.swift b/Shared/Model/Contact.swift index 7006076..461ba8f 100644 --- a/Shared/Model/Contact.swift +++ b/Shared/Model/Contact.swift @@ -8,9 +8,9 @@ import Foundation -struct Contact { - let name: String - let birthday: Date +struct Contact: Hashable, Codable { + var name: String + var dateOfBirth: Date } extension Contact: Identifiable { @@ -20,18 +20,22 @@ extension Contact: Identifiable { extension Contact { static func getAll() -> [Contact] { let key = UserDefaults.Keys.contacts.rawValue - let contacts = UserDefaults.appGroup.object(forKey: key) as? [Contact] - return contacts ?? [.friend1, .friend2] + guard let contacts: [Contact] = UserDefaults.appGroup.getArray(forKey: key) else { + let contacts: [Contact] = [.friend1, .friend2] + UserDefaults.appGroup.setArray(contacts, forKey: key) + return contacts + } + return contacts } static let friend1: Contact = { - let birthday = Calendar.current.date(byAdding: .day, value: 1, to: Date())! - return Contact(name: "Friend 1", birthday: birthday) + let date = Calendar.current.date(byAdding: .month, value: -2, to: Date())! + return Contact(name: "Friend 1", dateOfBirth: date) }() static let friend2: Contact = { - let birthday = Calendar.current.date(byAdding: .day, value: 3, to: Date())! - return Contact(name: "Friend 2", birthday: birthday) + let date = Calendar.current.date(byAdding: .year, value: -3, to: Date())! + return Contact(name: "Friend 2", dateOfBirth: date) }() } diff --git a/Shared/UserDefaults+Ext.swift b/Shared/UserDefaults+Ext.swift index c29fe42..d5a9353 100644 --- a/Shared/UserDefaults+Ext.swift +++ b/Shared/UserDefaults+Ext.swift @@ -18,3 +18,15 @@ extension UserDefaults { case contacts } } + +extension UserDefaults { + func setArray(_ array: [Element], forKey key: String) where Element: Encodable { + let data = try? JSONEncoder().encode(array) + set(data, forKey: key) + } + + func getArray(forKey key: String) -> [Element]? where Element: Decodable { + guard let data = data(forKey: key) else { return nil } + return try? JSONDecoder().decode([Element].self, from: data) + } +} diff --git a/WidgetExamples.xcodeproj/project.pbxproj b/WidgetExamples.xcodeproj/project.pbxproj index bca3f4c..ed23a39 100644 --- a/WidgetExamples.xcodeproj/project.pbxproj +++ b/WidgetExamples.xcodeproj/project.pbxproj @@ -128,6 +128,7 @@ E55497D42551C02E0066870B /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; E55497EB2551CA0B0066870B /* DynamicPersonSelection.intentdefinition */ = {isa = PBXFileReference; lastKnownFileType = file.intentdefinition; path = DynamicPersonSelection.intentdefinition; sourceTree = ""; }; E55498372551D2160066870B /* Contact.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Contact.swift; sourceTree = ""; }; + E554985A25521B8D0066870B /* IntentHandler.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = IntentHandler.entitlements; sourceTree = ""; }; E591C5712551666A002044C8 /* IntentWidget.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = IntentWidget.swift; sourceTree = ""; }; E591C57825516810002044C8 /* BackgroundColorSelection.intentdefinition */ = {isa = PBXFileReference; lastKnownFileType = file.intentdefinition; path = BackgroundColorSelection.intentdefinition; sourceTree = ""; }; /* End PBXFileReference section */ @@ -162,7 +163,6 @@ E542134D253879B700CCC9C3 = { isa = PBXGroup; children = ( - E54213CF253892AB00CCC9C3 /* WidgetExamplesWidgetExtension.entitlements */, E5421358253879B700CCC9C3 /* WidgetExamples */, E542137325387A0300CCC9C3 /* WidgetExamplesWidget */, E55497D12551C02E0066870B /* IntentHandler */, @@ -213,6 +213,7 @@ E542137325387A0300CCC9C3 /* WidgetExamplesWidget */ = { isa = PBXGroup; children = ( + E54213CF253892AB00CCC9C3 /* WidgetExamplesWidgetExtension.entitlements */, E542137425387A0300CCC9C3 /* WidgetExamplesWidget.swift */, E54213C7253891CB00CCC9C3 /* AppGroupWidget */, E542139725387ECE00CCC9C3 /* ClockWidget */, @@ -367,6 +368,7 @@ E55497D12551C02E0066870B /* IntentHandler */ = { isa = PBXGroup; children = ( + E554985A25521B8D0066870B /* IntentHandler.entitlements */, E55497D22551C02E0066870B /* IntentHandler.swift */, E55497D42551C02E0066870B /* Info.plist */, ); @@ -762,7 +764,7 @@ buildSettings = { ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; ASSETCATALOG_COMPILER_WIDGET_BACKGROUND_COLOR_NAME = WidgetBackground; - CODE_SIGN_ENTITLEMENTS = WidgetExamplesWidgetExtension.entitlements; + CODE_SIGN_ENTITLEMENTS = WidgetExamplesWidget/WidgetExamplesWidgetExtension.entitlements; CODE_SIGN_STYLE = Automatic; DEVELOPMENT_TEAM = DMCVYHA293; INFOPLIST_FILE = WidgetExamplesWidget/Resources/Info.plist; @@ -784,7 +786,7 @@ buildSettings = { ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; ASSETCATALOG_COMPILER_WIDGET_BACKGROUND_COLOR_NAME = WidgetBackground; - CODE_SIGN_ENTITLEMENTS = WidgetExamplesWidgetExtension.entitlements; + CODE_SIGN_ENTITLEMENTS = WidgetExamplesWidget/WidgetExamplesWidgetExtension.entitlements; CODE_SIGN_STYLE = Automatic; DEVELOPMENT_TEAM = DMCVYHA293; INFOPLIST_FILE = WidgetExamplesWidget/Resources/Info.plist; @@ -804,6 +806,7 @@ E55497D92551C02E0066870B /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { + CODE_SIGN_ENTITLEMENTS = IntentHandler/IntentHandler.entitlements; CODE_SIGN_STYLE = Automatic; DEVELOPMENT_TEAM = DMCVYHA293; INFOPLIST_FILE = IntentHandler/Info.plist; @@ -824,6 +827,7 @@ E55497DA2551C02E0066870B /* Release */ = { isa = XCBuildConfiguration; buildSettings = { + CODE_SIGN_ENTITLEMENTS = IntentHandler/IntentHandler.entitlements; CODE_SIGN_STYLE = Automatic; DEVELOPMENT_TEAM = DMCVYHA293; INFOPLIST_FILE = IntentHandler/Info.plist; diff --git a/WidgetExamples/ContentView.swift b/WidgetExamples/ContentView.swift index cc1c70d..0747cf0 100644 --- a/WidgetExamples/ContentView.swift +++ b/WidgetExamples/ContentView.swift @@ -15,6 +15,8 @@ struct ContentView: View { @AppStorage(UserDefaults.Keys.luckyNumber.rawValue, store: UserDefaults.appGroup) private var luckyNumber = 0 + @State private var contacts = Contact.getAll() + @FetchRequest(entity: Item.entity(), sortDescriptors: []) private var items: FetchedResults var body: some View { @@ -22,11 +24,14 @@ struct ContentView: View { appGroupWidgetSection coreDataWidgetSection deepLinkWidgetSection + dynamicIntentWidgetSection previewWidgetSection } .listStyle(InsetGroupedListStyle()) } +} +private extension ContentView { var appGroupWidgetSection: some View { Section(header: Text("AppGroup Widget")) { Text("Lucky number: \(luckyNumber)") @@ -42,9 +47,11 @@ struct ContentView: View { try? String(luckyNumber).write(to: url, atomically: false, encoding: .utf8) } } +} +private extension ContentView { var coreDataWidgetSection: some View { - return Section(header: Text("CoreData Widget")) { + Section(header: Text("CoreData Widget")) { Text("Items count: \(items.count)") Button("Add new item") { let context = CoreDataStack.shared.workingContext @@ -74,7 +81,9 @@ struct ContentView: View { try? String(luckyNumber).write(to: url, atomically: false, encoding: .utf8) } } +} +private extension ContentView { var deepLinkWidgetSection: some View { Section(header: Text("DeepLink Widget")) { Text("") @@ -86,7 +95,33 @@ struct ContentView: View { } } } +} + +private extension ContentView { + var dynamicIntentWidgetSection: some View { + Section(header: Text("Dynamic Intent Widget")) { + ForEach(contacts.indices, id: \.self) { index in + HStack { + TextField("", text: $contacts[index].name, onCommit: { + saveContacts() + }) + DatePicker("", selection: $contacts[index].dateOfBirth, displayedComponents: .date) + .onChange(of: contacts[index].dateOfBirth) { _ in + saveContacts() + } + } + } + } + } + + func saveContacts() { + let key = UserDefaults.Keys.contacts.rawValue + UserDefaults.appGroup.setArray(contacts, forKey: key) + WidgetCenter.shared.reloadTimelines(ofKind: "DynamicIntentWidget") + } +} +private extension ContentView { var previewWidgetSection: some View { let entry = PreviewWidgetEntry(date: Date(), systemImageName: "star.fill") return Section(header: Text("Preview Widget")) { diff --git a/WidgetExamplesWidget/DynamicIntentWidget/DynamicIntentWidget.swift b/WidgetExamplesWidget/DynamicIntentWidget/DynamicIntentWidget.swift index d8c7cd5..7c1170c 100644 --- a/WidgetExamplesWidget/DynamicIntentWidget/DynamicIntentWidget.swift +++ b/WidgetExamplesWidget/DynamicIntentWidget/DynamicIntentWidget.swift @@ -11,11 +11,11 @@ import WidgetKit private struct Provider: IntentTimelineProvider { func placeholder(in context: Context) -> SimpleEntry { - SimpleEntry(date: Date()) + SimpleEntry(date: Date(), contact: .friend1) } func getSnapshot(for configuration: DynamicPersonSelectionIntent, in context: Context, completion: @escaping (SimpleEntry) -> Void) { - let entry = SimpleEntry(date: Date()) + let entry = SimpleEntry(date: Date(), contact: .friend1) completion(entry) } @@ -35,7 +35,7 @@ private struct Provider: IntentTimelineProvider { private struct SimpleEntry: TimelineEntry { let date: Date - var contact: Contact = .friend1 + let contact: Contact } private struct DynamicIntentWidgetEntryView: View { @@ -43,11 +43,11 @@ private struct DynamicIntentWidgetEntryView: View { var body: some View { VStack { - Text(String(describing: entry.contact.name)) + Text(entry.contact.name) .fontWeight(.semibold) Group { - Text("Born on:") - Text(entry.contact.birthday, style: .date) + Text("Date of birth:") + Text(entry.contact.dateOfBirth, style: .date) .multilineTextAlignment(.center) } .font(.footnote) @@ -64,7 +64,7 @@ struct DynamicIntentWidget: Widget { DynamicIntentWidgetEntryView(entry: entry) } .configurationDisplayName("Dynamic Intent Widget") - .description("A dynamically configurable Widget that displays the remaining time.") + .description("A Widget that has dynamically configurable data.") .supportedFamilies([.systemSmall]) } } diff --git a/WidgetExamplesWidget/DynamicIntentWidget/DynamicPersonSelection.intentdefinition b/WidgetExamplesWidget/DynamicIntentWidget/DynamicPersonSelection.intentdefinition index 58d6015..8119b52 100644 --- a/WidgetExamplesWidget/DynamicIntentWidget/DynamicPersonSelection.intentdefinition +++ b/WidgetExamplesWidget/DynamicIntentWidget/DynamicPersonSelection.intentdefinition @@ -62,26 +62,6 @@ INIntentParameterPromptDialogType Primary - - INIntentParameterPromptDialogCustom - - INIntentParameterPromptDialogFormatString - There are ${count} options matching ‘${person}’. - INIntentParameterPromptDialogFormatStringID - MJwysV - INIntentParameterPromptDialogType - DisambiguationIntroduction - - - INIntentParameterPromptDialogCustom - - INIntentParameterPromptDialogFormatString - Just to confirm, you wanted ‘${person}’? - INIntentParameterPromptDialogFormatStringID - zrGV1L - INIntentParameterPromptDialogType - Confirmation - INIntentParameterSupportsDynamicEnumeration diff --git a/WidgetExamplesWidget/WidgetExamplesWidget.swift b/WidgetExamplesWidget/WidgetExamplesWidget.swift index af26aab..1fce56f 100644 --- a/WidgetExamplesWidget/WidgetExamplesWidget.swift +++ b/WidgetExamplesWidget/WidgetExamplesWidget.swift @@ -12,8 +12,8 @@ import WidgetKit @main struct WidgetExamplesWidgetBundle: WidgetBundle { var body: some Widget { - WidgetBundle1().body - WidgetBundle2().body +// WidgetBundle1().body +// WidgetBundle2().body WidgetBundle3().body } } diff --git a/WidgetExamplesWidget/WidgetExamplesWidgetExtension.entitlements b/WidgetExamplesWidget/WidgetExamplesWidgetExtension.entitlements new file mode 100644 index 0000000..bb47a4f --- /dev/null +++ b/WidgetExamplesWidget/WidgetExamplesWidgetExtension.entitlements @@ -0,0 +1,10 @@ + + + + + com.apple.security.application-groups + + group.com.pawelwiszenko.WidgetExamples + + +