[Swift] How to display the entered characters in Widget via UserDefaults when using WidgetKit

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.

environment

Preparation

1. Add AppGroup from Capability

Select Target as Widget App (Project from which Widget Kit was added), and double-click App Group from Capability to add App Group. スクリーンショット 2020-09-29 11.47.46.png OK if the App Groups column is added as shown below スクリーンショット 2020-09-29 11.58.32.png

2. Register a common Identifier in AppGroups

When you press "+" of AppGroups, the following dialog will be displayed, so select the development team. スクリーンショット 2020-09-29 12.02.01.png

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) スクリーンショット 2020-09-29 12.00.20.png

※Note

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.

3. Add App Groups on the Widget Extension side as well

The procedure is almost the same as the procedure we did earlier.

Select Target (WidgetAppExtension) added as Extension and add Capacity> AppGroups スクリーンショット 2020-09-29 13.19.15.png

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. スクリーンショット 2020-09-29 12.02.22.png

This completes the preparation for using UserDefaults.

Implementation

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

1. Arrange text fields and buttons for screen input

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

2. Add save process to UserDefaults

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.

3. Value acquisition process from UserDefaults

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.

4. Display the value obtained from UserDefaults in Widget

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.

If you want to update the display contents of the widget immediately after pressing the button

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

motion

0929gif2.gif

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

Remarks

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

[Swift] How to display the entered characters in Widget via UserDefaults when using WidgetKit
How to add characters to display when using link_to method
How to check if the characters entered in the Swift Text Field are email addresses
[Swift] How to display when the quiz app answers correctly [Beginner]
How to solve the unknown error when using slf4j in Java
[Rails 5] How to display the password change screen when using devise
How to display the text entered in text_area in Rails with line breaks
[Swift] How to set an image in the background without using UIImageView.
How to add sound in the app (swift)
How to set the display time to Japan time in Rails
[Swift] Use UserDefaults to save data in the app
[Rails] How to display an image in the view
How to get the class name of the argument of LoggerFactory.getLogger when using SLF4J in Java
[Rails] How to display the weather forecast of the registered address in Japanese using OpenWeatherMap
[Swift] How to use UserDefaults
How to set chrony when the time shifts in CentOS7
[Rails] How to display information stored in the database in view
How to output the value when there is an array in the array
[Behavior confirmed in December 2020] How to implement the alert display function
How to store the information entered in textarea in a variable in the method
[Swift] How to implement the Twitter login function using Firebase UI ②
How to write the view when Vue is introduced in Rails?
How to display characters entered in Spring Boot on a browser and reference links [Introduction to Spring Boot / For beginners]
[Ruby on Rails] When logging in for the first time ・ How to split the screen in half using jQuery
[Swift] How to pass the Label value in the selected collectionViewCell to the destination TextField
How to return a value from Model to Controller using the [Swift5] protocol
I summarized the points to note when using resources and resources in combination
Try to display a slightly rich Webview in Swift UI using UIViewRepresentable
How to find the total number of pages when paging in Java
How to constrain the action of the transition destination when not logged in
I want to display an error message when registering in the database
[Ruby] Returns characters in a pyramid shape according to the entered numbers
[Ruby] How to prevent errors when nil is included in the operation
How to reference a column when overriding the column name method in ActiveRecord
[swift5] How to specify color in hexadecimal
Shorten the UUID to base64 in Swift.
[Swift] How to implement the countdown function
How to get the date in java
[Swift5] How to implement animation using "lottie-ios"
How to overwrite Firebase data in Swift
How to display error messages in Japanese
How to embed and display youtube videos in Rails (You can also get the URL you entered by editing)
[Swift] How to get the number of elements in an array (super basic)
[swift] How to control the behavior when the back button of NavigationBar is pressed
How to get the query string to actually issue when using PreparedStatement with JDBC
How to get the ID of a user authenticated with Firebase in Swift
How to fix a crash when deleting Realm data in Swift UI List
[Swift] How to change the order of Bar Items in Tab Bar Controller [Beginner]
How to set when "The constructor Empty () is not visible" occurs in junit
How to display a web page in Java
[Swift] How to implement the LINE login function
How to implement the breadcrumb function using gretel
[Swift] How to link the app with Firebase
How to delete the database when recreating the application
[Swift] How to implement the fade-in / out function
Display "Hello World" in the browser using Java
Display "Hello World" in the browser using Java
How to display the result of form input
How to execute tasks in parallel in Swift in Swift Package
How to build the simplest blockchain in Ruby
How to log in automatically when Ubuntu restarts