Share SwiftData with a Widget

In this post, we’ll see how to share data from the application to the widget.

First, create the project and call it SharedWidget, selecting SwiftData as storage. After that, create the widget by selecting Target in the File -> New menu and then WidgetExtension.

After the creation process, you can also deselect the Live and Intent options because we won’t be using them.

To share data between the application and the widget, we need to create an App Group in the capabilities. After that, add a group and call it whatever you want. In my case, I named it:

Then, select the widget extension in the target:

Thus, also for it, add the app group in the capabilities, but this time don’t add a new one; select the existing one.

One last thing before we look at the code. We have to share the model between the app and the widget, so select the Item.swift file and select both project components:

If you run the application at this moment, you can add an item (the default Apple example for SwiftData) and if you add the widget, you’ll see a time and an emoji. We want to simply display the number of items added below the emoji, so the first thing to do is:

struct SimpleEntry: TimelineEntry {
    let date: Date
    let emoji: String
    let number: Int
}

Where the TimeLineEntry is: “A type that specifies the date to display a widget, and, optionally, indicates the current relevance of the widget’s content.”

Now, in the provider, we add the structure to retrieve the item numbers:

@MainActor
    private func getNumberOfItems() -> Int {
        guard let modelContainer = try? ModelContainer(for: Item.self) else {
            return 0
        }
        let descriptor = FetchDescriptor<Item>()
        let number = try? modelContainer.mainContext.fetch(descriptor).count
        return number ?? 0
    }

Note the @MainActor, now we change the Provider in this way:

struct Provider: TimelineProvider {
    
    @MainActor
    func placeholder(in context: Context) -> SimpleEntry {
        SimpleEntry(date: Date(), emoji: "😀", number: getNumberOfItems())
    }

    @MainActor
    func getSnapshot(in context: Context, completion: @escaping (SimpleEntry) -> ()) {
        let entry = SimpleEntry(date: Date(), emoji: "😀", number: getNumberOfItems())
        completion(entry)
    }

    @MainActor
    func getTimeline(in context: Context, completion: @escaping (Timeline<Entry>) -> ()) {
        print(Date())
        let timeline = Timeline(entries: [SimpleEntry(date: Date(), emoji: "😀", number: getNumberOfItems())], policy: .after(.now.advanced(by: 60)))
        completion(timeline)
    }
    
    @MainActor
    private func getNumberOfItems() -> Int {
        guard let modelContainer = try? ModelContainer(for: Item.self) else {
            return 0
        }
        let descriptor = FetchDescriptor<Item>()
        let number = try? modelContainer.mainContext.fetch(descriptor).count
        return number ?? 0
    }
}

So, every function that uses the getNumberOfItems must use the @MainActor annotation (Its role is to ensure that all code is executed on the main thread).

For more information about timelines, I advise reading the official documentation: Apple Developer – Timeline.

Now do a little changes in the view:

struct ForShareWidgetEntryView : View {
    var entry: Provider.Entry
    
    var body: some View {
        VStack {
            Text("Time:")
            Text(entry.date, style: .time)

            Text("Emoji:")
            Text(entry.emoji)
            Text("\(entry.number)")
        }
    }
}

Now everything should work. Keep in mind that in a production environment, the operating system decides when to update the widget.

share this post with friends

Picture of Nicola De filippo

Nicola De filippo

I'm a software engineer who adds to the passion for technologies the wisdom and the experience without losing the wonder for the world. I love to create new projects and to help people and teams to improve

Leave a comment

Your email address will not be published. Required fields are marked *

Who I am

I'm a software engineer who adds to the passion for technologies the wisdom and the experience without losing the wonder for the world. I love to create new projects and to help people and teams to improve.

Follow Me Here

Get The Latest Updates

Periodically receive my super contents on coding and programming

join the family;)

Recent Posts