Last article [Swift] How to easily implement WidgetKit added from iOS 14.0 So, I described how to use WidgetKit, This time, after adding WidgetKit as Extension, I will describe how to share the value via UserDefaults.
Select Target as Widget App (Project from which Widget Kit was added), and double-click App Group from Capability to add App Group. OK if the App Groups column is added as shown below
When you press "+" of AppGroups, the following dialog will be displayed, so select the development team.
b. You will be prompted to enter the common Identifier used by AppGroups, so enter as follows
group.{Bundle Identifier when creating a project}.xxxxx
Example)
The xxxxx part can be registered freely, but if you connect two or more with a period like xxxxx.yyy, Please note that it will be played with an error when applying for the App Store.
After pressing OK, it is OK if the Identifier for AppGroup is registered as shown below.
The procedure is almost the same as the procedure we did earlier.
Select Target (WidgetAppExtension) added as Extension and add Capacity> AppGroups
b. Press the "+" button to register the common Identifier in App Groups This side should end when you select a developer team. Then, the Identifier added in step 2 will be displayed, so check the check box.
This completes the preparation for using UserDefaults.
This time, we will implement the following contents. -Save the character string entered on the screen side in UserDefaults -Display the character string saved in UserDefaults in Widget
ContentView.swift
struct ContentView: View {
@State private var text = ""
var body: some View {
VStack {
TextField("Character input", text: $text)
.textFieldStyle(RoundedBorderTextFieldStyle())
.padding(.horizontal)
Button(action: {
//When the button is pressed
}){
Text("Save the character")
}
}
}
}
This time, the value of the text field is saved when the button is pressed.
ContentView.swift
struct ContentView: View {
@State private var text = ""
var body: some View {
VStack {
TextField("Character input", text: $text)
.textFieldStyle(RoundedBorderTextFieldStyle())
.padding(.horizontal)
Button(action: {
//When the button is pressed
let userDefaults = UserDefaults(suiteName: "group.com.sample.yajima.WidgetApp.WidgetExtension")
if let userDefaults = userDefaults {
userDefaults.synchronize()
userDefaults.setValue(text, forKeyPath: "inputText")
}
}){
Text("Save the character")
}
}
}
}
In this way, you can share the value of UserDefaults by specifying the Identifier registered earlier in suiteName. I also saw an article saying that userDefaults.synchronize () is unnecessary, but it didn't work without me adding it.
WidgetAppExtension.swift
// struct Provider:In TimelineProvider
func getTimeline(in context: Context, completion: @escaping (Timeline<Entry>) -> ()) {
var entries: [SimpleEntry] = []
/*Postscript from here*/
var text = ""
let userDefaults = UserDefaults(suiteName: "group.com.sample.yajima.WidgetApp.WidgetExtension")
if let userDefaults = userDefaults {
text = userDefaults.string(forKey: "inputText") ?? ""
}
/*So far*/
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)
}
As with saving the value, specify the Identifier for suiteName, and the other things are the same as the usual usage of UserDefaults.
If you haven't touched anything from previous article I think that the default Widget describes the process of displaying the time, so In addition to that, I will try to display the character string.
WidgetAppExtension.swift
import WidgetKit
import SwiftUI
struct Provider: TimelineProvider {
func placeholder(in context: Context) -> SimpleEntry {
SimpleEntry(date: Date(), text: "")
}
func getSnapshot(in context: Context, completion: @escaping (SimpleEntry) -> ()) {
let entry = SimpleEntry(date: Date(), text: "")
completion(entry)
}
func getTimeline(in context: Context, completion: @escaping (Timeline<Entry>) -> ()) {
var entries: [SimpleEntry] = []
var text = ""
let userDefaults = UserDefaults(suiteName: "group.com.sample.yajima.WidgetApp.WidgetExtension")
if let userDefaults = userDefaults {
text = userDefaults.string(forKey: "inputText") ?? ""
}
let currentDate = Date()
for hourOffset in 0 ..< 5 {
let entryDate = Calendar.current.date(byAdding: .hour, value: hourOffset, to: currentDate)!
//Set the string obtained from UserDefaults
let entry = SimpleEntry(date: entryDate, text: text)
entries.append(entry)
}
let timeline = Timeline(entries: entries, policy: .atEnd)
completion(timeline)
}
}
struct SimpleEntry: TimelineEntry {
let date: Date
let text: String
}
struct WidgetAppExtensionEntryView : View {
var entry: Provider.Entry
var body: some View {
Text(entry.date, style: .time)
Text(entry.text)
}
}
@main
struct WidgetAppExtension: Widget {
let kind: String = "WidgetAppExtension"
var body: some WidgetConfiguration {
StaticConfiguration(kind: kind, provider: Provider()) { entry in
WidgetAppExtensionEntryView(entry: entry)
}
.configurationDisplayName("My Widget")
.description("This is an example widget.")
}
}
struct WidgetAppExtension_Previews: PreviewProvider {
static var previews: some View {
WidgetAppExtensionEntryView(entry: SimpleEntry(date: Date(),text: ""))
.previewContext(WidgetPreviewContext(family: .systemSmall))
}
}
Now you can display the values saved in UserDefaults in WidgetKit.
Update is possible by calling the following process when the button is pressed.
WidgetCenter.shared.reloadAllTimelines()
ContentView.swift
import SwiftUI
import WidgetKit
struct ContentView: View {
@State private var text = ""
var body: some View {
VStack {
TextField("Character input", text: $text)
.textFieldStyle(RoundedBorderTextFieldStyle())
.padding(.horizontal)
Button(action: {
//When the button is pressed
//Identifier set when adding AppGroups
let userDefaults = UserDefaults(suiteName: "group.com.sample.yajima.WidgetApp.WidgetExtension")
if let userDefaults = userDefaults {
userDefaults.synchronize()
userDefaults.setValue(text, forKeyPath: "inputText")
}
//Update Widget
WidgetCenter.shared.reloadAllTimelines()
}){
Text("Save the character")
}
}
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}
The characters entered in this way are now reflected in WidgetKit! I would appreciate it if you could point out any misunderstandings of me.
We have released the following apps with implementations like this article. It still lacks features, but if you like, please install it! Accent Widget
The app has been released! Please install it if you like. Toranpo
I started Twitter! Please follow me if you like. @yajima_tohshu
Recommended Posts