From 84700b6cf1086c7122d956793d88fdae632fd785 Mon Sep 17 00:00:00 2001 From: "Sahin, Meryem" Date: Fri, 11 Aug 2023 10:15:59 +0300 Subject: [PATCH 1/3] added widget extension --- .../AccentColor.colorset/Contents.json | 11 ++++ .../AppIcon.appiconset/Contents.json | 13 ++++ .../Assets.xcassets/Contents.json | 6 ++ .../WidgetBackground.colorset/Contents.json | 11 ++++ CoinWidgetExtension/CoinWidgetExtension.swift | 66 +++++++++++++++++++ .../CoinWidgetExtensionBundle.swift | 16 +++++ CoinWidgetExtension/Info.plist | 11 ++++ 7 files changed, 134 insertions(+) create mode 100644 CoinWidgetExtension/Assets.xcassets/AccentColor.colorset/Contents.json create mode 100644 CoinWidgetExtension/Assets.xcassets/AppIcon.appiconset/Contents.json create mode 100644 CoinWidgetExtension/Assets.xcassets/Contents.json create mode 100644 CoinWidgetExtension/Assets.xcassets/WidgetBackground.colorset/Contents.json create mode 100644 CoinWidgetExtension/CoinWidgetExtension.swift create mode 100644 CoinWidgetExtension/CoinWidgetExtensionBundle.swift create mode 100644 CoinWidgetExtension/Info.plist diff --git a/CoinWidgetExtension/Assets.xcassets/AccentColor.colorset/Contents.json b/CoinWidgetExtension/Assets.xcassets/AccentColor.colorset/Contents.json new file mode 100644 index 0000000..eb87897 --- /dev/null +++ b/CoinWidgetExtension/Assets.xcassets/AccentColor.colorset/Contents.json @@ -0,0 +1,11 @@ +{ + "colors" : [ + { + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/CoinWidgetExtension/Assets.xcassets/AppIcon.appiconset/Contents.json b/CoinWidgetExtension/Assets.xcassets/AppIcon.appiconset/Contents.json new file mode 100644 index 0000000..13613e3 --- /dev/null +++ b/CoinWidgetExtension/Assets.xcassets/AppIcon.appiconset/Contents.json @@ -0,0 +1,13 @@ +{ + "images" : [ + { + "idiom" : "universal", + "platform" : "ios", + "size" : "1024x1024" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/CoinWidgetExtension/Assets.xcassets/Contents.json b/CoinWidgetExtension/Assets.xcassets/Contents.json new file mode 100644 index 0000000..73c0059 --- /dev/null +++ b/CoinWidgetExtension/Assets.xcassets/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/CoinWidgetExtension/Assets.xcassets/WidgetBackground.colorset/Contents.json b/CoinWidgetExtension/Assets.xcassets/WidgetBackground.colorset/Contents.json new file mode 100644 index 0000000..eb87897 --- /dev/null +++ b/CoinWidgetExtension/Assets.xcassets/WidgetBackground.colorset/Contents.json @@ -0,0 +1,11 @@ +{ + "colors" : [ + { + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/CoinWidgetExtension/CoinWidgetExtension.swift b/CoinWidgetExtension/CoinWidgetExtension.swift new file mode 100644 index 0000000..d89e318 --- /dev/null +++ b/CoinWidgetExtension/CoinWidgetExtension.swift @@ -0,0 +1,66 @@ +// +// CoinWidgetExtension.swift +// CoinWidgetExtension +// +// Created by Sahin, Meryem on 11.08.2023. +// + +import WidgetKit +import SwiftUI + +struct Provider: TimelineProvider { + func placeholder(in context: Context) -> SimpleEntry { + SimpleEntry(date: Date()) + } + + func getSnapshot(in context: Context, completion: @escaping (SimpleEntry) -> ()) { + let entry = SimpleEntry(date: Date()) + completion(entry) + } + + func getTimeline(in context: Context, completion: @escaping (Timeline) -> ()) { + var entries: [SimpleEntry] = [] + + // 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(date: entryDate) + entries.append(entry) + } + + let timeline = Timeline(entries: entries, policy: .atEnd) + completion(timeline) + } +} + +struct SimpleEntry: TimelineEntry { + let date: Date +} + +struct CoinWidgetExtensionEntryView : View { + var entry: Provider.Entry + + var body: some View { + Text(entry.date, style: .time) + } +} + +struct CoinWidgetExtension: Widget { + let kind: String = "CoinWidgetExtension" + + var body: some WidgetConfiguration { + StaticConfiguration(kind: kind, provider: Provider()) { entry in + CoinWidgetExtensionEntryView(entry: entry) + } + .configurationDisplayName("My Widget") + .description("This is an example widget.") + } +} + +struct CoinWidgetExtension_Previews: PreviewProvider { + static var previews: some View { + CoinWidgetExtensionEntryView(entry: SimpleEntry(date: Date())) + .previewContext(WidgetPreviewContext(family: .systemSmall)) + } +} diff --git a/CoinWidgetExtension/CoinWidgetExtensionBundle.swift b/CoinWidgetExtension/CoinWidgetExtensionBundle.swift new file mode 100644 index 0000000..be00867 --- /dev/null +++ b/CoinWidgetExtension/CoinWidgetExtensionBundle.swift @@ -0,0 +1,16 @@ +// +// CoinWidgetExtensionBundle.swift +// CoinWidgetExtension +// +// Created by Sahin, Meryem on 11.08.2023. +// + +import WidgetKit +import SwiftUI + +@main +struct CoinWidgetExtensionBundle: WidgetBundle { + var body: some Widget { + CoinWidgetExtension() + } +} diff --git a/CoinWidgetExtension/Info.plist b/CoinWidgetExtension/Info.plist new file mode 100644 index 0000000..0f118fb --- /dev/null +++ b/CoinWidgetExtension/Info.plist @@ -0,0 +1,11 @@ + + + + + NSExtension + + NSExtensionPointIdentifier + com.apple.widgetkit-extension + + + From 42e01f38cfa9fc50e584d2603b702e95dc404512 Mon Sep 17 00:00:00 2001 From: "Sahin, Meryem" Date: Thu, 24 Aug 2023 16:20:36 +0300 Subject: [PATCH 2/3] coin widget extension added xcodegen/project.yml file --- CoinWidgetExtension/CoinWidgetExtension.swift | 7 ++++--- CoinWidgetExtension/Info.plist | 10 ++++++++++ project.yml | 14 ++++++++++++++ 3 files changed, 28 insertions(+), 3 deletions(-) diff --git a/CoinWidgetExtension/CoinWidgetExtension.swift b/CoinWidgetExtension/CoinWidgetExtension.swift index d89e318..2d5e8f5 100644 --- a/CoinWidgetExtension/CoinWidgetExtension.swift +++ b/CoinWidgetExtension/CoinWidgetExtension.swift @@ -24,7 +24,7 @@ struct Provider: TimelineProvider { // 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 entryDate = Calendar.current.date(byAdding: .hour, value: hourOffset, to: currentDate) ?? Date() let entry = SimpleEntry(date: entryDate) entries.append(entry) } @@ -60,7 +60,8 @@ struct CoinWidgetExtension: Widget { struct CoinWidgetExtension_Previews: PreviewProvider { static var previews: some View { - CoinWidgetExtensionEntryView(entry: SimpleEntry(date: Date())) - .previewContext(WidgetPreviewContext(family: .systemSmall)) + VStack { + CoinWidgetExtensionEntryView(entry: SimpleEntry(date: Date())) + }.previewContext(WidgetPreviewContext(family: .systemSmall)) } } diff --git a/CoinWidgetExtension/Info.plist b/CoinWidgetExtension/Info.plist index 0f118fb..3da3ca4 100644 --- a/CoinWidgetExtension/Info.plist +++ b/CoinWidgetExtension/Info.plist @@ -2,6 +2,16 @@ + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleName + $(PRODUCT_NAME) + CFBundlePackageType + $(PRODUCT_BUNDLE_PACKAGE_TYPE) + CFBundleVersion + 1 NSExtension NSExtensionPointIdentifier diff --git a/project.yml b/project.yml index d2b7ad3..edea8e1 100644 --- a/project.yml +++ b/project.yml @@ -36,6 +36,7 @@ targets: - package: Pulse product: Pulse product: PulseUI + - target: CoinWidgetExtension configFiles: Development: SampleAppSwiftUI/Configs/Development.xcconfig Production: SampleAppSwiftUI/Configs/Production.xcconfig @@ -73,3 +74,16 @@ targets: platform: iOS sources: - path: SampleAppSwiftUIUITests + CoinWidgetExtension: + type: app-extension + platform: iOS + sources: + - path: CoinWidgetExtension + settings: + PRODUCT_BUNDLE_IDENTIFIER: com.adesso.SampleAppSwiftUI.development.CoinWidgetExtension + INFOPLIST_FILE: CoinWidgetExtension/Info.plist + dependencies: + - framework: SwiftUI.framework + implicit: true + - framework: WidgetKit.framework + implicit: true From a0c33d345f08f4475c8086468548da7da3139613 Mon Sep 17 00:00:00 2001 From: alieren97 Date: Wed, 6 Sep 2023 09:14:11 +0300 Subject: [PATCH 3/3] Created app groups for widget extension and app, Fetching favorite coins from AppStorage added. --- .../CoinWidgetExtension.entitlements | 10 +++ CoinWidgetExtension/CoinWidgetExtension.swift | 77 ++++++++++++++----- .../SampleAppSwiftUI.entitlements | 10 +++ .../Utility/Managers/StorageManager.swift | 4 +- 4 files changed, 81 insertions(+), 20 deletions(-) create mode 100644 CoinWidgetExtension/CoinWidgetExtension.entitlements create mode 100644 SampleAppSwiftUI/SampleAppSwiftUI.entitlements diff --git a/CoinWidgetExtension/CoinWidgetExtension.entitlements b/CoinWidgetExtension/CoinWidgetExtension.entitlements new file mode 100644 index 0000000..0b4100d --- /dev/null +++ b/CoinWidgetExtension/CoinWidgetExtension.entitlements @@ -0,0 +1,10 @@ + + + + + com.apple.security.application-groups + + group.com.adesso.SampleAppSwiftUI + + + diff --git a/CoinWidgetExtension/CoinWidgetExtension.swift b/CoinWidgetExtension/CoinWidgetExtension.swift index 2d5e8f5..c718b63 100644 --- a/CoinWidgetExtension/CoinWidgetExtension.swift +++ b/CoinWidgetExtension/CoinWidgetExtension.swift @@ -9,40 +9,57 @@ import WidgetKit import SwiftUI struct Provider: TimelineProvider { + @AppStorage("favoriteCoins", store: UserDefaults(suiteName: "group.com.adesso.SampleAppSwiftUI")) var favoriteCoins: [CoinData] = [] + func placeholder(in context: Context) -> SimpleEntry { - SimpleEntry(date: Date()) + SimpleEntry(favoriteCoins: favoriteCoins) } func getSnapshot(in context: Context, completion: @escaping (SimpleEntry) -> ()) { - let entry = SimpleEntry(date: Date()) + let entry = SimpleEntry(favoriteCoins: favoriteCoins) completion(entry) } func getTimeline(in context: Context, completion: @escaping (Timeline) -> ()) { - var entries: [SimpleEntry] = [] - - // 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) ?? Date() - let entry = SimpleEntry(date: entryDate) - entries.append(entry) - } - - let timeline = Timeline(entries: entries, policy: .atEnd) + let timeline = Timeline(entries: [SimpleEntry(favoriteCoins: favoriteCoins)], policy: .atEnd) completion(timeline) } } struct SimpleEntry: TimelineEntry { - let date: Date + let date = Date() + var favoriteCoins: [CoinData] } -struct CoinWidgetExtensionEntryView : View { +struct CoinWidgetExtensionEntryView: View { + @Environment(\.widgetFamily) var widgetFamily var entry: Provider.Entry + @ViewBuilder + var emptyView: some View { + Text("There are no favorite coins") + } + + @ViewBuilder + var coinsView: some View { + VStack { + ForEach(entry.favoriteCoins) { val in + HStack { + Text(val.coinInfo?.title ?? "") + } + } + } + } + var body: some View { - Text(entry.date, style: .time) + switch widgetFamily { + case .systemSmall: + entry.favoriteCoins.isEmpty ? AnyView(emptyView) : AnyView(coinsView) + case .systemMedium: + entry.favoriteCoins.isEmpty ? AnyView(emptyView) : AnyView(coinsView) + default: + Text(entry.date, style: .time) + } } } @@ -55,13 +72,35 @@ struct CoinWidgetExtension: Widget { } .configurationDisplayName("My Widget") .description("This is an example widget.") + .supportedFamilies([.systemSmall, .systemMedium]) } } struct CoinWidgetExtension_Previews: PreviewProvider { static var previews: some View { - VStack { - CoinWidgetExtensionEntryView(entry: SimpleEntry(date: Date())) - }.previewContext(WidgetPreviewContext(family: .systemSmall)) + + CoinWidgetExtensionEntryView(entry: SimpleEntry(favoriteCoins: [])).previewContext(WidgetPreviewContext(family: .systemSmall)) + CoinWidgetExtensionEntryView(entry: SimpleEntry(favoriteCoins: [])).previewContext(WidgetPreviewContext(family: .systemMedium)) + + } +} + +extension Array: RawRepresentable where Element: Codable { +public init?(rawValue: String) { + guard let data = rawValue.data(using: .utf8), + let result = try? JSONDecoder().decode([Element].self, from: data) + else { + return nil + } + self = result +} + +public var rawValue: String { + guard let data = try? JSONEncoder().encode(self), + let result = String(data: data, encoding: .utf8) + else { + return "[]" + } + return result } } diff --git a/SampleAppSwiftUI/SampleAppSwiftUI.entitlements b/SampleAppSwiftUI/SampleAppSwiftUI.entitlements new file mode 100644 index 0000000..0b4100d --- /dev/null +++ b/SampleAppSwiftUI/SampleAppSwiftUI.entitlements @@ -0,0 +1,10 @@ + + + + + com.apple.security.application-groups + + group.com.adesso.SampleAppSwiftUI + + + diff --git a/SampleAppSwiftUI/Utility/Managers/StorageManager.swift b/SampleAppSwiftUI/Utility/Managers/StorageManager.swift index f5695d2..8c4f347 100644 --- a/SampleAppSwiftUI/Utility/Managers/StorageManager.swift +++ b/SampleAppSwiftUI/Utility/Managers/StorageManager.swift @@ -6,14 +6,16 @@ // import SwiftUI +import WidgetKit final class StorageManager: ObservableObject { static let shared = StorageManager() - @AppStorage("favoriteCoins") var favoriteCoins: [CoinData] = [] { + @AppStorage("favoriteCoins", store: UserDefaults(suiteName: "group.com.adesso.SampleAppSwiftUI")) var favoriteCoins: [CoinData] = [] { didSet { objectWillChange.send() + WidgetCenter.shared.reloadAllTimelines() } }